From e2908369e92b14e661b16401b6389d6d4a026278 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 29 Oct 2018 13:30:41 +0100 Subject: add support for deep userdata cloning --- deep_test/deep_test.c | 63 ++++++++++++++- deep_test/deep_test.lua | 17 +++-- src/tools.c | 199 ++++++++++++++++++++++++++---------------------- 3 files changed, 181 insertions(+), 98 deletions(-) diff --git a/deep_test/deep_test.c b/deep_test/deep_test.c index abe9fdc..8f34fe5 100644 --- a/deep_test/deep_test.c +++ b/deep_test/deep_test.c @@ -1,4 +1,5 @@ #include +#include #include "lua.h" #include "lualib.h" @@ -12,6 +13,8 @@ #define LANES_API #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) +// ################################################################################################ + struct s_MyDeepUserdata { lua_Integer val; @@ -33,7 +36,7 @@ static int deep_set( lua_State* L) static int deep_tostring( lua_State* L) { struct s_MyDeepUserdata* self = luaG_todeep( L, deep_test_id, 1); - lua_pushfstring( L, "deep_test(%d)", self->val); + lua_pushfstring( L, "deep(%d)", self->val); return 1; } @@ -102,11 +105,69 @@ int luaD_new_deep( lua_State* L) return luaG_newdeepuserdata( L, deep_test_id); } +// ################################################################################################ +// ################################################################################################ + +struct s_MyClonableUserdata +{ + lua_Integer val; +}; + +// ################################################################################################ + +static int clonable_tostring(lua_State* L) +{ + struct s_MyClonableUserdata* self = (struct s_MyClonableUserdata*) lua_touserdata( L, 1); + lua_pushfstring( L, "clonable(%d)", self->val); + return 1; +} + +// ################################################################################################ + +static int clonable_lanesclone( lua_State* L) +{ + // no need to set the metatable, the Lane copying mechanism will take care of it + struct s_MyClonableUserdata* self = lua_touserdata( L, 1); + struct s_MyClonableUserdata* to = lua_newuserdata( L, sizeof( struct s_MyClonableUserdata)); + memcpy( to, self, sizeof(struct s_MyClonableUserdata)); + return 1; +} + +// ################################################################################################ + +static luaL_Reg const clonable_mt[] = +{ + { "__tostring", clonable_tostring}, + //{ "__gc", deep_gc}, + { "__lanesclone", clonable_lanesclone}, + //{ "set", deep_set}, + { NULL, NULL } +}; + +// ################################################################################################ + +int luaD_new_clonable( lua_State* L) +{ + lua_newuserdata( L, sizeof( struct s_MyClonableUserdata)); + if( luaL_getmetatable( L, "clonable") == LUA_TNIL) // u mt? + { + lua_pop( L, 1); // u + lua_newtable( L); // u mt + luaL_setfuncs( L, clonable_mt, 0); + lua_pushvalue(L, -1); // u mt mt + lua_setfield(L, -2, "__index"); // u mt + } + lua_setmetatable( L, -2); // u + return 1; +} + +// ################################################################################################ // ################################################################################################ static luaL_Reg const deep_module[] = { { "new_deep", luaD_new_deep}, + { "new_clonable", luaD_new_clonable}, { NULL, NULL} }; diff --git a/deep_test/deep_test.lua b/deep_test/deep_test.lua index fd78115..3b514dd 100644 --- a/deep_test/deep_test.lua +++ b/deep_test/deep_test.lua @@ -4,12 +4,19 @@ local deep = dt.new_deep() deep:set(666) print( deep) +local clonable = dt.new_clonable() + -- now load Lanes and see if that userdata is transferable --[[ local lanes = require("lanes").configure() - local l = lanes.linda "my linda" -l.put( "key", deep) -local out = l.get( "key") -print( out) -]] \ No newline at end of file + +l:set( "key", deep) +local deep_out = l:get( "key") +print( deep_out) + +lanes.register() +l:set( "key", clonable) +local clonable_out = l:get( "key") +print( clonable_out) +--]] \ No newline at end of file diff --git a/src/tools.c b/src/tools.c index 9403fd6..13e714d 100644 --- a/src/tools.c +++ b/src/tools.c @@ -1480,6 +1480,52 @@ static void push_cached_func( struct s_Universe* U, lua_State* L2, uint_t L2_cac } } +static bool_t push_cached_metatable( struct s_Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, enum eLookupMode mode_, char const* upName_) +{ + if( lua_getmetatable( L, i)) // ... mt + { + uint_t const mt_id = get_mt_id( U, L, -1); // Unique id for the metatable + + STACK_CHECK( L2); + STACK_GROW( L2, 4); + // do we already know this metatable? + push_registry_subtable( L2, REG_MTID); // rst + lua_pushinteger( L2, mt_id); // rst id + lua_rawget( L2, -2); // rst mt? + + STACK_MID( L2, 2); + + if( lua_isnil( L2, -1)) + { // L2 did not know the metatable + lua_pop( L2, 1); // rst + if( inter_copy_one_( U, L2, L2_cache_i, L, lua_gettop( L), VT_METATABLE, mode_, upName_)) // rst mt + { + STACK_MID( L2, 2); + // mt_id -> metatable + lua_pushinteger( L2, mt_id); // rst mt id + lua_pushvalue( L2, -2); // rst mt id mt + lua_rawset( L2, -4); // rst mt + + // metatable -> mt_id + lua_pushvalue( L2, -1); // rst mt mt + lua_pushinteger( L2, mt_id); // rst mt mt id + lua_rawset( L2, -4); // rst mt + } + else + { + (void) luaL_error( L, "Error copying a metatable"); + } + STACK_MID( L2, 2); + } + lua_remove( L2, -2); // mt + + lua_pop( L, 1); // ... + STACK_END( L2, 1); + return TRUE; + } + return FALSE; +} + /* * Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove * the original value. @@ -1496,17 +1542,17 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca bool_t ignore = FALSE; int val_type = lua_type( L, i); STACK_GROW( L2, 1); - STACK_CHECK( L2); + STACK_CHECK( L2); // L // L2 /* Skip the object if it has metatable with { __lanesignore = true } */ - if( lua_getmetatable( L, i)) // ... mt + if( lua_getmetatable( L, i)) // ... mt { - lua_getfield( L, -1, "__lanesignore"); // ... mt ignore? + lua_getfield( L, -1, "__lanesignore"); // ... mt ignore? if( lua_isboolean( L, -1) && lua_toboolean( L, -1)) { val_type = LUA_TNIL; } - lua_pop( L, 2); // ... + lua_pop( L, 2); // ... } /* Lets push nil to L2 if the object should be ignored */ @@ -1563,27 +1609,61 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "USERDATA\n" INDENT_END)); if( !copydeep( U, L, L2, i, mode_)) { - // Not a deep full userdata - bool_t demote = FALSE; - lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); - if( lua_istable( L, -1)) // should not happen, but who knows... - { - lua_getfield( L, -1, "demote_full_userdata"); - demote = lua_toboolean( L, -1); - lua_pop( L, 2); - } - else - { - lua_pop( L, 1); - } - if( demote) // attempt demotion to light userdata + if( lua_getmetatable( L, i)) // ... mt? { - void* lud = lua_touserdata( L, i); - lua_pushlightuserdata( L2, lud); + lua_getfield( L, -1, "__lanesclone"); // ... mt clone? + if( !lua_isnil( L, -1)) + { + void* const source = lua_touserdata( L, i); + // copy the metatable in the target state + if( push_cached_metatable( U, L2, L2_cache_i, L, i, mode_, upName_)) // ... mt? + { + // retrieve cloning function + lua_getfield( L2, -1, "__lanesclone"); // ... mt clone + lua_pushlightuserdata( L2, source); // ... mt clone p + // cloning function should create a new full userdata without a metatable + if( lua_pcall( L2, 1, 1, 0) == LUA_OK) // ... mt u + { + lua_insert( L2, -2); // ... u mt + lua_setmetatable( L2, -2); // ... u + } + else // ... mt err + { + // propagate any error to the source state + char const* errmsg = lua_tostring( L2, -1); + (void) luaL_error( L, "can't copy non-deep full userdata across lanes: %s", errmsg); + } + } + else + { + (void) luaL_error( L, "Error copying a metatable"); + } + } + lua_pop( L, 2); // ... } - else // raise an error { - (void) luaL_error( L, "can't copy non-deep full userdata across lanes"); + // Not a deep or clonable full userdata + bool_t demote = FALSE; + lua_getfield( L, LUA_REGISTRYINDEX, CONFIG_REGKEY); + if( lua_istable( L, -1)) // should not happen, but who knows... + { + lua_getfield( L, -1, "demote_full_userdata"); + demote = lua_toboolean( L, -1); + lua_pop( L, 2); + } + else + { + lua_pop( L, 1); + } + if( demote) // attempt demotion to light userdata + { + void* lud = lua_touserdata( L, i); + lua_pushlightuserdata( L2, lud); + } + else // raise an error + { + (void) luaL_error( L, "can't copy non-deep full userdata across lanes"); + } } } break; @@ -1687,7 +1767,7 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca } else { - luaL_error( L, "Unable to copy over type '%s' (in %s)", luaL_typename( L, val_i), (vt == VT_NORMAL) ? "table" : "metatable"); + (void) luaL_error( L, "Unable to copy over type '%s' (in %s)", luaL_typename( L, val_i), (vt == VT_NORMAL) ? "table" : "metatable"); } } lua_pop( L, 1); // pop value (next round) @@ -1695,75 +1775,10 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca STACK_MID( L, 0); STACK_MID( L2, 1); - /* Metatables are expected to be immutable, and copied only once. - */ - if( lua_getmetatable( L, i)) + // Metatables are expected to be immutable, and copied only once. + if( push_cached_metatable( U, L2, L2_cache_i, L, i, mode_, upName_)) // ... t mt? { - // - // L [-1]: metatable - - uint_t mt_id = get_mt_id( U, L, -1); // Unique id for the metatable - - STACK_GROW( L2, 4); - - push_registry_subtable( L2, REG_MTID); - STACK_MID( L2, 2); - lua_pushinteger( L2, mt_id); - lua_rawget( L2, -2); - // - // L2 ([-3]: copied table) - // [-2]: reg[REG_MTID] - // [-1]: nil/metatable pre-known in L2 - - STACK_MID( L2, 3); - - if( lua_isnil( L2, -1)) - { /* L2 did not know the metatable */ - lua_pop( L2, 1); - STACK_MID( L2, 2); - ASSERT_L( lua_istable( L,-1)); - if( inter_copy_one_( U, L2, L2_cache_i /*for function cacheing*/, L, lua_gettop( L) /*[-1]*/, VT_METATABLE, mode_, upName_)) - { - // - // L2 ([-3]: copied table) - // [-2]: reg[REG_MTID] - // [-1]: metatable (copied from L) - - STACK_MID( L2, 3); - // mt_id -> metatable - // - lua_pushinteger( L2, mt_id); - lua_pushvalue( L2, -2); - lua_rawset( L2, -4); - - // metatable -> mt_id - // - lua_pushvalue( L2, -1); - lua_pushinteger( L2, mt_id); - lua_rawset( L2, -4); - - STACK_MID( L2, 3); - } - else - { - luaL_error( L, "Error copying a metatable"); - } - STACK_MID( L2, 3); - } - // L2 ([-3]: copied table) - // [-2]: reg[REG_MTID] - // [-1]: metatable (pre-known or copied from L) - - lua_remove( L2, -2); // take away 'reg[REG_MTID]' - // - // L2: ([-2]: copied table) - // [-1]: metatable for that table - - lua_setmetatable( L2, -2); - - // L2: [-1]: copied table (with metatable set if source had it) - - lua_pop( L, 1); // remove source metatable (L, not L2!) + lua_setmetatable( L2, -2); // ... t } STACK_END( L2, 1); STACK_END( L, 0); @@ -1773,7 +1788,7 @@ static bool_t inter_copy_one_( struct s_Universe* U, lua_State* L2, uint_t L2_ca /* The following types cannot be copied */ case 10: // LuaJIT CDATA - case LUA_TTHREAD: + case LUA_TTHREAD: ret = FALSE; break; } -- cgit v1.2.3-55-g6feb