diff options
Diffstat (limited to 'src/state.c')
-rw-r--r-- | src/state.c | 439 |
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 | |||
10 | Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com> | ||
11 | 2011-21 benoit Germain <bnt.germain@gmail.com> | ||
12 | |||
13 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
14 | of this software and associated documentation files (the "Software"), to deal | ||
15 | in the Software without restriction, including without limitation the rights | ||
16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
17 | copies of the Software, and to permit persons to whom the Software is | ||
18 | furnished to do so, subject to the following conditions: | ||
19 | |||
20 | The above copyright notice and this permission notice shall be included in | ||
21 | all copies or substantial portions of the Software. | ||
22 | |||
23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
29 | THE 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 | // | ||
60 | static 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 | */ | ||
97 | void 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 | |||
125 | static 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 | |||
133 | static 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 | |||
169 | static 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) | ||
200 | static 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 | |||
222 | void 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 | |||
253 | lua_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 | |||
278 | void 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 | */ | ||
325 | lua_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 | } | ||