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