From 17ca8b54fdc0aa2df875fc7b80def4dd56959d7f Mon Sep 17 00:00:00 2001 From: Benoit Germain <benoit.germain@ubisoft.com> Date: Thu, 23 May 2024 08:35:40 +0200 Subject: on_state_create receives a string argument, "lane" or "keeper" --- docs/index.html | 14 +++++------- src/state.cpp | 56 ++++++++++++++++++++++++++--------------------- tests/manual_register.lua | 6 ++--- tests/package.lua | 8 +++---- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/docs/index.html b/docs/index.html index d0b06ae..47dc47c 100644 --- a/docs/index.html +++ b/docs/index.html @@ -400,20 +400,16 @@ function/<tt>nil</tt> </td> <td> - If provided, will be called in every created Lua state right after initializing the base libraries. - <br /> - Keeper states will call it as well, but only if it is a C function (keeper states are not able to execute any user Lua code). - <br /> + If provided, will be called in every created Lua state right after initializing the base libraries, with a single string argument, either <tt>"lane"</tt> or <tt>"keeper"</tt>.<br/> + If it is a C function, a C closure will be reconstructed in the created state from the C pointer. Lanes will raise an error if the function has upvalues.<br/> + Keeper states will call it as well, but only if it is a C function (keeper states are not able to execute any user Lua code).<br/> Typical usage is twofold: <ul> <li>Tweak <tt>package.loaders</tt></li> <li>Load some additional C functions in the global space (of course only a C function will be able to do this).</li> </ul> - That way, all changes in the state can be properly taken into account when building the function lookup database. Default is <tt>nil</tt>. - <br /> - If <tt>on_state_create()</tt> is a Lua function, it will be transfered normally before the call. - <br /> - If it is a C function, a C closure will be reconstructed in the created state from the C pointer. Lanes will raise an error if the function has upvalues. + That way, all changes in the state can be properly taken into account when building the function lookup database. Default is <tt>nil</tt>.<br/> + If <tt>on_state_create()</tt> is a Lua function, it will be transfered normally before the call.<br/> </td> </tr> diff --git a/src/state.cpp b/src/state.cpp index 0175449..83b9e85 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -207,22 +207,24 @@ static void copy_one_time_settings(Universe* U_, SourceState L1_, DestState L2_) // ################################################################################################# +static constexpr char const* kOnStateCreate{ "on_state_create" }; + void InitializeOnStateCreate(Universe* U_, lua_State* L_) { STACK_CHECK_START_REL(L_, 1); // L_: settings - if (luaG_getfield(L_, -1, "on_state_create") != LuaType::NIL) { // L_: settings on_state_create|nil + if (luaG_getfield(L_, -1, kOnStateCreate) != LuaType::NIL) { // L_: settings on_state_create|nil // store C function pointer in an internal variable U_->onStateCreateFunc = lua_tocfunction(L_, -1); // L_: settings on_state_create if (U_->onStateCreateFunc != nullptr) { // make sure the function doesn't have upvalues char const* _upname{ lua_getupvalue(L_, -1, 1) }; // L_: settings on_state_create upval? if (_upname != nullptr) { // should be "" for C functions with upvalues if any - raise_luaL_error(L_, "on_state_create shouldn't have upvalues"); + raise_luaL_error(L_, "%s shouldn't have upvalues", kOnStateCreate); } // remove this C function from the config table so that it doesn't cause problems // when we transfer the config table in newly created Lua states lua_pushnil(L_); // L_: settings on_state_create nil - lua_setfield(L_, -3, "on_state_create"); // L_: settings on_state_create + lua_setfield(L_, -3, kOnStateCreate); // L_: settings on_state_create } else { // optim: store marker saying we have such a function in the config table U_->onStateCreateFunc = reinterpret_cast<lua_CFunction>(InitializeOnStateCreate); @@ -265,31 +267,35 @@ lua_State* create_state([[maybe_unused]] Universe* U_, lua_State* from_) void CallOnStateCreate(Universe* U_, lua_State* L_, lua_State* from_, LookupMode mode_) { - if (U_->onStateCreateFunc != nullptr) { - STACK_CHECK_START_REL(L_, 0); - DEBUGSPEW_CODE(DebugSpew(U_) << "calling on_state_create()" << std::endl); - if (U_->onStateCreateFunc != reinterpret_cast<lua_CFunction>(InitializeOnStateCreate)) { - // C function: recreate a closure in the new state, bypassing the lookup scheme - lua_pushcfunction(L_, U_->onStateCreateFunc); // on_state_create() - } else { // Lua function located in the config table, copied when we opened "lanes.core" - if (mode_ != LookupMode::LaneBody) { - // if attempting to call in a keeper state, do nothing because the function doesn't exist there - // this doesn't count as an error though - STACK_CHECK(L_, 0); - return; - } - kConfigRegKey.pushValue(L_); // L_: {} - STACK_CHECK(L_, 1); - std::ignore = luaG_getfield(L_, -1, "on_state_create"); // L_: {} on_state_create() - lua_remove(L_, -2); // L_: on_state_create() + if (U_->onStateCreateFunc == nullptr) { + return; + } + + STACK_CHECK_START_REL(L_, 0); + DEBUGSPEW_CODE(DebugSpew(U_) << "calling on_state_create()" << std::endl); + if (U_->onStateCreateFunc != reinterpret_cast<lua_CFunction>(InitializeOnStateCreate)) { + // C function: recreate a closure in the new state, bypassing the lookup scheme + lua_pushcfunction(L_, U_->onStateCreateFunc); // on_state_create() + } else { // Lua function located in the config table, copied when we opened "lanes.core" + if (mode_ != LookupMode::LaneBody) { + // if attempting to call in a keeper state, do nothing because the function doesn't exist there + // this doesn't count as an error though + STACK_CHECK(L_, 0); + return; } + kConfigRegKey.pushValue(L_); // L_: {} STACK_CHECK(L_, 1); - // capture error and raise it in caller state - if (lua_pcall(L_, 0, 0, 0) != LUA_OK) { - raise_luaL_error(from_, "on_state_create failed: \"%s\"", lua_isstring(L_, -1) ? lua_tostring(L_, -1) : lua_typename(L_, lua_type(L_, -1))); - } - STACK_CHECK(L_, 0); + std::ignore = luaG_getfield(L_, -1, kOnStateCreate); // L_: {} on_state_create() + lua_remove(L_, -2); // L_: on_state_create() } + STACK_CHECK(L_, 1); + // capture error and raise it in caller state + std::string_view const _stateType{ mode_ == LookupMode::LaneBody ? "lane" : "keeper" }; + std::ignore = lua_pushstringview(L_, _stateType); // L_: on_state_create() "<type>" + if (lua_pcall(L_, 1, 0, 0) != LUA_OK) { + raise_luaL_error(from_, "%s failed: \"%s\"", kOnStateCreate, lua_isstring(L_, -1) ? lua_tostring(L_, -1) : lua_typename(L_, lua_type(L_, -1))); + } + STACK_CHECK(L_, 0); } // ################################################################################################# diff --git a/tests/manual_register.lua b/tests/manual_register.lua index 23b33c1..a8f2098 100644 --- a/tests/manual_register.lua +++ b/tests/manual_register.lua @@ -1,7 +1,7 @@ local register_func = true -local RequireAModuleThatExportsGlobalFunctions = function() +local RequireAModuleThatExportsGlobalFunctions = function(type_) -- grab some module that exports C functions, this is good enough for our purpose local lfs = require "lfs" -- make one of these a global @@ -11,14 +11,14 @@ local RequireAModuleThatExportsGlobalFunctions = function() if register_func then lanes.register( "GlobalFunc", GlobalFunc) end - print("RequireAModuleThatExportsGlobalFunctions done:", lanes.nameof(GlobalFunc)) + print(type_, "RequireAModuleThatExportsGlobalFunctions done:", lanes.nameof(GlobalFunc)) end local lanes = require "lanes".configure{ with_timers = false, on_state_create = RequireAModuleThatExportsGlobalFunctions} -- load some module that adds C functions to the global space -RequireAModuleThatExportsGlobalFunctions() +RequireAModuleThatExportsGlobalFunctions("main") local GlobalFuncUpval = GlobalFunc diff --git a/tests/package.lua b/tests/package.lua index de11c9e..4e8b59f 100644 --- a/tests/package.lua +++ b/tests/package.lua @@ -2,21 +2,21 @@ local loaders = package.loaders or package.searchers assert(nil == loaders[5]) -local configure_loaders = function() +local configure_loaders = function(type_) table.insert(loaders, 4, function() end) assert(loaders[1]) assert(loaders[2]) assert(loaders[3]) assert(loaders[4]) assert(loaders[5]) - print "loaders configured!" + print(type_, "loaders configured!") end -configure_loaders() +configure_loaders("main") for k,v in pairs(loaders) do print( k, type(v)) end lanes = require "lanes" -lanes.configure{with_timers=false, on_state_create = configure_loaders} \ No newline at end of file +lanes.configure{on_state_create = configure_loaders} \ No newline at end of file -- cgit v1.2.3-55-g6feb