aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-05-29 15:31:52 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-05-29 15:31:52 +0200
commita6247ecf707ae9c28c259bb1cec02e92e87d135e (patch)
treea91cc5bad2dd90e67bb40d9247bc559bd8763681 /src
parentf125d90796a7394760e98380255117952bcb0109 (diff)
downloadlanes-a6247ecf707ae9c28c259bb1cec02e92e87d135e.tar.gz
lanes-a6247ecf707ae9c28c259bb1cec02e92e87d135e.tar.bz2
lanes-a6247ecf707ae9c28c259bb1cec02e92e87d135e.zip
Organized namespace 'state'
Diffstat (limited to 'src')
-rw-r--r--src/lanes.cpp4
-rw-r--r--src/state.cpp529
-rw-r--r--src/state.h13
-rw-r--r--src/universe.cpp4
4 files changed, 283 insertions, 267 deletions
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 04b0955..9fc7fc9 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -240,7 +240,7 @@ LUAG_FUNC(lane_new)
240 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: setup" << std::endl); 240 DEBUGSPEW_CODE(DebugSpew(_U) << "lane_new: setup" << std::endl);
241 241
242 std::optional<std::string_view> _libs_str{ lua_isnil(L_, kLibsIdx) ? std::nullopt : std::make_optional(lua_tostringview(L_, kLibsIdx)) }; 242 std::optional<std::string_view> _libs_str{ lua_isnil(L_, kLibsIdx) ? std::nullopt : std::make_optional(lua_tostringview(L_, kLibsIdx)) };
243 lua_State* const _L2{ luaG_newstate(_U, SourceState{ L_ }, _libs_str) }; // L_: [fixed] ... L2: 243 lua_State* const _L2{ state::NewLaneState(_U, SourceState{ L_ }, _libs_str) }; // L_: [fixed] ... L2:
244 STACK_CHECK_START_REL(_L2, 0); 244 STACK_CHECK_START_REL(_L2, 0);
245 245
246 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread) 246 // 'lane' is allocated from heap, not Lua, since its life span may surpass the handle's (if free running thread)
@@ -663,7 +663,7 @@ LUAG_FUNC(configure)
663 // Linked chains handling 663 // Linked chains handling
664 _U->selfdestructFirst = SELFDESTRUCT_END; 664 _U->selfdestructFirst = SELFDESTRUCT_END;
665 _U->initializeAllocatorFunction(L_); 665 _U->initializeAllocatorFunction(L_);
666 InitializeOnStateCreate(_U, L_); 666 state::InitializeOnStateCreate(_U, L_);
667 _U->initializeKeepers(L_); 667 _U->initializeKeepers(L_);
668 STACK_CHECK(L_, 1); 668 STACK_CHECK(L_, 1);
669 669
diff --git a/src/state.cpp b/src/state.cpp
index b747a9f..5589c30 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -43,17 +43,23 @@ THE SOFTWARE.
43 43
44// ################################################################################################# 44// #################################################################################################
45 45
46/*---=== luaG_newstate ===---*/ 46static constexpr char const* kOnStateCreate{ "on_state_create" }; // update lanes.lua if the name changes!
47
48[[nodiscard]] static int require_lanes_core(lua_State* L_)
49{
50 // leaves a copy of 'lanes.core' module table on the stack
51 luaL_requiref(L_, kLanesCoreLibName, luaopen_lanes_core, 0);
52 return 1;
53}
54 47
55// ################################################################################################# 48// #################################################################################################
49// #################################################################################################
56namespace { 50namespace {
51 // #############################################################################################
52 // #############################################################################################
53
54 [[nodiscard]] static int require_lanes_core(lua_State* L_)
55 {
56 // leaves a copy of 'lanes.core' module table on the stack
57 luaL_requiref(L_, kLanesCoreLibName, luaopen_lanes_core, 0);
58 return 1;
59 }
60
61 // #############################################################################################
62
57 namespace local { 63 namespace local {
58 static luaL_Reg const sLibs[] = { 64 static luaL_Reg const sLibs[] = {
59 { "base", nullptr }, // ignore "base" (already acquired it) 65 { "base", nullptr }, // ignore "base" (already acquired it)
@@ -87,291 +93,302 @@ namespace {
87 }; 93 };
88 94
89 } // namespace local 95 } // namespace local
90} // namespace
91 96
92// ################################################################################################# 97 // #############################################################################################
93 98
94static void Open1Lib(lua_State* L_, std::string_view const& name_) 99 static void Open1Lib(lua_State* L_, std::string_view const& name_)
95{ 100 {
96 for (luaL_Reg const& _entry : local::sLibs) { 101 for (luaL_Reg const& _entry : local::sLibs) {
97 if (name_ == _entry.name) { 102 if (name_ == _entry.name) {
98 lua_CFunction const _libfunc{ _entry.func }; 103 lua_CFunction const _libfunc{ _entry.func };
99 if (!_libfunc) { 104 if (!_libfunc) {
105 break;
106 }
107 std::string_view const _name{ _entry.name };
108 DEBUGSPEW_CODE(DebugSpew(universe_get(L_)) << "opening '" << _name << "' library" << std::endl);
109 STACK_CHECK_START_REL(L_, 0);
110 // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack)
111 bool const isLanesCore{ _libfunc == require_lanes_core }; // don't want to create a global for "lanes.core"
112 luaL_requiref(L_, _name.data(), _libfunc, !isLanesCore); // L_: {lib}
113 // lanes.core doesn't declare a global, so scan it here and now
114 if (isLanesCore) {
115 tools::PopulateFuncLookupTable(L_, -1, _name);
116 }
117 lua_pop(L_, 1); // L_:
118 STACK_CHECK(L_, 0);
100 break; 119 break;
101 } 120 }
102 std::string_view const _name{ _entry.name };
103 DEBUGSPEW_CODE(DebugSpew(universe_get(L_)) << "opening '" << _name << "' library" << std::endl);
104 STACK_CHECK_START_REL(L_, 0);
105 // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack)
106 bool const isLanesCore{ _libfunc == require_lanes_core }; // don't want to create a global for "lanes.core"
107 luaL_requiref(L_, _name.data(), _libfunc, !isLanesCore); // L_: {lib}
108 // lanes.core doesn't declare a global, so scan it here and now
109 if (isLanesCore) {
110 tools::PopulateFuncLookupTable(L_, -1, _name);
111 }
112 lua_pop(L_, 1); // L_:
113 STACK_CHECK(L_, 0);
114 break;
115 } 121 }
116 } 122 }
117}
118 123
119// ################################################################################################# 124 // #############################################################################################
120 125
121// just like lua_xmove, args are (from, to) 126 // just like lua_xmove, args are (from, to)
122static void copy_one_time_settings(Universe* U_, SourceState L1_, DestState L2_) 127 static void CopyOneTimeSettings(Universe* U_, SourceState L1_, DestState L2_)
123{ 128 {
124 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U_ }); 129 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U_ });
125 130
126 STACK_GROW(L1_, 2); 131 STACK_GROW(L1_, 2);
127 STACK_CHECK_START_REL(L1_, 0); 132 STACK_CHECK_START_REL(L1_, 0);
128 STACK_CHECK_START_REL(L2_, 0); 133 STACK_CHECK_START_REL(L2_, 0);
129 134
130 DEBUGSPEW_CODE(DebugSpew(U_) << "copy_one_time_settings()" << std::endl); 135 DEBUGSPEW_CODE(DebugSpew(U_) << "CopyOneTimeSettings()" << std::endl);
131 136
132 kConfigRegKey.pushValue(L1_); // L1_: config 137 kConfigRegKey.pushValue(L1_); // L1_: config
133 // copy settings from from source to destination registry 138 // copy settings from from source to destination registry
134 InterCopyContext _c{ U_, L2_, L1_, {}, {}, {}, {}, {} }; 139 InterCopyContext _c{ U_, L2_, L1_, {}, {}, {}, {}, {} };
135 if (_c.inter_move(1) != InterCopyResult::Success) { // L1_: L2_: config 140 if (_c.inter_move(1) != InterCopyResult::Success) { // L1_: L2_: config
136 raise_luaL_error(L1_, "failed to copy settings when loading " kLanesCoreLibName); 141 raise_luaL_error(L1_, "failed to copy settings when loading " kLanesCoreLibName);
142 }
143 // set L2:_R[kConfigRegKey] = settings
144 kConfigRegKey.setValue(L2_, [](lua_State* L_) { lua_insert(L_, -2); }); // L1_: L2_: config
145 STACK_CHECK(L2_, 0);
146 STACK_CHECK(L1_, 0);
137 } 147 }
138 // set L2:_R[kConfigRegKey] = settings
139 kConfigRegKey.setValue(L2_, [](lua_State* L_) { lua_insert(L_, -2); }); // L1_: L2_: config
140 STACK_CHECK(L2_, 0);
141 STACK_CHECK(L1_, 0);
142}
143 148
149 // #############################################################################################
150 // #############################################################################################
151} // namespace
152// #################################################################################################
144// ################################################################################################# 153// #################################################################################################
145 154
146static constexpr char const* kOnStateCreate{ "on_state_create" }; // update lanes.lua if the name changes! 155// #################################################################################################
156// #################################################################################################
157namespace state {
158 // #############################################################################################
159 // #############################################################################################
160
161 void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_)
162 {
163 if (U_->onStateCreateFunc == nullptr) {
164 return;
165 }
147 166
148void InitializeOnStateCreate(Universe* U_, lua_State* L_) 167 STACK_CHECK_START_REL(L_, 0);
149{ 168 DEBUGSPEW_CODE(DebugSpew(U_) << "calling on_state_create()" << std::endl);
150 STACK_CHECK_START_REL(L_, 1); // L_: settings 169 if (U_->onStateCreateFunc != reinterpret_cast<lua_CFunction>(InitializeOnStateCreate)) {
151 if (luaG_getfield(L_, -1, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil 170 // C function: recreate a closure in the new state, bypassing the lookup scheme
152 // store C function pointer in an internal variable 171 lua_pushcfunction(L_, U_->onStateCreateFunc); // on_state_create()
153 U_->onStateCreateFunc = lua_tocfunction(L_, -1); // L_: settings on_state_create 172 } else { // Lua function located in the config table, copied when we opened "lanes.core"
154 if (U_->onStateCreateFunc != nullptr) { 173 if (mode_ != LookupMode::LaneBody) {
155 // make sure the function doesn't have upvalues 174 // if attempting to call in a keeper state, do nothing because the function doesn't exist there
156 char const* _upname{ lua_getupvalue(L_, -1, 1) }; // L_: settings on_state_create upval? 175 // this doesn't count as an error though
157 if (_upname != nullptr) { // should be "" for C functions with upvalues if any 176 STACK_CHECK(L_, 0);
158 raise_luaL_error(L_, "%s shouldn't have upvalues", kOnStateCreate); 177 return;
159 } 178 }
160 // remove this C function from the config table so that it doesn't cause problems 179 kConfigRegKey.pushValue(L_); // L_: {}
161 // when we transfer the config table in newly created Lua states 180 STACK_CHECK(L_, 1);
162 lua_pushnil(L_); // L_: settings on_state_create nil 181 std::ignore = luaG_getfield(L_, -1, kOnStateCreate); // L_: {} on_state_create()
163 lua_setfield(L_, -3, kOnStateCreate); // L_: settings on_state_create 182 lua_remove(L_, -2); // L_: on_state_create()
164 } else { 183 }
165 // optim: store marker saying we have such a function in the config table 184 STACK_CHECK(L_, 1);
166 U_->onStateCreateFunc = reinterpret_cast<lua_CFunction>(InitializeOnStateCreate); 185 // capture error and raise it in caller state
186 std::string_view const _stateType{ mode_ == LookupMode::LaneBody ? "lane" : "keeper" };
187 std::ignore = lua_pushstringview(L_, _stateType); // L_: on_state_create() "<type>"
188 if (lua_pcall(L_, 1, 0, 0) != LUA_OK) {
189 raise_luaL_error(from_, "%s failed: \"%s\"", kOnStateCreate, lua_isstring(L_, -1) ? lua_tostring(L_, -1) : lua_typename(L_, lua_type(L_, -1)));
167 } 190 }
191 STACK_CHECK(L_, 0);
168 } 192 }
169 lua_pop(L_, 1); // L_: settings
170 STACK_CHECK(L_, 1);
171}
172 193
173// ################################################################################################# 194 // #############################################################################################
174 195
175lua_State* create_state([[maybe_unused]] Universe* U_, lua_State* from_) 196 lua_State* CreateState([[maybe_unused]] Universe* U_, lua_State* from_)
176{ 197 {
177 lua_State* const _L { 198 lua_State* const _L {
178 std::invoke( 199 std::invoke(
179 [U = U_, from = from_]() { 200 [U = U_, from = from_]() {
180 if constexpr (LUAJIT_FLAVOR() == 64) { 201 if constexpr (LUAJIT_FLAVOR() == 64) {
181 // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate... 202 // for some reason, LuaJIT 64 bits does not support creating a state with lua_newstate...
182 return luaL_newstate(); 203 return luaL_newstate();
183 } else {
184 if (U->provideAllocator != nullptr) { // we have a function we can call to obtain an allocator
185 lua_pushcclosure(from, U->provideAllocator, 0);
186 lua_call(from, 0, 1);
187 AllocatorDefinition* const _def{ lua_tofulluserdata<AllocatorDefinition>(from, -1) };
188 lua_State* const _L{ lua_newstate(_def->allocF, _def->allocUD) };
189 lua_pop(from, 1);
190 return _L;
191 } else { 204 } else {
192 // reuse the allocator provided when the master state was created 205 if (U->provideAllocator != nullptr) { // we have a function we can call to obtain an allocator
193 return lua_newstate(U->protectedAllocator.allocF, U->protectedAllocator.allocUD); 206 lua_pushcclosure(from, U->provideAllocator, 0);
207 lua_call(from, 0, 1);
208 AllocatorDefinition* const _def{ lua_tofulluserdata<AllocatorDefinition>(from, -1) };
209 lua_State* const _L{ lua_newstate(_def->allocF, _def->allocUD) };
210 lua_pop(from, 1);
211 return _L;
212 } else {
213 // reuse the allocator provided when the master state was created
214 return lua_newstate(U->protectedAllocator.allocF, U->protectedAllocator.allocUD);
215 }
194 } 216 }
195 } 217 }
196 } 218 )
197 ) 219 };
198 };
199
200 if (_L == nullptr) {
201 raise_luaL_error(from_, "luaG_newstate() failed while creating state; out of memory");
202 }
203 return _L;
204}
205
206// #################################################################################################
207 220
208void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_) 221 if (_L == nullptr) {
209{ 222 raise_luaL_error(from_, "luaG_newstate() failed while creating state; out of memory");
210 if (U_->onStateCreateFunc == nullptr) { 223 }
211 return; 224 return _L;
212 } 225 }
213 226
214 STACK_CHECK_START_REL(L_, 0); 227 // #############################################################################################
215 DEBUGSPEW_CODE(DebugSpew(U_) << "calling on_state_create()" << std::endl); 228
216 if (U_->onStateCreateFunc != reinterpret_cast<lua_CFunction>(InitializeOnStateCreate)) { 229 void InitializeOnStateCreate(Universe* U_, lua_State* L_)
217 // C function: recreate a closure in the new state, bypassing the lookup scheme 230 {
218 lua_pushcfunction(L_, U_->onStateCreateFunc); // on_state_create() 231 STACK_CHECK_START_REL(L_, 1); // L_: settings
219 } else { // Lua function located in the config table, copied when we opened "lanes.core" 232 if (luaG_getfield(L_, -1, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil
220 if (mode_ != LookupMode::LaneBody) { 233 // store C function pointer in an internal variable
221 // if attempting to call in a keeper state, do nothing because the function doesn't exist there 234 U_->onStateCreateFunc = lua_tocfunction(L_, -1); // L_: settings on_state_create
222 // this doesn't count as an error though 235 if (U_->onStateCreateFunc != nullptr) {
223 STACK_CHECK(L_, 0); 236 // make sure the function doesn't have upvalues
224 return; 237 char const* _upname{ lua_getupvalue(L_, -1, 1) }; // L_: settings on_state_create upval?
238 if (_upname != nullptr) { // should be "" for C functions with upvalues if any
239 raise_luaL_error(L_, "%s shouldn't have upvalues", kOnStateCreate);
240 }
241 // remove this C function from the config table so that it doesn't cause problems
242 // when we transfer the config table in newly created Lua states
243 lua_pushnil(L_); // L_: settings on_state_create nil
244 lua_setfield(L_, -3, kOnStateCreate); // L_: settings on_state_create
245 } else {
246 // optim: store marker saying we have such a function in the config table
247 U_->onStateCreateFunc = reinterpret_cast<lua_CFunction>(InitializeOnStateCreate);
248 }
225 } 249 }
226 kConfigRegKey.pushValue(L_); // L_: {} 250 lua_pop(L_, 1); // L_: settings
227 STACK_CHECK(L_, 1); 251 STACK_CHECK(L_, 1);
228 std::ignore = luaG_getfield(L_, -1, kOnStateCreate); // L_: {} on_state_create()
229 lua_remove(L_, -2); // L_: on_state_create()
230 } 252 }
231 STACK_CHECK(L_, 1);
232 // capture error and raise it in caller state
233 std::string_view const _stateType{ mode_ == LookupMode::LaneBody ? "lane" : "keeper" };
234 std::ignore = lua_pushstringview(L_, _stateType); // L_: on_state_create() "<type>"
235 if (lua_pcall(L_, 1, 0, 0) != LUA_OK) {
236 raise_luaL_error(from_, "%s failed: \"%s\"", kOnStateCreate, lua_isstring(L_, -1) ? lua_tostring(L_, -1) : lua_typename(L_, lua_type(L_, -1)));
237 }
238 STACK_CHECK(L_, 0);
239}
240 253
241// ################################################################################################# 254 // #############################################################################################
242 255
243/* 256 /*
244 * Like 'luaL_openlibs()' but allows the set of libraries be selected 257 * Like 'luaL_openlibs()' but allows the set of libraries be selected
245 * 258 *
246 * nullptr no libraries, not even base 259 * nullptr no libraries, not even base
247 * "" base library only 260 * "" base library only
248 * "io,string" named libraries 261 * "io,string" named libraries
249 * "*" all libraries 262 * "*" all libraries
250 * 263 *
251 * Base ("unpack", "print" etc.) is always added, unless 'libs' is nullptr. 264 * Base ("unpack", "print" etc.) is always added, unless 'libs' is nullptr.
252 * 265 *
253 * *NOT* called for keeper states! 266 */
254 * 267 lua_State* NewLaneState(Universe* U_, SourceState from_, std::optional<std::string_view> const& libs_)
255 */ 268 {
256lua_State* luaG_newstate(Universe* U_, SourceState from_, std::optional<std::string_view> const& libs_) 269 DestState const _L{ CreateState(U_, from_) };
257{ 270
258 DestState const _L{ create_state(U_, from_) }; 271 STACK_GROW(_L, 2);
259 272 STACK_CHECK_START_ABS(_L, 0);
260 STACK_GROW(_L, 2); 273
261 STACK_CHECK_START_ABS(_L, 0); 274 // copy the universe as a light userdata (only the master state holds the full userdata)
262 275 // that way, if Lanes is required in this new state, we'll know we are part of this universe
263 // copy the universe as a light userdata (only the master state holds the full userdata) 276 universe_store(_L, U_);
264 // that way, if Lanes is required in this new state, we'll know we are part of this universe 277 STACK_CHECK(_L, 0);
265 universe_store(_L, U_); 278
266 STACK_CHECK(_L, 0); 279 // we'll need this every time we transfer some C function from/to this state
267 280 kLookupRegKey.setValue(_L, [](lua_State* L_) { lua_newtable(L_); });
268 // we'll need this every time we transfer some C function from/to this state 281 STACK_CHECK(_L, 0);
269 kLookupRegKey.setValue(_L, [](lua_State* L_) { lua_newtable(L_); }); 282
270 STACK_CHECK(_L, 0); 283 // neither libs (not even 'base') nor special init func: we are done
271 284 if (!libs_.has_value() && U_->onStateCreateFunc == nullptr) {
272 // neither libs (not even 'base') nor special init func: we are done 285 DEBUGSPEW_CODE(DebugSpew(U_) << "luaG_newstate(nullptr)" << std::endl);
273 if (!libs_.has_value() && U_->onStateCreateFunc == nullptr) { 286 return _L;
274 DEBUGSPEW_CODE(DebugSpew(U_) << "luaG_newstate(nullptr)" << std::endl); 287 }
275 return _L;
276 }
277 288
278 DEBUGSPEW_CODE(DebugSpew(U_) << "luaG_newstate()" << std::endl); 289 DEBUGSPEW_CODE(DebugSpew(U_) << "luaG_newstate()" << std::endl);
279 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U_ }); 290 DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ U_ });
280 291
281 // copy settings (for example because it may contain a Lua on_state_create function) 292 // copy settings (for example because it may contain a Lua on_state_create function)
282 copy_one_time_settings(U_, from_, _L); 293 CopyOneTimeSettings(U_, from_, _L);
283 294
284 // 'lua.c' stops GC during initialization so perhaps it is a good idea. :) 295 // 'lua.c' stops GC during initialization so perhaps it is a good idea. :)
285 lua_gc(_L, LUA_GCSTOP, 0); 296 lua_gc(_L, LUA_GCSTOP, 0);
286 297
287 // Anything causes 'base' and 'jit' to be taken in 298 // Anything causes 'base' and 'jit' to be taken in
288 std::string_view _libs{}; 299 std::string_view _libs{};
289 if (libs_.has_value()) { 300 if (libs_.has_value()) {
290 _libs = libs_.value(); 301 _libs = libs_.value();
291 // special "*" case (mainly to help with LuaJIT compatibility) 302 // special "*" case (mainly to help with LuaJIT compatibility)
292 // as we are called from luaopen_lanes_core() already, and that would deadlock 303 // as we are called from luaopen_lanes_core() already, and that would deadlock
293 if (_libs == "*") { 304 if (_libs == "*") {
294 DEBUGSPEW_CODE(DebugSpew(U_) << "opening ALL standard libraries" << std::endl); 305 DEBUGSPEW_CODE(DebugSpew(U_) << "opening ALL standard libraries" << std::endl);
295 luaL_openlibs(_L); 306 luaL_openlibs(_L);
296 // don't forget lanes.core for regular lane states 307 // don't forget lanes.core for regular lane states
297 Open1Lib(_L, kLanesCoreLibName); 308 Open1Lib(_L, kLanesCoreLibName);
298 _libs = ""; // done with libs 309 _libs = ""; // done with libs
299 } else {
300 if constexpr (LUAJIT_FLAVOR() != 0) { // building against LuaJIT headers, always open jit
301 DEBUGSPEW_CODE(DebugSpew(U_) << "opening 'jit' library" << std::endl);
302 Open1Lib(_L, LUA_JITLIBNAME);
303 }
304 DEBUGSPEW_CODE(DebugSpew(U_) << "opening 'base' library" << std::endl);
305 if constexpr (LUA_VERSION_NUM >= 502) {
306 // open base library the same way as in luaL_openlibs()
307 luaL_requiref(_L, LUA_GNAME, luaopen_base, 1);
308 lua_pop(_L, 1);
309 } else { 310 } else {
310 lua_pushcfunction(_L, luaopen_base); 311 if constexpr (LUAJIT_FLAVOR() != 0) { // building against LuaJIT headers, always open jit
311 lua_pushstring(_L, ""); 312 DEBUGSPEW_CODE(DebugSpew(U_) << "opening 'jit' library" << std::endl);
312 lua_call(_L, 1, 0); 313 Open1Lib(_L, LUA_JITLIBNAME);
314 }
315 DEBUGSPEW_CODE(DebugSpew(U_) << "opening 'base' library" << std::endl);
316 if constexpr (LUA_VERSION_NUM >= 502) {
317 // open base library the same way as in luaL_openlibs()
318 luaL_requiref(_L, LUA_GNAME, luaopen_base, 1);
319 lua_pop(_L, 1);
320 } else {
321 lua_pushcfunction(_L, luaopen_base);
322 lua_pushstring(_L, "");
323 lua_call(_L, 1, 0);
324 }
313 } 325 }
314 } 326 }
315 } 327 STACK_CHECK(_L, 0);
316 STACK_CHECK(_L, 0); 328
317 329 // scan all libraries, open them one by one
318 // scan all libraries, open them one by one 330 if (!_libs.empty()) {
319 if (!_libs.empty()) { 331 unsigned int _len{ 0 };
320 unsigned int _len{ 0 }; 332 for (char const* _p{ _libs.data() }; *_p; _p += _len) {
321 for (char const* _p{ _libs.data() }; *_p; _p += _len) { 333 // skip delimiters ('.' can be part of name for "lanes.core")
322 // skip delimiters ('.' can be part of name for "lanes.core") 334 while (*_p && !std::isalnum(*_p) && *_p != '.') {
323 while (*_p && !std::isalnum(*_p) && *_p != '.') { 335 ++_p;
324 ++_p; 336 }
325 } 337 // skip name
326 // skip name 338 _len = 0;
327 _len = 0; 339 while (std::isalnum(_p[_len]) || _p[_len] == '.') {
328 while (std::isalnum(_p[_len]) || _p[_len] == '.') { 340 ++_len;
329 ++_len; 341 }
342 // open library
343 Open1Lib(_L, { _p, _len });
330 } 344 }
331 // open library
332 Open1Lib(_L, { _p, _len });
333 } 345 }
334 } 346 lua_gc(_L, LUA_GCRESTART, 0);
335 lua_gc(_L, LUA_GCRESTART, 0); 347
336 348 tools::SerializeRequire(_L);
337 tools::SerializeRequire(_L); 349
338 350 // call this after the base libraries are loaded and GC is restarted
339 // call this after the base libraries are loaded and GC is restarted 351 // will raise an error in from_ in case of problem
340 // will raise an error in from_ in case of problem 352 CallOnStateCreate(U_, _L, from_, LookupMode::LaneBody);
341 CallOnStateCreate(U_, _L, from_, LookupMode::LaneBody); 353
342 354 STACK_CHECK(_L, 0);
343 STACK_CHECK(_L, 0); 355 // after all this, register everything we find in our name<->function database
344 // after all this, register everything we find in our name<->function database 356 lua_pushglobaltable(_L); // L: _G
345 lua_pushglobaltable(_L); // L: _G 357 tools::PopulateFuncLookupTable(_L, -1, {});
346 tools::PopulateFuncLookupTable(_L, -1, {});
347 lua_pop(_L, 1); // L:
348 STACK_CHECK(_L, 0);
349
350 if constexpr (USE_DEBUG_SPEW()) {
351 DEBUGSPEW_CODE(DebugSpew(U_) << std::source_location::current().function_name() << " LOOKUP DB CONTENTS" << std::endl);
352 DEBUGSPEW_CODE(DebugSpewIndentScope _scope2{ U_ });
353 // dump the lookup database contents
354 kLookupRegKey.pushValue(_L); // L: {}
355 lua_pushnil(_L); // L: {} nil
356 while (lua_next(_L, -2)) { // L: {} k v
357 std::ignore = lua_pushstringview(_L, "["); // L: {} k v "["
358
359 lua_getglobal(_L, "tostring"); // L: {} k v "[" tostring
360 lua_pushvalue(_L, -4); // L: {} k v "[" tostring k
361 lua_call(_L, 1, 1); // L: {} k v "[" 'k'
362
363 std::ignore = lua_pushstringview(_L, "] = "); // L: {} k v "[" 'k' "] = "
364
365 lua_getglobal(_L, "tostring"); // L: {} k v "[" 'k' "] = " tostring
366 lua_pushvalue(_L, -5); // L: {} k v "[" 'k' "] = " tostring v
367 lua_call(_L, 1, 1); // L: {} k v "[" 'k' "] = " 'v'
368 lua_concat(_L, 4); // L: {} k v "[k] = v"
369 DEBUGSPEW_CODE(DebugSpew(U_) << lua_tostringview(_L, -1) << std::endl);
370 lua_pop(_L, 2); // L: {} k
371 } // lua_next() // L: {}
372 lua_pop(_L, 1); // L: 358 lua_pop(_L, 1); // L:
359 STACK_CHECK(_L, 0);
360
361 if constexpr (USE_DEBUG_SPEW()) {
362 DEBUGSPEW_CODE(DebugSpew(U_) << std::source_location::current().function_name() << " LOOKUP DB CONTENTS" << std::endl);
363 DEBUGSPEW_CODE(DebugSpewIndentScope _scope2{ U_ });
364 // dump the lookup database contents
365 kLookupRegKey.pushValue(_L); // L: {}
366 lua_pushnil(_L); // L: {} nil
367 while (lua_next(_L, -2)) { // L: {} k v
368 std::ignore = lua_pushstringview(_L, "["); // L: {} k v "["
369
370 lua_getglobal(_L, "tostring"); // L: {} k v "[" tostring
371 lua_pushvalue(_L, -4); // L: {} k v "[" tostring k
372 lua_call(_L, 1, 1); // L: {} k v "[" 'k'
373
374 std::ignore = lua_pushstringview(_L, "] = "); // L: {} k v "[" 'k' "] = "
375
376 lua_getglobal(_L, "tostring"); // L: {} k v "[" 'k' "] = " tostring
377 lua_pushvalue(_L, -5); // L: {} k v "[" 'k' "] = " tostring v
378 lua_call(_L, 1, 1); // L: {} k v "[" 'k' "] = " 'v'
379 lua_concat(_L, 4); // L: {} k v "[k] = v"
380 DEBUGSPEW_CODE(DebugSpew(U_) << lua_tostringview(_L, -1) << std::endl);
381 lua_pop(_L, 2); // L: {} k
382 } // lua_next() // L: {}
383 lua_pop(_L, 1); // L:
384 }
385
386 STACK_CHECK(_L, 0);
387 return _L;
373 } 388 }
374 389
375 STACK_CHECK(_L, 0); 390 // #############################################################################################
376 return _L; 391 // #############################################################################################
377} 392} // namespace state
393// #################################################################################################
394// #################################################################################################
diff --git a/src/state.h b/src/state.h
index ff70196..9b42786 100644
--- a/src/state.h
+++ b/src/state.h
@@ -7,12 +7,11 @@
7enum class LookupMode; 7enum class LookupMode;
8class Universe; 8class Universe;
9 9
10// ################################################################################################# 10namespace state {
11 11
12[[nodiscard]] lua_State* create_state(Universe* U_, lua_State* from_); 12 void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_);
13[[nodiscard]] lua_State* luaG_newstate(Universe* U_, SourceState from_, std::optional<std::string_view> const& libs_); 13 [[nodiscard]] lua_State* CreateState(Universe* U_, lua_State* from_);
14 void InitializeOnStateCreate(Universe* U_, lua_State* L_);
15 [[nodiscard]] lua_State* NewLaneState(Universe* U_, SourceState from_, std::optional<std::string_view> const& libs_);
14 16
15// ################################################################################################# 17} // namespace state
16
17void InitializeOnStateCreate(Universe* U_, lua_State* L_);
18void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_);
diff --git a/src/universe.cpp b/src/universe.cpp
index 172cc4c..77f799b 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -265,7 +265,7 @@ void Universe::initializeKeepers(lua_State* L_)
265 265
266 for (int const _i : std::ranges::iota_view{ 0, _nb_keepers }) { 266 for (int const _i : std::ranges::iota_view{ 0, _nb_keepers }) {
267 // note that we will leak K if we raise an error later 267 // note that we will leak K if we raise an error later
268 KeeperState const _K{ create_state(this, L_) }; // L_: settings K: 268 KeeperState const _K{ state::CreateState(this, L_) }; // L_: settings K:
269 if (_K == nullptr) { 269 if (_K == nullptr) {
270 raise_luaL_error(L_, "out of memory while creating keeper states"); 270 raise_luaL_error(L_, "out of memory while creating keeper states");
271 } 271 }
@@ -307,7 +307,7 @@ void Universe::initializeKeepers(lua_State* L_)
307 // attempt to call on_state_create(), if we have one and it is a C function 307 // attempt to call on_state_create(), if we have one and it is a C function
308 // (only support a C function because we can't transfer executable Lua code in keepers) 308 // (only support a C function because we can't transfer executable Lua code in keepers)
309 // will raise an error in L_ in case of problem 309 // will raise an error in L_ in case of problem
310 CallOnStateCreate(this, _K, L_, LookupMode::ToKeeper); 310 state::CallOnStateCreate(this, _K, L_, LookupMode::ToKeeper);
311 311
312 // to see VM name in Decoda debugger 312 // to see VM name in Decoda debugger
313 lua_pushfstring(_K, "Keeper #%d", _i + 1); // L_: settings K: "Keeper #n" 313 lua_pushfstring(_K, "Keeper #%d", _i + 1); // L_: settings K: "Keeper #n"