From 47eb3f94373a13ac9f204ca65dfde602f53bdc1a Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 17 Feb 2014 11:05:19 +0100 Subject: Deep userdata support improvements * bumped version to 3.9.0 * keepers now require "package", receive package.path & package.cpath, and call on_state_create() if it is a C function * changed the deep public API (improved deep idfunc signature, renamed luaG_deep_userdata to luaG_newdeepuserdata) * if an error occurs while copying a deep userdata, don't raise inside the keeper state * fixed situations where raised errors could lead to memory leaks (deep gc) --- src/tools.c | 791 ++++++++++++++++++++++++++++-------------------------------- 1 file changed, 372 insertions(+), 419 deletions(-) (limited to 'src/tools.c') diff --git a/src/tools.c b/src/tools.c index fc3e7e0..2aa9b82 100644 --- a/src/tools.c +++ b/src/tools.c @@ -195,14 +195,14 @@ int initialize_on_state_create( lua_State* L) } // just like lua_xmove, args are (from, to) -void luaG_copy_one_time_settings( lua_State* L, lua_State* L2, char const* name_) +void luaG_copy_one_time_settings( lua_State* L, lua_State* L2) { STACK_GROW( L, 1); // copy settings from from source to destination registry lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); if( luaG_inter_move( L, L2, 1, eLM_LaneBody) < 0) // error? { - (void) luaL_error( L, "failed to copy settings when loading %s", name_); + (void) luaL_error( L, "failed to copy settings when loading lanes.core"); } lua_setfield( L2, LUA_REGISTRYINDEX, CONFIG_REGKEY); } @@ -258,7 +258,7 @@ static void open1lib( lua_State* L, char const* name_, size_t len_, lua_State* f if( isLanesCore == TRUE) { // copy settings from from source to destination registry - luaG_copy_one_time_settings( from_, L, name_); + luaG_copy_one_time_settings( from_, L); } // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack) luaL_requiref( L, name_, libfunc, !isLanesCore); @@ -591,28 +591,61 @@ void populate_func_lookup_table( lua_State* L, int _i, char const* name_) DEBUGSPEW_CODE( -- debugspew_indent_depth); } +void call_on_state_create( lua_State* L, lua_State* from_, enum eLookupMode mode_) +{ + if( s_on_state_create_func != NULL) + { + STACK_CHECK( L); + DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); + if( s_on_state_create_func != initialize_on_state_create) + { + // C function: recreate a closure in the new state, bypassing the lookup scheme + lua_pushcfunction( L, s_on_state_create_func); + } + else // Lua function located in the config table, copied when we opened "lanes.core" + { + if( mode_ != eLM_LaneBody) + { + // if attempting to call in a keeper state, do nothing because the function doesn't exist there + return; + } + lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); + lua_getfield( L, -1, "on_state_create"); + lua_remove( L, -2); + } + // capture error and forward it to main state + if( lua_pcall( L, 0, 0, 0) != LUA_OK) + { + (void) luaL_error( from_, "on_state_create failed: \"%s\"", lua_isstring( L, -1) ? lua_tostring( L, -1) : lua_typename( L, lua_type( L, -1))); + } + STACK_END( L, 0); + } +} + /* -* Like 'luaL_openlibs()' but allows the set of libraries be selected -* -* NULL no libraries, not even base -* "" base library only -* "io,string" named libraries -* "*" all libraries -* -* Base ("unpack", "print" etc.) is always added, unless 'libs' is NULL. -* -*/ -lua_State* luaG_newstate( lua_State* _from, char const* libs) + * Like 'luaL_openlibs()' but allows the set of libraries be selected + * + * NULL no libraries, not even base + * "" base library only + * "io,string" named libraries + * "*" all libraries + * + * Base ("unpack", "print" etc.) is always added, unless 'libs' is NULL. + * + * *NOT* called for keeper states! + * + */ +lua_State* luaG_newstate( lua_State* from_, char const* libs_) { // reuse alloc function from the originating state #if PROPAGATE_ALLOCF - PROPAGATE_ALLOCF_PREP( _from); + PROPAGATE_ALLOCF_PREP( from_); #endif // PROPAGATE_ALLOCF lua_State* L = PROPAGATE_ALLOCF_ALLOC(); if( L == NULL) { - (void) luaL_error( _from, "luaG_newstate() failed while creating state; out of memory"); + (void) luaL_error( from_, "luaG_newstate() failed while creating state; out of memory"); } // we'll need this everytime we transfer some C function from/to this state @@ -620,7 +653,7 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) lua_setfield( L, LUA_REGISTRYINDEX, LOOKUP_REGKEY); // neither libs (not even 'base') nor special init func: we are done - if( libs == NULL && s_on_state_create_func == NULL) + if( libs_ == NULL && s_on_state_create_func == NULL) { DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "luaG_newstate(NULL)\n" INDENT_END)); return L; @@ -636,17 +669,17 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) // Anything causes 'base' to be taken in // - if( libs != NULL) + if( libs_ != NULL) { // special "*" case (mainly to help with LuaJIT compatibility) // as we are called from luaopen_lanes_core() already, and that would deadlock - if( libs[0] == '*' && libs[1] == 0) + if( libs_[0] == '*' && libs_[1] == 0) { DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END)); luaL_openlibs( L); // don't forget lanes.core for regular lane states - open1lib( L, "lanes.core", 10, _from); - libs = NULL; // done with libs + open1lib( L, "lanes.core", 10, from_); + libs_ = NULL; // done with libs } else { @@ -665,11 +698,11 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) STACK_END( L, 0); // scan all libraries, open them one by one - if( libs) + if( libs_) { char const* p; unsigned int len = 0; - for( p = libs; *p; p += len) + for( p = libs_; *p; p += len) { // skip delimiters ('.' can be part of name for "lanes.core") while( *p && !isalnum( *p) && *p != '.') @@ -679,37 +712,17 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) while( isalnum( p[len]) || p[len] == '.') ++ len; // open library - open1lib( L, p, len, _from); + open1lib( L, p, len, from_); } serialize_require( L); } lua_gc( L, LUA_GCRESTART, 0); - STACK_CHECK( L); // call this after the base libraries are loaded and GC is restarted - if( s_on_state_create_func != NULL) - { - DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "calling on_state_create()\n" INDENT_END)); - if( s_on_state_create_func != initialize_on_state_create) - { - // C function: recreate a closure in the new state, bypassing the lookup scheme - lua_pushcfunction( L, s_on_state_create_func); - } - else // Lua function located in the config table - { - lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); - lua_getfield( L, -1, "on_state_create"); - lua_remove( L, -2); - } - // capture error and forward it to main state - if( lua_pcall( L, 0, 0, 0) != LUA_OK) - { - (void) luaL_error( _from, "on_state_create failed: \"%s\"", lua_isstring( L, -1) ? lua_tostring( L, -1) : lua_typename( L, lua_type( L, -1))); - } - STACK_MID( L, 0); - } + call_on_state_create( L, from_, eLM_LaneBody); + STACK_CHECK( L); // after all this, register everything we find in our name<->function database lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack populate_func_lookup_table( L, -1, NULL); @@ -743,7 +756,7 @@ lua_State* luaG_newstate( lua_State* _from, char const* libs) /* * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying */ -#define DEEP_PROXY_CACHE_KEY ((void*)luaG_push_proxy) +#define DEEP_PROXY_CACHE_KEY ((void*)push_deep_proxy) static void push_registry_subtable_mode( lua_State *L, void *token, const char* mode ); static void push_registry_subtable( lua_State *L, void *token ); @@ -752,107 +765,76 @@ static void push_registry_subtable( lua_State *L, void *token ); * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. * Pops the both values off the stack. */ -void set_deep_lookup( lua_State *L ) { - - STACK_GROW(L,3); - - STACK_CHECK( L); -#if 1 - push_registry_subtable( L, DEEP_LOOKUP_KEY ); -#else - /* ..to be removed.. */ - lua_pushlightuserdata( L, DEEP_LOOKUP_KEY ); - lua_rawget( L, LUA_REGISTRYINDEX ); - - if (lua_isnil(L,-1)) { - // First time here; let's make the lookup - // - lua_pop(L,1); - - lua_newtable(L); - lua_pushlightuserdata( L, DEEP_LOOKUP_KEY ); - lua_pushvalue(L,-2); - // - // [-3]: {} (2nd ref) - // [-2]: DEEP_LOOKUP_KEY - // [-1]: {} - - lua_rawset( L, LUA_REGISTRYINDEX ); - // - // [-1]: lookup table (empty) - } -#endif - STACK_MID( L, 1); - - lua_insert(L,-3); - - // [-3]: lookup table - // [-2]: A - // [-1]: B - - lua_pushvalue( L,-1 ); // B - lua_pushvalue( L,-3 ); // A - lua_rawset( L, -5 ); // B->A - lua_rawset( L, -3 ); // A->B - lua_pop( L,1 ); - - STACK_END( L, -2); +static void set_deep_lookup( lua_State* L) +{ + STACK_GROW( L, 3); + STACK_CHECK( L); // a b + push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {} + STACK_MID( L, 1); + lua_insert( L, -3); // {} a b + lua_pushvalue( L, -1); // {} a b b + lua_pushvalue( L,-3); // {} a b b a + lua_rawset( L, -5); // {} a b + lua_rawset( L, -3); // {} + lua_pop( L, 1); // + STACK_END( L, -2); } /* * Pops the key (metatable or idfunc) off the stack, and replaces with the * deep lookup value (idfunc/metatable/nil). */ -void get_deep_lookup( lua_State *L ) { - - STACK_GROW(L,1); +static void get_deep_lookup( lua_State* L) +{ + STACK_GROW( L, 1); + STACK_CHECK( L); // a + lua_pushlightuserdata( L, DEEP_LOOKUP_KEY); // a DLK + lua_rawget( L, LUA_REGISTRYINDEX); // a {} - STACK_CHECK( L); - lua_pushlightuserdata( L, DEEP_LOOKUP_KEY ); - lua_rawget( L, LUA_REGISTRYINDEX ); - - if (!lua_isnil(L,-1)) { - // [-2]: key (metatable or idfunc) - // [-1]: lookup table - - lua_insert( L, -2 ); - lua_rawget( L, -2 ); - - // [-2]: lookup table - // [-1]: value (metatable / idfunc / nil) - } - lua_remove(L,-2); - // remove lookup, or unused key - STACK_END( L, 0); + if( !lua_isnil( L, -1)) + { + lua_insert( L, -2); // {} a + lua_rawget( L, -2); // {} b + } + lua_remove( L, -2); // a|b + STACK_END( L, 0); } /* * Return the registered ID function for 'index' (deep userdata proxy), * or NULL if 'index' is not a deep userdata proxy. */ -static -luaG_IdFunction get_idfunc( lua_State *L, int index ) +static inline luaG_IdFunction get_idfunc( lua_State* L, int index, enum eLookupMode mode_) { - luaG_IdFunction ret; - - index = lua_absindex( L, index); - - STACK_GROW(L,1); - - STACK_CHECK( L); - if (!lua_getmetatable( L, index )) - return NULL; // no metatable - - // [-1]: metatable of [index] + // when looking inside a keeper, we are 100% sure the object is a deep userdata + if( mode_ == eLM_FromKeeper) + { + DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, index); + // we can (and must) cast and fetch the internally stored idfunc + return (*proxy)->idfunc; + } + else + { + // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database + // it is the only way to ensure that the userdata is indeed a deep userdata! + // of course, we could just trust the caller, but we won't + luaG_IdFunction ret; + STACK_GROW( L, 1); + STACK_CHECK( L); + + if( !lua_getmetatable( L, index)) // deep ... metatable? + { + return NULL; // no metatable: can't be a deep userdata object! + } - get_deep_lookup(L); - // - // [-1]: idfunc/nil + // replace metatable with the idfunc pointer, if it is actually a deep userdata + get_deep_lookup( L); // deep ... idfunc|nil - ret= (luaG_IdFunction)lua_touserdata(L,-1); - lua_pop(L,1); - STACK_END( L, 0); - return ret; + ret = (luaG_IdFunction) lua_touserdata( L, -1); // NULL if not a userdata + lua_pop( L, 1); + STACK_END( L, 0); + return ret; + } } @@ -862,205 +844,190 @@ luaG_IdFunction get_idfunc( lua_State *L, int index ) * End of life for a proxy object; reduce the deep reference count and clean * it up if reaches 0. */ -static -int deep_userdata_gc( lua_State *L ) +static int deep_userdata_gc( lua_State* L) { - DEEP_PRELUDE **proxy= (DEEP_PRELUDE**)lua_touserdata( L, 1 ); - DEEP_PRELUDE *p= *proxy; - int v; + DEEP_PRELUDE** proxy = (DEEP_PRELUDE**) lua_touserdata( L, 1); + DEEP_PRELUDE* p = *proxy; + int v; - *proxy= 0; // make sure we don't use it any more + *proxy = 0; // make sure we don't use it any more - MUTEX_LOCK( &deep_lock ); - v= --(p->refcount); - MUTEX_UNLOCK( &deep_lock ); - - if (v==0) - { - // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup - // - luaG_IdFunction idfunc = get_idfunc(L,1); - ASSERT_L(idfunc); - - lua_settop( L, 0); // clean stack so we can call 'idfunc' directly + MUTEX_LOCK( &deep_lock); + v = -- (p->refcount); + MUTEX_UNLOCK( &deep_lock); - // void= idfunc( "delete", lightuserdata ) - // - lua_pushlightuserdata( L, p->deep ); - idfunc( L, "delete"); - - // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything! - if ( lua_gettop( L) > 1) - luaL_error( L, "Bad idfunc on \"delete\": returned something"); - - DEEP_FREE( (void*)p ); - } - return 0; + if( v == 0) + { + // clean stack so we can call 'idfunc' directly + lua_settop( L, 0); + // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup + lua_pushlightuserdata( L, p->deep); + ASSERT_L( p->idfunc); + p->idfunc( L, eDO_delete); + DEEP_FREE( (void*) p); + + // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything! + if ( lua_gettop( L) > 1) + { + luaL_error( L, "Bad idfunc(eDO_delete): should not push anything"); + } + } + return 0; } /* -* Push a proxy userdata on the stack. -* -* Initializes necessary structures if it's the first time 'idfunc' is being -* used in this Lua state (metatable, registring it). Otherwise, increments the -* reference count. -*/ -void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *prelude ) + * Push a proxy userdata on the stack. + * returns NULL if ok, else some error string related to bad idfunc behavior or module require problem + * (error cannot happen with mode_ == eLM_ToKeeper) + * + * Initializes necessary structures if it's the first time 'idfunc' is being + * used in this Lua state (metatable, registring it). Otherwise, increments the + * reference count. + */ +char const* push_deep_proxy( lua_State* L, DEEP_PRELUDE* prelude, enum eLookupMode mode_) { - DEEP_PRELUDE **proxy; - - // Check if a proxy already exists - push_registry_subtable_mode(L, DEEP_PROXY_CACHE_KEY, "v"); - lua_pushlightuserdata(L, prelude->deep); - lua_rawget(L, -2); - if (!lua_isnil(L, -1)) - { - lua_remove(L, -2); // deep proxy cache table - return; - } - else - { - lua_pop(L, 2); // Pop the nil and proxy cache table - } - - MUTEX_LOCK( &deep_lock ); - ++(prelude->refcount); // one more proxy pointing to this deep data - MUTEX_UNLOCK( &deep_lock ); - - STACK_GROW(L,4); - - STACK_CHECK( L); - - proxy= lua_newuserdata( L, sizeof( DEEP_PRELUDE* ) ); - ASSERT_L(proxy); - *proxy= prelude; + DEEP_PRELUDE** proxy; - // Get/create metatable for 'idfunc' (in this state) - // - lua_pushlightuserdata( L, idfunc ); // key - get_deep_lookup(L); - // - // [-2]: proxy - // [-1]: metatable / nil - - if (lua_isnil(L,-1)) - { - // No metatable yet. We have two things to do: - // 1 - make one and register it - { - int oldtop; + // Check if a proxy already exists + push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC + lua_pushlightuserdata( L, prelude->deep); // DPC deep + lua_rawget( L, -2); // DPC proxy + if ( !lua_isnil( L, -1)) + { + lua_remove( L, -2); // proxy + return NULL; + } + else + { + lua_pop( L, 1); // DPC + } - lua_pop( L, 1); + MUTEX_LOCK( &deep_lock); + ++ (prelude->refcount); // one more proxy pointing to this deep data + MUTEX_UNLOCK( &deep_lock); - // tbl= idfunc( "metatable" ) - // - oldtop = lua_gettop( L); - idfunc( L, "metatable"); - // - // [-2]: proxy - // [-1]: metatable (returned by 'idfunc') + STACK_GROW( L, 7); + STACK_CHECK( L); - if (lua_gettop( L) - oldtop != 1 || !lua_istable(L, -1)) - { - luaL_error( L, "Bad idfunc on \"metatable\": did not return one" ); - } + proxy = lua_newuserdata( L, sizeof( DEEP_PRELUDE*)); // DPC proxy + ASSERT_L( proxy); + *proxy = prelude; - // Add '__gc' method - // - lua_pushcfunction( L, deep_userdata_gc ); - lua_setfield( L, -2, "__gc" ); + // Get/create metatable for 'idfunc' (in this state) + lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy idfunc + get_deep_lookup( L); // DPC proxy metatable? - // Memorize for later rounds - // - lua_pushvalue( L,-1 ); - lua_pushlightuserdata( L, idfunc ); - // - // [-4]: proxy - // [-3]: metatable (2nd ref) - // [-2]: metatable - // [-1]: idfunc - - set_deep_lookup(L); - } - - // 2 - cause the target state to require the module that exported the idfunc - // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc - STACK_CHECK( L); - { - char const * modname; - // make sure the function pushed a single value on the stack! - { - int oldtop = lua_gettop( L); - idfunc( L, "module"); // ... "module"/nil - if( lua_gettop( L) - oldtop != 1) - { - luaL_error( L, "Bad idfunc on \"module\": should return a single value"); - } - } - modname = luaL_optstring( L, -1, NULL); // raises an error if not a string or nil - if( modname) // we actually got a module name - { - // somehow, L.registry._LOADED can exist without having registered the 'package' library. - lua_getglobal( L, "require"); // ... "module" require() - // check that the module is already loaded (or being loaded, we are happy either way) - if( lua_isfunction( L, -1)) - { - lua_insert( L, -2); // ... require() "module" - lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // ... require() "module" L.registry._LOADED - if( lua_istable( L, -1)) - { - bool_t alreadyloaded; - lua_pushvalue( L, -2); // ... require() "module" L.registry._LOADED "module" - lua_rawget( L, -2); // ... require() "module" L.registry._LOADED module - alreadyloaded = lua_toboolean( L, -1); - if( !alreadyloaded) // not loaded - { - lua_pop( L, 2); // ... require() "module" - lua_call( L, 1, 0); // call require "modname" // ... - } - else // already loaded, we are happy - { - lua_pop( L, 4); // ... - } - } - else // no L.registry._LOADED; can this ever happen? - { - luaL_error( L, "unexpected error while requiring a module"); - lua_pop( L, 3); // ... - } - } - else // a module name, but no require() function :-( - { - luaL_error( L, "lanes receiving deep userdata should register the 'package' library"); - lua_pop( L, 2); // ... - } - } - else // no module name - { - lua_pop( L, 1); // ... - } - } - STACK_END( L, 0); - } - STACK_MID( L, 2); - ASSERT_L( lua_isuserdata(L,-2) ); - ASSERT_L( lua_istable(L,-1) ); + if( lua_isnil( L, -1)) // // No metatable yet. + { + char const* modname; + int oldtop = lua_gettop( L); // DPC proxy nil + lua_pop( L, 1); // DPC proxy + // 1 - make one and register it + if( mode_ != eLM_ToKeeper) + { + prelude->idfunc( L, eDO_metatable); // DPC proxy metatable + if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1)) + { + lua_pop( L, 3); // + return "Bad idfunc(eOP_metatable): unexpected pushed value"; + } + // make sure the idfunc didn't export __gc, as we will store our own + lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc + if( !lua_isnil( L, -1)) + { + lua_pop( L, 4); // + return "idfunc-created metatable shouldn't contain __gc"; + } + lua_pop( L, 1); // DPC proxy metatable + } + else + { + // keepers need a minimal metatable that only contains __gc + lua_newtable( L); // DPC proxy metatable + } + // Add our own '__gc' method + lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable __gc + lua_setfield( L, -2, "__gc"); // DPC proxy metatable - // [-2]: proxy userdata - // [-1]: metatable to use + // Memorize for later rounds + lua_pushvalue( L, -1); // DPC proxy metatable metatable + lua_pushlightuserdata( L, prelude->idfunc); // DPC proxy metatable metatable idfunc + set_deep_lookup( L); // DPC proxy metatable - lua_setmetatable( L, -2 ); - - // If we're here, we obviously had to create a new proxy, so cache it. - push_registry_subtable_mode(L, DEEP_PROXY_CACHE_KEY, "v"); - lua_pushlightuserdata(L, (*proxy)->deep); - lua_pushvalue(L, -3); // Copy of the proxy - lua_rawset(L, -3); - lua_pop(L, 1); // Remove the cache proxy table - - STACK_END( L, 1); - // [-1]: proxy userdata + // 2 - cause the target state to require the module that exported the idfunc + // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc + { + int oldtop = lua_gettop( L); + modname = (char const*) prelude->idfunc( L, eDO_module); // DPC proxy metatable + // make sure the function pushed nothing on the stack! + if( lua_gettop( L) - oldtop != 0) + { + lua_pop( L, 3); // + return "Bad idfunc(eOP_module): should not push anything"; + } + } + if( modname) // we actually got a module name + { + // somehow, L.registry._LOADED can exist without having registered the 'package' library. + lua_getglobal( L, "require"); // DPC proxy metatable require() + // check that the module is already loaded (or being loaded, we are happy either way) + if( lua_isfunction( L, -1)) + { + lua_pushstring( L, modname); // DPC proxy metatable require() "module" + lua_getfield( L, LUA_REGISTRYINDEX, "_LOADED"); // DPC proxy metatable require() "module" _R._LOADED + if( lua_istable( L, -1)) + { + bool_t alreadyloaded; + lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module" + lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module + alreadyloaded = lua_toboolean( L, -1); + if( !alreadyloaded) // not loaded + { + int require_result; + lua_pop( L, 2); // DPC proxy metatable require() "module" + // require "modname" + require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error? + if( require_result != LUA_OK) + { + // failed, return the error message + lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname); + lua_insert( L, -2); // DPC proxy metatable prefix error + lua_concat( L, 2); // DPC proxy metatable error + return lua_tostring( L, -1); + } + } + else // already loaded, we are happy + { + lua_pop( L, 4); // DPC proxy metatable + } + } + else // no L.registry._LOADED; can this ever happen? + { + lua_pop( L, 6); // + return "unexpected error while requiring a module identified by idfunc(eOP_module)"; + } + } + else // a module name, but no require() function :-( + { + lua_pop( L, 4); // + return "lanes receiving deep userdata should register the 'package' library"; + } + } + } + STACK_MID( L, 2); // DPC proxy metatable + ASSERT_L( lua_isuserdata( L, -2)); + ASSERT_L( lua_istable( L, -1)); + lua_setmetatable( L, -2); // DPC proxy + + // If we're here, we obviously had to create a new proxy, so cache it. + lua_pushlightuserdata( L, (*proxy)->deep); // DPC proxy deep + lua_pushvalue( L, -2); // DPC proxy deep proxy + lua_rawset( L, -4); // DPC proxy + lua_remove( L, -2); // proxy + ASSERT_L( lua_isuserdata( L, -1)); + STACK_END( L, 0); + return NULL; } @@ -1086,39 +1053,34 @@ void luaG_push_proxy( lua_State *L, luaG_IdFunction idfunc, DEEP_PRELUDE *prelud * * Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' */ -int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc) +int luaG_newdeepuserdata( lua_State* L, luaG_IdFunction idfunc) { - int oldtop; - - DEEP_PRELUDE *prelude= DEEP_MALLOC( sizeof(DEEP_PRELUDE) ); - ASSERT_L(prelude); - - prelude->refcount= 0; // 'luaG_push_proxy' will lift it to 1 - - STACK_GROW(L,1); - STACK_CHECK( L); - - // lightuserdata= idfunc( "new" [, ...] ) - // - oldtop = lua_gettop( L); - idfunc(L, "new"); - - if( lua_gettop( L) - oldtop != 1 || lua_type( L, -1) != LUA_TLIGHTUSERDATA) - { - luaL_error( L, "Bad idfunc on \"new\": did not return light userdata"); - } + char const* errmsg; + DEEP_PRELUDE* prelude = DEEP_MALLOC( sizeof(DEEP_PRELUDE)); + ASSERT_L( prelude); - prelude->deep= lua_touserdata(L,-1); - ASSERT_L(prelude->deep); + prelude->refcount = 0; // 'push_deep_proxy' will lift it to 1 + prelude->idfunc = idfunc; - lua_pop(L,1); // pop deep data - - luaG_push_proxy( L, idfunc, prelude ); - // - // [-1]: proxy userdata + STACK_GROW( L, 1); + STACK_CHECK( L); + { + int oldtop = lua_gettop( L); + prelude->deep = idfunc( L, eDO_new); + ASSERT_L( prelude->deep); - STACK_END( L, 1); - return 1; + if( lua_gettop( L) - oldtop != 0) + { + luaL_error( L, "Bad idfunc(eDO_new): should not push anything on the stack"); + } + } + errmsg = push_deep_proxy( L, prelude, eLM_LaneBody); // proxy + if( errmsg != NULL) + { + luaL_error( L, errmsg); + } + STACK_END( L, 1); + return 1; } @@ -1128,52 +1090,51 @@ int luaG_deep_userdata( lua_State *L, luaG_IdFunction idfunc) * Reference count is not changed, and access to the deep userdata is not * serialized. It is the module's responsibility to prevent conflicting usage. */ -void *luaG_todeep( lua_State *L, luaG_IdFunction idfunc, int index ) +void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index) { - DEEP_PRELUDE **proxy; + DEEP_PRELUDE** proxy; - STACK_CHECK( L); - if( get_idfunc(L,index) != idfunc) - return NULL; // no metatable, or wrong kind + STACK_CHECK( L); + // ensure it is actually a deep userdata + if( get_idfunc( L, index, eLM_LaneBody) != idfunc) + { + return NULL; // no metatable, or wrong kind + } - proxy= (DEEP_PRELUDE**)lua_touserdata( L, index ); - STACK_END( L, 0); + proxy = (DEEP_PRELUDE**) lua_touserdata( L, index); + STACK_END( L, 0); - return (*proxy)->deep; + return (*proxy)->deep; } /* -* Copy deep userdata between two separate Lua states. -* -* Returns: -* the id function of the copied value, or NULL for non-deep userdata -* (not copied) -*/ -static -luaG_IdFunction luaG_copydeep( lua_State *L, lua_State *L2, int index ) + * Copy deep userdata between two separate Lua states (from L to L2) + * + * Returns: + * the id function of the copied value, or NULL for non-deep userdata + * (not copied) + */ +static luaG_IdFunction copydeep( lua_State* L, lua_State* L2, int index, enum eLookupMode mode_) { - DEEP_PRELUDE **proxy; - DEEP_PRELUDE *p; - - luaG_IdFunction idfunc = get_idfunc( L, index); - if (!idfunc) - return NULL; // not a deep userdata - - // Increment reference count - // - proxy= (DEEP_PRELUDE**)lua_touserdata( L, index ); - p= *proxy; - - luaG_push_proxy( L2, idfunc, p ); - // - // L2 [-1]: proxy userdata + char const* errmsg; + luaG_IdFunction idfunc = get_idfunc( L, index, mode_); + if( idfunc == NULL) + { + return NULL; // not a deep userdata + } - return idfunc; + errmsg = push_deep_proxy( L2, *(DEEP_PRELUDE**) lua_touserdata( L, index), mode_); + if( errmsg != NULL) + { + // raise the error in the proper state (not the keeper) + lua_State* errL = (mode_ == eLM_FromKeeper) ? L2 : L; + luaL_error( errL, errmsg); + } + return idfunc; } - /*---=== Inter-state copying ===---*/ /*-- Metatable copying --*/ @@ -1190,51 +1151,45 @@ luaG_IdFunction luaG_copydeep( lua_State *L, lua_State *L2, int index ) /* * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it */ -static -void push_registry_subtable_mode( lua_State *L, void *token, const char* mode ) { +static void push_registry_subtable_mode( lua_State* L, void* key_, const char* mode_) +{ + STACK_GROW( L, 3); + STACK_CHECK( L); - STACK_GROW(L,3); + lua_pushlightuserdata( L, key_); // key + lua_rawget( L, LUA_REGISTRYINDEX); // {}|nil - STACK_CHECK( L); - - lua_pushlightuserdata( L, token ); - lua_rawget( L, LUA_REGISTRYINDEX ); - // - // [-1]: nil/subtable - - if (lua_isnil(L,-1)) { - lua_pop(L,1); - lua_newtable(L); // value - lua_pushlightuserdata( L, token ); // key - lua_pushvalue(L,-2); - // - // [-3]: value (2nd ref) - // [-2]: key - // [-1]: value - - lua_rawset( L, LUA_REGISTRYINDEX ); - - // Set it's metatable if requested - if (mode) { - lua_newtable(L); - lua_pushliteral(L, "__mode"); - lua_pushstring(L, mode); - lua_rawset(L, -3); - lua_setmetatable(L, -2); - } - } - STACK_END( L, 1); + if( lua_isnil( L, -1)) + { + lua_pop( L, 1); // + lua_newtable( L); // {} + lua_pushlightuserdata( L, key_); // {} key + lua_pushvalue( L, -2); // {} key {} + + // _R[key_] = {} + lua_rawset( L, LUA_REGISTRYINDEX); // {} - ASSERT_L( lua_istable(L,-1) ); + // Set its metatable if requested + if( mode_) + { + lua_newtable( L); // {} mt + lua_pushliteral( L, "__mode"); // {} mt "__mode" + lua_pushstring( L, mode_); // {} mt "__mode" mode + lua_rawset( L, -3); // {} mt + lua_setmetatable( L, -2); // {} + } + } + STACK_END( L, 1); + ASSERT_L( lua_istable( L, -1)); } /* -* Push a registry subtable (keyed by unique 'token') onto the stack. +* Push a registry subtable (keyed by unique 'key_') onto the stack. * If the subtable does not exist, it is created and chained. */ -static -void push_registry_subtable( lua_State *L, void *token ) { - push_registry_subtable_mode(L, token, NULL); +static inline void push_registry_subtable( lua_State* L, void* key_) +{ + push_registry_subtable_mode( L, key_, NULL); } #define REG_MTID ( (void*) get_mt_id ) @@ -1864,12 +1819,10 @@ static void push_cached_func( lua_State* L2, uint_t L2_cache_i, lua_State* L, ui static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, enum eLookupMode mode_, char const* upName_) { bool_t ret = TRUE; - STACK_GROW( L2, 1); - STACK_CHECK( L2); - switch ( lua_type( L, i)) + switch( lua_type( L, i)) { /* Basic types allowed both as values, and as table keys */ @@ -1921,7 +1874,7 @@ static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, u /* Allow only deep userdata entities to be copied across */ DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "USERDATA\n" INDENT_END)); - if( !luaG_copydeep( L, L2, i)) + if( !copydeep( L, L2, i, mode_)) { // Not a deep full userdata bool_t demote = FALSE; @@ -2129,7 +2082,6 @@ static bool_t inter_copy_one_( lua_State* L2, uint_t L2_cache_i, lua_State* L, u } STACK_END( L2, ret ? 1 : 0); - return ret; } @@ -2225,7 +2177,8 @@ void luaG_inter_copy_package( lua_State* L, lua_State* L2, int _idx, enum eLooku // package.loaders is renamed package.searchers in Lua 5.2 // but don't copy it anyway, as the function names change depending on the slot index! // users should provide an on_state_create function to setup custom loaders instead - char const* entries[] = { "path", "cpath", "preload"/*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, NULL}; + // don't copy package.preload in keeper states (they don't know how to translate functions) + char const* entries[] = { "path", "cpath", (mode_ == eLM_LaneBody) ? "preload" : NULL/*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, NULL}; for( i = 0; entries[i]; ++ i) { DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s\n" INDENT_END, entries[i])); -- cgit v1.2.3-55-g6feb