From 909470be9f7ec1dd2d09ae1a371d69c9c652e957 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Sat, 26 Jun 2021 18:25:53 +0200 Subject: fix stack overflow when transfering a clonable userdata referencing itself through a uservalue --- deep_test/deep_test.c | 2 + deep_test/deep_test.vcxproj | 5 + deep_test/deep_test.vcxproj.user | 6 + deep_test/deeptest.lua | 47 +++++--- src/deep.c | 70 ++--------- src/tools.c | 254 +++++++++++++++++++++++++++------------ src/tools.h | 13 ++ 7 files changed, 245 insertions(+), 152 deletions(-) diff --git a/deep_test/deep_test.c b/deep_test/deep_test.c index dabc84d..873428b 100644 --- a/deep_test/deep_test.c +++ b/deep_test/deep_test.c @@ -16,6 +16,7 @@ // ################################################################################################ +// a lanes-deep userdata. needs DeepPrelude and luaG_newdeepuserdata from Lanes code. struct s_MyDeepUserdata { DeepPrelude prelude; // Deep userdata MUST start with this header @@ -190,6 +191,7 @@ static int clonable_gc( lua_State* L) // ################################################################################################ +// this is all we need to make a userdata lanes-clonable. no dependency on Lanes code. static int clonable_lanesclone( lua_State* L) { switch( lua_gettop( L)) diff --git a/deep_test/deep_test.vcxproj b/deep_test/deep_test.vcxproj index 67d3afd..6e25b9a 100644 --- a/deep_test/deep_test.vcxproj +++ b/deep_test/deep_test.vcxproj @@ -115,6 +115,10 @@ $(SolutionDir)_Output\$(ProjectName)\$(PlatformName)\$(Configuration)\ $(SolutionDir)_Tmp\$(ProjectName)\$(PlatformName)\$(Configuration)\ + + $(SolutionDir)_Output\$(ProjectName)\$(PlatformName)\$(Configuration)\ + $(SolutionDir)_Tmp\$(ProjectName)\$(PlatformName)\$(Configuration)\ + Level3 @@ -125,6 +129,7 @@ true $(SolutionDir)Lanes\lanes\src;$(SolutionDir)..\lualib;%(AdditionalIncludeDirectories) $(IntDir)$(TargetName).pdb + _WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true diff --git a/deep_test/deep_test.vcxproj.user b/deep_test/deep_test.vcxproj.user index 70871df..9195f89 100644 --- a/deep_test/deep_test.vcxproj.user +++ b/deep_test/deep_test.vcxproj.user @@ -12,4 +12,10 @@ -i deeptest.lua D:\Boulot\anubis\Lua\bindings\Lanes\lanes\deep_test\ + + D:\Boulot\anubis\Lua\framework\lua53.exe + WindowsLocalDebugger + -i -- deeptest.lua + D:\Boulot\anubis\Lua\bindings\Lanes\lanes\deep_test\ + \ No newline at end of file diff --git a/deep_test/deeptest.lua b/deep_test/deeptest.lua index 92cd372..3c89c3d 100644 --- a/deep_test/deeptest.lua +++ b/deep_test/deeptest.lua @@ -5,8 +5,8 @@ local l = lanes.linda "my linda" local dt = lanes.require "deep_test" local test_deep = true -local test_clonable = false -local test_uvtype = "string" +local test_clonable = true +local test_uvtype = "function" local makeUserValue = function( obj_) if test_uvtype == "string" then @@ -14,12 +14,23 @@ local makeUserValue = function( obj_) elseif test_uvtype == "function" then -- a function that pull the userdata as upvalue local f = function() - print( obj_) + return tostring( obj_) end return f end end +local printDeep = function( prefix_, obj_, t_) + local uservalue = obj_:getuv( 1) + print( prefix_) + print ( obj_, uservalue, type( uservalue) == "function" and uservalue() or "") + if t_ then + for k, v in pairs( t_) do + print( k, v) + end + end +end + local performTest = function( obj_) -- setup the userdata with some value and a uservalue obj_:set( 666) @@ -29,15 +40,20 @@ local performTest = function( obj_) -- lua 5.4 supports multiple uservalues of arbitrary types -- obj_:setuv( 2, "ENDUV") + local t = + { + ["key"] = obj_, + -- [obj_] = "val" + } + -- read back the contents of the object - print( "immediate:", obj_, obj_:getuv( 1)) + printDeep( "immediate:", obj_, t) -- send the object in a linda, get it back out, read the contents - l:set( "key", obj_) + l:set( "key", obj_, t) -- when obj_ is a deep userdata, out is the same userdata as obj_ (not another one pointing on the same deep memory block) because of an internal cache table [deep*] -> proxy) -- when obj_ is a clonable userdata, we get a different clone everytime we cross a linda or lane barrier - local out = l:get( "key") - print( "out of linda:", out, out:getuv( 1)) + printDeep( "out of linda:", l:get( "key", 2)) -- send the object in a lane through parameter passing, the lane body returns it as return value, read the contents local g = lanes.gen( @@ -45,23 +61,26 @@ local performTest = function( obj_) , { required = { "deep_test"} -- we will transfer userdata created by this module, so we need to make this lane aware of it } - , function( param_) - -- read contents inside lane - print( "in lane:", param_, param_:getuv( 1)) - return param_ + , function( arg_, t_) + -- read contents inside lane: arg_ and t_ by argument + printDeep( "in lane, as arguments:", arg_, t_) + -- read contents inside lane: obj_ and t by upvalue + printDeep( "in lane, as upvalues:", obj_, t) + return arg_, t_ end ) - h = g( obj_) + h = g( obj_, t) -- when obj_ is a deep userdata, from_lane is the same userdata as obj_ (not another one pointing on the same deep memory block) because of an internal cache table [deep*] -> proxy) -- when obj_ is a clonable userdata, we get a different clone everytime we cross a linda or lane barrier - local from_lane = h[1] - print( "from lane:", from_lane, from_lane:getuv( 1)) + printDeep( "from lane:", h[1], h[2]) end if test_deep then + print "DEEP" performTest( dt.new_deep()) end if test_clonable then + print "CLONABLE" performTest( dt.new_clonable()) end diff --git a/src/deep.c b/src/deep.c index fd2ae73..3c7680d 100644 --- a/src/deep.c +++ b/src/deep.c @@ -49,59 +49,6 @@ THE SOFTWARE. /*-- Metatable copying --*/ -/* - * 'reg[ REG_MT_KNOWN ]'= { - * [ table ]= id_uint, - * ... - * [ id_uint ]= table, - * ... - * } - */ - -/* -* 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, UniqueKey key_, const char* mode_) -{ - STACK_GROW( L, 3); - STACK_CHECK( L, 0); - - REGISTRY_GET( L, key_); // {}|nil - STACK_MID( L, 1); - - if( lua_isnil( L, -1)) - { - lua_pop( L, 1); // - lua_newtable( L); // {} - // _R[key_] = {} - REGISTRY_SET( L, key_, lua_pushvalue( L, -2)); // {} - STACK_MID( 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 'key_') onto the stack. -* If the subtable does not exist, it is created and chained. -*/ -void push_registry_subtable( lua_State* L, UniqueKey key_) -{ - push_registry_subtable_mode( L, key_, NULL); -} - - /*---=== Deep userdata ===---*/ /* @@ -503,10 +450,10 @@ void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index) * the id function of the copied value, or NULL for non-deep userdata * (not copied) */ -bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode mode_) +bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) { char const* errmsg; - luaG_IdFunction idfunc = get_idfunc( L, index, mode_); + luaG_IdFunction idfunc = get_idfunc( L, i, mode_); int nuv = 0; if( idfunc == NULL) @@ -518,24 +465,25 @@ bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode STACK_CHECK( L2, 0); // extract all uservalues of the source - while( lua_getiuservalue( L, index, nuv + 1) != LUA_TNONE) // ... u [uv]+ nil + while( lua_getiuservalue( L, i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil { ++ nuv; } // last call returned TNONE and pushed nil, that we don't need - lua_pop( L, 1); // ... u [uv]+ + lua_pop( L, 1); // ... u [uv]* STACK_MID( L, nuv); - errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, index), nuv, mode_); // u + errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u // transfer all uservalues of the source in the destination { int const clone_i = lua_gettop( L2); - luaG_inter_move( U, L, L2, nuv, mode_); // ... u // u [uv]+ - while( nuv > 0) + while( nuv) { + inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // u uv + lua_pop( L, 1); // ... u [uv]* // this pops the value from the stack - lua_setiuservalue( L2, clone_i, nuv); // ... u // u + lua_setiuservalue( L2, clone_i, nuv); // u -- nuv; } } diff --git a/src/tools.c b/src/tools.c index a0ba20e..18015c1 100644 --- a/src/tools.c +++ b/src/tools.c @@ -48,12 +48,60 @@ THE SOFTWARE. #include "uniquekey.h" // functions implemented in deep.c -extern bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode mode_); +extern bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_); extern void push_registry_subtable( lua_State* L, UniqueKey key_); DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); +// ################################################################################################ + +/* + * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it + */ +void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_) +{ + STACK_GROW( L, 3); + STACK_CHECK( L, 0); + + REGISTRY_GET( L, key_); // {}|nil + STACK_MID( L, 1); + + if( lua_isnil( L, -1)) + { + lua_pop( L, 1); // + lua_newtable( L); // {} + // _R[key_] = {} + REGISTRY_SET( L, key_, lua_pushvalue( L, -2)); // {} + STACK_MID( 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 'key_') onto the stack. + * If the subtable does not exist, it is created and chained. + */ +void push_registry_subtable( lua_State* L, UniqueKey key_) +{ + push_registry_subtable_mode( L, key_, NULL); +} + +// ################################################################################################ + /*---=== luaG_dump ===---*/ #ifdef _DEBUG void luaG_dump( lua_State* L) @@ -695,7 +743,7 @@ static bool_t lookup_table( lua_State* L2, lua_State* L, uint_t i, LookupMode mo return FALSE; } // push the equivalent table in the destination's stack, retrieved from the lookup table - STACK_CHECK( L2, 0); // L // L2 + STACK_CHECK( L2, 0); // L // L2 STACK_GROW( L2, 3); // up to 3 slots are necessary on error switch( mode_) { @@ -1066,13 +1114,6 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, LookupMod * Copy a function over, which has not been found in the cache. * L2 has the cache key for this function at the top of the stack */ -enum e_vt -{ - VT_NORMAL, - VT_KEY, - VT_METATABLE -}; -static bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt value_type, LookupMode mode_, char const* upName_); #if USE_DEBUG_SPEW static char const* lua_type_names[] = @@ -1412,17 +1453,41 @@ static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache } } +/* +* The clone cache is a weak valued table listing all clones, indexed by their userdatapointer +* fnv164 of string "CLONABLES_CACHE_KEY" generated at https://www.pelock.com/products/hash-calculator +*/ +static DECLARE_CONST_UNIQUE_KEY( CLONABLES_CACHE_KEY, 0xD04EE018B3DEE8F5); + static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) { + void* const source = lua_touserdata( L, i); + STACK_CHECK( L, 0); STACK_CHECK( L2, 0); + // Check if the source was already cloned during this copy + lua_pushlightuserdata( L2, source); // ... source + lua_rawget( L2, L2_cache_i); // ... clone? + if ( !lua_isnil( L2, -1)) + { + STACK_MID( L2, 1); + return TRUE; + } + else + { + lua_pop( L2, 1); // ... + } + STACK_MID( L2, 0); + + // no metatable? -> not clonable if( !lua_getmetatable( L, i)) // ... mt? { STACK_MID( L, 0); return FALSE; } + // no __lanesclone? -> not clonable lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? if( lua_isnil( L, -1)) { @@ -1432,8 +1497,8 @@ static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_Stat } { + int const mt = lua_absindex( L, -2); size_t userdata_size = 0; - void* const source = lua_touserdata( L, i); void* clone = NULL; lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone @@ -1443,69 +1508,77 @@ static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_Stat userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size lua_pop( L, 1); // ... mt __lanesclone // we need to copy over the uservalues of the userdata as well - lua_pushnil( L2); // ... nil { - int const clone_i = lua_gettop( L2); + // extract all the uservalues, but don't transfer them yet int uvi = 0; - while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... mt __lanesclone uv + while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... mt __lanesclone [uv]+ nil { - luaG_inter_move( U, L, L2, 1, mode_); // ... mt __lanesclone // ... nil [uv]+ ++ uvi; } // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now - lua_pop( L, 1); // ... mt __lanesclone + lua_pop( L, 1); // ... mt __lanesclone [uv]+ // create the clone userdata with the required number of uservalue slots - clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... nil [uv]+ u - lua_replace( L2, clone_i); // ... u [uv]+ + clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... u + // copy the metatable in the target state, and give it to the clone we put there + if( inter_copy_one( U, L2, L2_cache_i, L, mt, VT_NORMAL, mode_, upName_)) // ... u mt|sentinel + { + if( eLM_ToKeeper == mode_) // ... u sentinel + { + ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel); + // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn + lua_getupvalue( L2, -1, 1); // ... u sentinel fqn + lua_remove( L2, -2); // ... u fqn + lua_insert( L2, -2); // ... fqn u + lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel + } + else // from keeper or direct // ... u mt + { + ASSERT_L( lua_istable( L2, -1)); + lua_setmetatable( L2, -2); // ... u + } + STACK_MID( L2, 1); + } + else + { + (void) luaL_error( L, "Error copying a metatable"); + } + // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel + lua_pushlightuserdata( L2, source); // ... u source + lua_pushvalue( L2, -2); // ... u source u + lua_rawset( L2, L2_cache_i); // ... u + // make sure we have the userdata now + if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel + { + lua_getupvalue( L2, -1, 2); // ... userdata_clone_sentinel u + } // assign uservalues while( uvi > 0) { + inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // ... u uv + lua_pop( L, 1); // ... mt __lanesclone [uv]* // this pops the value from the stack - lua_setiuservalue( L2, clone_i, uvi); // ... u [uv]+ + lua_setiuservalue( L2, -2, uvi); // ... u -- uvi; } - // when we are done, all uservalues are popped from the stack - STACK_MID( L2, 1); // ... u - } - STACK_MID( L, 2); // ... mt __lanesclone - // call cloning function in source state to perform the actual memory cloning - lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone - lua_pushlightuserdata( L, source); // ... mt __lanesclone source - lua_call( L, 2, 0); // ... mt - STACK_MID( L, 1); - // copy the metatable in the target state - if( inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // ... u mt? - { - lua_pop( L, 1); // ... - STACK_MID( L, 0); - // when writing to a keeper state, we have here a sentinel function with the metatable's fqn as upvalue - if( eLM_ToKeeper == mode_) // ... u sentinel + // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination + if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel u { - ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel); - // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn - lua_getupvalue( L2, -1, 1); // ... u sentinel fqn - lua_remove( L2, -2); // ... u fqn - lua_insert( L2, -2); // ... fqn u - lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel - } - else // from keeper or direct, we have the userdata and the metatable - { - ASSERT_L( lua_istable( L2, -1)); - lua_setmetatable( L2, -2); // ... u + lua_pop( L2, 1); // ... userdata_clone_sentinel } STACK_MID( L2, 1); - STACK_MID( L, 0); - return TRUE; - } - else - { - (void) luaL_error( L, "Error copying a metatable"); + STACK_MID( L, 2); + // call cloning function in source state to perform the actual memory cloning + lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone + lua_pushlightuserdata( L, source); // ... mt __lanesclone clone source + lua_call( L, 2, 0); // ... mt + STACK_MID( L, 1); } } STACK_END( L2, 1); + lua_pop( L, 1); // ... STACK_END( L, 0); - return FALSE; + return TRUE; } static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) @@ -1516,17 +1589,21 @@ static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i { return FALSE; } - // Allow only deep userdata entities to be copied across - DEBUGSPEW_CODE( fprintf( stderr, "USERDATA\n")); - if( copydeep( U, L, L2, i, mode_)) + + // try clonable userdata first + if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_)) { + STACK_MID( L, 0); + STACK_MID( L2, 1); return TRUE; } STACK_MID( L, 0); STACK_MID( L2, 0); - if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_)) + // Allow only deep userdata entities to be copied across + DEBUGSPEW_CODE( fprintf( stderr, "USERDATA\n")); + if( copydeep( U, L2, L2_cache_i, L, i, mode_, upName_)) { STACK_MID( L, 0); STACK_MID( L2, 1); @@ -1563,54 +1640,78 @@ static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i STACK_CHECK( L2, 0); DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_)); - if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata + if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper { // clone the full userdata again size_t userdata_size = 0; void* source; void* clone; - // this function has 2 upvalues: the fqn of its metatable, and the userdata itself + + // let's see if we already restored this userdata lua_getupvalue( L, i, 2); // ... u source = lua_touserdata( L, -1); - lookup_table( L2, L, i, mode_, upName_); // ... u // ... mt + lua_pushlightuserdata( L2, source); // ... source + lua_rawget( L2, L2_cache_i); // ... u? + if( !lua_isnil( L2, -1)) + { + lua_pop( L, 1); // ... + STACK_MID( L, 0); + STACK_MID( L2, 1); + return TRUE; + } + lua_pop( L2, 1); // ... + + // this function has 2 upvalues: the fqn of its metatable, and the userdata itself + lookup_table( L2, L, i, mode_, upName_); // ... mt // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with lua_getfield( L2, -1, "__lanesclone"); // ... mt __lanesclone lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone + // 'i' slot is the closure, but from now on it is the actual userdata + i = lua_gettop( L); + source = lua_touserdata( L, -1); // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone lua_pushlightuserdata( L2, source); // ... mt __lanesclone __lanesclone source lua_call( L2, 1, 1); // ... mt __lanesclone size userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size lua_pop( L2, 1); // ... mt __lanesclone - lua_pushnil( L2); // ... mt __lanesclone nil { - int const clone_i = lua_gettop( L2); + // extract uservalues (don't transfer them yet) int uvi = 0; - while( lua_getiuservalue( L, -1, uvi + 1) != LUA_TNONE) // ... u uv + while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... u uv { - luaG_inter_move( U, L, L2, 1, mode_); // ... u // ... mt __lanesclone nil [uv]+ ++ uvi; } - // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now at the same time as the rest - lua_pop( L, 2); // ... u - STACK_MID( L, 0); // ... + // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now + lua_pop( L, 1); // ... u [uv]* + STACK_MID( L, uvi + 1); // create the clone userdata with the required number of uservalue slots - clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt __lanesclone nil [uv]+ u - lua_replace( L2, clone_i); // ... mt __lanesclone u [uv]+ - // assign uservalues + clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt __lanesclone u + // add it in the cache + lua_pushlightuserdata( L2, source); // ... mt __lanesclone u source + lua_pushvalue( L2, -2); // ... mt __lanesclone u source u + lua_rawset( L2, L2_cache_i); // ... mt __lanesclone u + // set metatable + lua_pushvalue( L2, -3); // ... mt __lanesclone u mt + lua_setmetatable( L2, -2); // ... mt __lanesclone u + // transfer and assign uservalues while( uvi > 0) { + inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_); // ... mt __lanesclone u uv + lua_pop( L, 1); // ... u [uv]* // this pops the value from the stack - lua_setiuservalue( L2, clone_i, uvi); // ... mt __lanesclone u [uv]+ + lua_setiuservalue( L2, -2, uvi); // ... mt __lanesclone u -- uvi; } // when we are done, all uservalues are popped from the stack + lua_pop( L, 1); // ... + STACK_MID( L, 0); STACK_MID( L2, 3); // ... mt __lanesclone u } - lua_insert( L2, -3); // ... u mt __lanesclone - lua_pushlightuserdata( L2, clone); // ... u mt __lanesclone clone - lua_pushlightuserdata( L2, source); // ... u mt __lanesclone clone source - lua_call( L2, 2, 0); // ... u mt - lua_setmetatable( L2, -2); // ... u + // perform the custom cloning part + lua_replace( L2, -3); // ... u __lanesclone + lua_pushlightuserdata( L2, clone); // ... u __lanesclone clone + lua_pushlightuserdata( L2, source); // ... u __lanesclone clone source + lua_call( L2, 2, 0); // ... u } else { @@ -1696,7 +1797,7 @@ static bool_t inter_copy_table( Universe* U, lua_State* L2, uint_t L2_cache_i, l * * Returns TRUE if value was pushed, FALSE if its type is non-supported. */ -static bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) +bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_) { bool_t ret = TRUE; int val_type = lua_type( L, i); @@ -1812,7 +1913,6 @@ static bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua return ret; } - /* * Akin to 'lua_xmove' but copies values between _any_ Lua states. * diff --git a/src/tools.h b/src/tools.h index 0df88e9..3bf5a02 100644 --- a/src/tools.h +++ b/src/tools.h @@ -22,6 +22,19 @@ void luaG_dump( lua_State* L); // ################################################################################################ +void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_); +void push_registry_subtable( lua_State* L, UniqueKey key_); + +enum e_vt +{ + VT_NORMAL, + VT_KEY, + VT_METATABLE +}; +bool_t inter_copy_one( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum e_vt vt, LookupMode mode_, char const* upName_); + +// ################################################################################################ + int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_); int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); -- cgit v1.2.3-55-g6feb