aboutsummaryrefslogtreecommitdiff
path: root/src/state.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/state.c')
-rw-r--r--src/state.c439
1 files changed, 439 insertions, 0 deletions
diff --git a/src/state.c b/src/state.c
new file mode 100644
index 0000000..cbbc0d8
--- /dev/null
+++ b/src/state.c
@@ -0,0 +1,439 @@
1/*
2* STATE.C
3*
4* Lua tools to support Lanes.
5*/
6
7/*
8===============================================================================
9
10Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com>
112011-21 benoit Germain <bnt.germain@gmail.com>
12
13Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal
15in the Software without restriction, including without limitation the rights
16to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17copies of the Software, and to permit persons to whom the Software is
18furnished to do so, subject to the following conditions:
19
20The above copyright notice and this permission notice shall be included in
21all copies or substantial portions of the Software.
22
23THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29THE SOFTWARE.
30
31===============================================================================
32*/
33
34#include <stdio.h>
35#include <assert.h>
36#include <string.h>
37#include <ctype.h>
38#include <stdlib.h>
39#if !defined(__APPLE__)
40#include <malloc.h>
41#endif // __APPLE__
42
43#include "compat.h"
44#include "universe.h"
45#include "tools.h"
46#include "lanes.h"
47
48// ################################################################################################
49
50/*---=== Serialize require ===---
51*/
52
53//---
54// [val]= new_require( ... )
55//
56// Call 'old_require' but only one lane at a time.
57//
58// Upvalues: [1]: original 'require' function
59//
60static int luaG_new_require( lua_State* L)
61{
62 int rc, i;
63 int args = lua_gettop( L);
64 Universe* U = universe_get( L);
65 //char const* modname = luaL_checkstring( L, 1);
66
67 STACK_GROW( L, args + 1);
68 STACK_CHECK( L, 0);
69
70 lua_pushvalue( L, lua_upvalueindex( 1));
71 for( i = 1; i <= args; ++ i)
72 {
73 lua_pushvalue( L, i);
74 }
75
76 // Using 'lua_pcall()' to catch errors; otherwise a failing 'require' would
77 // leave us locked, blocking any future 'require' calls from other lanes.
78 //
79 MUTEX_LOCK( &U->require_cs);
80 rc = lua_pcall( L, args, 1 /*retvals*/, 0 /*errfunc*/ );
81 MUTEX_UNLOCK( &U->require_cs);
82
83 // the required module (or an error message) is left on the stack as returned value by original require function
84 STACK_END( L, 1);
85
86 if( rc != LUA_OK) // LUA_ERRRUN / LUA_ERRMEM ?
87 {
88 return lua_error( L); // error message already at [-1]
89 }
90
91 return 1;
92}
93
94/*
95* Serialize calls to 'require', if it exists
96*/
97void serialize_require( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L)
98{
99 STACK_GROW( L, 1);
100 STACK_CHECK( L, 0);
101 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "serializing require()\n" INDENT_END));
102
103 // Check 'require' is there and not already wrapped; if not, do nothing
104 //
105 lua_getglobal( L, "require");
106 if( lua_isfunction( L, -1) && lua_tocfunction( L, -1) != luaG_new_require)
107 {
108 // [-1]: original 'require' function
109 lua_pushcclosure( L, luaG_new_require, 1 /*upvalues*/);
110 lua_setglobal( L, "require");
111 }
112 else
113 {
114 // [-1]: nil
115 lua_pop( L, 1);
116 }
117
118 STACK_END( L, 0);
119}
120
121// ################################################################################################
122
123/*---=== luaG_newstate ===---*/
124
125static int require_lanes_core( lua_State* L)
126{
127 // leaves a copy of 'lanes.core' module table on the stack
128 luaL_requiref( L, "lanes.core", luaopen_lanes_core, 0);
129 return 1;
130}
131
132
133static const luaL_Reg libs[] =
134{
135 { LUA_LOADLIBNAME, luaopen_package},
136{ LUA_TABLIBNAME, luaopen_table},
137{ LUA_STRLIBNAME, luaopen_string},
138{ LUA_MATHLIBNAME, luaopen_math},
139#ifndef PLATFORM_XBOX // no os/io libs on xbox
140{ LUA_OSLIBNAME, luaopen_os},
141{ LUA_IOLIBNAME, luaopen_io},
142#endif // PLATFORM_XBOX
143#if LUA_VERSION_NUM >= 503
144{ LUA_UTF8LIBNAME, luaopen_utf8},
145#endif
146#if LUA_VERSION_NUM >= 502
147#ifdef luaopen_bit32
148{ LUA_BITLIBNAME, luaopen_bit32},
149#endif
150{ LUA_COLIBNAME, luaopen_coroutine}, // Lua 5.2: coroutine is no longer a part of base!
151#else // LUA_VERSION_NUM
152{ LUA_COLIBNAME, NULL}, // Lua 5.1: part of base package
153#endif // LUA_VERSION_NUM
154{ LUA_DBLIBNAME, luaopen_debug},
155#if defined LUA_JITLIBNAME // building against LuaJIT headers, add some LuaJIT-specific libs
156//#pragma message( "supporting JIT base libs")
157{ LUA_BITLIBNAME, luaopen_bit},
158{ LUA_JITLIBNAME, luaopen_jit},
159{ LUA_FFILIBNAME, luaopen_ffi},
160#endif // LUA_JITLIBNAME
161
162{ LUA_DBLIBNAME, luaopen_debug},
163{ "lanes.core", require_lanes_core}, // So that we can open it like any base library (possible since we have access to the init function)
164 //
165{ "base", NULL}, // ignore "base" (already acquired it)
166{ NULL, NULL }
167};
168
169static void open1lib( DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, char const* name_, size_t len_)
170{
171 int i;
172 for( i = 0; libs[i].name; ++ i)
173 {
174 if( strncmp( name_, libs[i].name, len_) == 0)
175 {
176 lua_CFunction libfunc = libs[i].func;
177 name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_
178 if( libfunc != NULL)
179 {
180 bool_t const isLanesCore = (libfunc == require_lanes_core) ? TRUE : FALSE; // don't want to create a global for "lanes.core"
181 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening %.*s library\n" INDENT_END, (int) len_, name_));
182 STACK_CHECK( L, 0);
183 // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack)
184 luaL_requiref( L, name_, libfunc, !isLanesCore);
185 // lanes.core doesn't declare a global, so scan it here and now
186 if( isLanesCore == TRUE)
187 {
188 populate_func_lookup_table( L, -1, name_);
189 }
190 lua_pop( L, 1);
191 STACK_END( L, 0);
192 }
193 break;
194 }
195 }
196}
197
198
199// just like lua_xmove, args are (from, to)
200static void copy_one_time_settings( Universe* U, lua_State* L, lua_State* L2)
201{
202 STACK_GROW( L, 2);
203 STACK_CHECK( L, 0);
204 STACK_CHECK( L2, 0);
205
206 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "copy_one_time_settings()\n" INDENT_END));
207 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
208
209 REGISTRY_GET( L, CONFIG_REGKEY); // config
210 // copy settings from from source to destination registry
211 if( luaG_inter_move( U, L, L2, 1, eLM_LaneBody) < 0) // // config
212 {
213 (void) luaL_error( L, "failed to copy settings when loading lanes.core");
214 }
215 // set L2:_R[CONFIG_REGKEY] = settings
216 REGISTRY_SET( L2, CONFIG_REGKEY, lua_insert( L2, -2)); //
217 STACK_END( L2, 0);
218 STACK_END( L, 0);
219 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
220}
221
222void initialize_on_state_create( Universe* U, lua_State* L)
223{
224 STACK_CHECK( L, 0);
225 lua_getfield( L, -1, "on_state_create"); // settings on_state_create|nil
226 if( !lua_isnil( L, -1))
227 {
228 // store C function pointer in an internal variable
229 U->on_state_create_func = lua_tocfunction( L, -1); // settings on_state_create
230 if( U->on_state_create_func != NULL)
231 {
232 // make sure the function doesn't have upvalues
233 char const* upname = lua_getupvalue( L, -1, 1); // settings on_state_create upval?
234 if( upname != NULL) // should be "" for C functions with upvalues if any
235 {
236 (void) luaL_error( L, "on_state_create shouldn't have upvalues");
237 }
238 // remove this C function from the config table so that it doesn't cause problems
239 // when we transfer the config table in newly created Lua states
240 lua_pushnil( L); // settings on_state_create nil
241 lua_setfield( L, -3, "on_state_create"); // settings on_state_create
242 }
243 else
244 {
245 // optim: store marker saying we have such a function in the config table
246 U->on_state_create_func = (lua_CFunction) initialize_on_state_create;
247 }
248 }
249 lua_pop( L, 1); // settings
250 STACK_END( L, 0);
251}
252
253lua_State* create_state( Universe* U, lua_State* from_)
254{
255 lua_State* L;
256 if( U->provide_allocator != NULL)
257 {
258 lua_pushcclosure( from_, U->provide_allocator, 0);
259 lua_call( from_, 0, 1);
260 {
261 AllocatorDefinition* def = lua_touserdata( from_, -1);
262 L = lua_newstate( def->allocF, def->allocUD);
263 }
264 lua_pop( from_, 1);
265 }
266 else
267 {
268 L = luaL_newstate();
269 }
270
271 if( L == NULL)
272 {
273 (void) luaL_error( from_, "luaG_newstate() failed while creating state; out of memory");
274 }
275 return L;
276}
277
278void call_on_state_create( Universe* U, lua_State* L, lua_State* from_, LookupMode mode_)
279{
280 if( U->on_state_create_func != NULL)
281 {
282 STACK_CHECK( L, 0);
283 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END));
284 if( U->on_state_create_func != (lua_CFunction) initialize_on_state_create)
285 {
286 // C function: recreate a closure in the new state, bypassing the lookup scheme
287 lua_pushcfunction( L, U->on_state_create_func); // on_state_create()
288 }
289 else // Lua function located in the config table, copied when we opened "lanes.core"
290 {
291 if( mode_ != eLM_LaneBody)
292 {
293 // if attempting to call in a keeper state, do nothing because the function doesn't exist there
294 // this doesn't count as an error though
295 return;
296 }
297 REGISTRY_GET( L, CONFIG_REGKEY); // {}
298 STACK_MID( L, 1);
299 lua_getfield( L, -1, "on_state_create"); // {} on_state_create()
300 lua_remove( L, -2); // on_state_create()
301 }
302 STACK_MID( L, 1);
303 // capture error and raise it in caller state
304 if( lua_pcall( L, 0, 0, 0) != LUA_OK)
305 {
306 luaL_error( from_, "on_state_create failed: \"%s\"", lua_isstring( L, -1) ? lua_tostring( L, -1) : lua_typename( L, lua_type( L, -1)));
307 }
308 STACK_END( L, 0);
309 }
310}
311
312/*
313* Like 'luaL_openlibs()' but allows the set of libraries be selected
314*
315* NULL no libraries, not even base
316* "" base library only
317* "io,string" named libraries
318* "*" all libraries
319*
320* Base ("unpack", "print" etc.) is always added, unless 'libs' is NULL.
321*
322* *NOT* called for keeper states!
323*
324*/
325lua_State* luaG_newstate( Universe* U, lua_State* from_, char const* libs_)
326{
327 lua_State* L = create_state( U, from_);
328
329 STACK_GROW( L, 2);
330 STACK_CHECK_ABS( L, 0);
331
332 // copy the universe as a light userdata (only the master state holds the full userdata)
333 // that way, if Lanes is required in this new state, we'll know we are part of this universe
334 universe_store( L, U);
335 STACK_MID( L, 0);
336
337 // we'll need this every time we transfer some C function from/to this state
338 REGISTRY_SET( L, LOOKUP_REGKEY, lua_newtable( L));
339 STACK_MID( L, 0);
340
341 // neither libs (not even 'base') nor special init func: we are done
342 if( libs_ == NULL && U->on_state_create_func == NULL)
343 {
344 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate(NULL)\n" INDENT_END));
345 return L;
346 }
347
348 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate()\n" INDENT_END));
349 DEBUGSPEW_CODE( ++ U->debugspew_indent_depth);
350
351 // copy settings (for example because it may contain a Lua on_state_create function)
352 copy_one_time_settings( U, from_, L);
353
354 // 'lua.c' stops GC during initialization so perhaps its a good idea. :)
355 lua_gc( L, LUA_GCSTOP, 0);
356
357
358 // Anything causes 'base' to be taken in
359 //
360 if( libs_ != NULL)
361 {
362 // special "*" case (mainly to help with LuaJIT compatibility)
363 // as we are called from luaopen_lanes_core() already, and that would deadlock
364 if( libs_[0] == '*' && libs_[1] == 0)
365 {
366 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END));
367 luaL_openlibs( L);
368 // don't forget lanes.core for regular lane states
369 open1lib( DEBUGSPEW_PARAM_COMMA( U) L, "lanes.core", 10);
370 libs_ = NULL; // done with libs
371 }
372 else
373 {
374 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening base library\n" INDENT_END));
375#if LUA_VERSION_NUM >= 502
376 // open base library the same way as in luaL_openlibs()
377 luaL_requiref( L, "_G", luaopen_base, 1);
378 lua_pop( L, 1);
379#else // LUA_VERSION_NUM
380 lua_pushcfunction( L, luaopen_base);
381 lua_pushstring( L, "");
382 lua_call( L, 1, 0);
383#endif // LUA_VERSION_NUM
384 }
385 }
386 STACK_END( L, 0);
387
388 // scan all libraries, open them one by one
389 if( libs_)
390 {
391 char const* p;
392 unsigned int len = 0;
393 for( p = libs_; *p; p += len)
394 {
395 // skip delimiters ('.' can be part of name for "lanes.core")
396 while( *p && !isalnum( *p) && *p != '.')
397 ++ p;
398 // skip name
399 len = 0;
400 while( isalnum( p[len]) || p[len] == '.')
401 ++ len;
402 // open library
403 open1lib( DEBUGSPEW_PARAM_COMMA( U) L, p, len);
404 }
405 }
406 lua_gc( L, LUA_GCRESTART, 0);
407
408 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L);
409
410 // call this after the base libraries are loaded and GC is restarted
411 // will raise an error in from_ in case of problem
412 call_on_state_create( U, L, from_, eLM_LaneBody);
413
414 STACK_CHECK( L, 0);
415 // after all this, register everything we find in our name<->function database
416 lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
417 populate_func_lookup_table( L, -1, NULL);
418
419#if 0 && USE_DEBUG_SPEW
420 // dump the lookup database contents
421 lua_getfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // {}
422 lua_pushnil( L); // {} nil
423 while( lua_next( L, -2)) // {} k v
424 {
425 lua_getglobal( L, "print"); // {} k v print
426 lua_pushlstring( L, debugspew_indent, U->debugspew_indent_depth); // {} k v print " "
427 lua_pushvalue( L, -4); // {} k v print " " k
428 lua_pushvalue( L, -4); // {} k v print " " k v
429 lua_call( L, 3, 0); // {} k v
430 lua_pop( L, 1); // {} k
431 }
432 lua_pop( L, 1); // {}
433#endif // USE_DEBUG_SPEW
434
435 lua_pop( L, 1);
436 STACK_END( L, 0);
437 DEBUGSPEW_CODE( -- U->debugspew_indent_depth);
438 return L;
439}