From 2c0000d5169cacf950d06637ada1a371cf382896 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Tue, 8 Feb 2022 10:39:48 +0100 Subject: __lanesclone is now called only once with 3 parameters dest, source, size -> BREAKS CUSTOM DEEP USERDATA API --- CHANGES | 4 + deep_test/deep_test.c | 13 +-- deep_test/deep_test.vcxproj.user | 2 +- deep_test/deeptest.lua | 24 +++-- docs/index.html | 28 ++--- lanes-3.15.1-0.rockspec | 78 -------------- lanes-3.16.0-0.rockspec | 78 ++++++++++++++ src/lanes.h | 4 +- src/tools.c | 220 ++++++++++++++++++--------------------- 9 files changed, 218 insertions(+), 233 deletions(-) delete mode 100644 lanes-3.15.1-0.rockspec create mode 100644 lanes-3.16.0-0.rockspec diff --git a/CHANGES b/CHANGES index f356c26..490d651 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,9 @@ CHANGES: +CHANGE 152: BGe 7-Feb-22 + * bumped version to 3.16.0 + * __lanesclone is now called only once with 3 parameters dest, source, size -> BREAKS CUSTOM DEEP USERDATA API + CHANGE 151: BGe 7-Feb-22 * bumped version to 3.15.2 * Lanes no longer relies on malloc/free for internal allocations, but uses the primary alloc function from the master Lua state diff --git a/deep_test/deep_test.c b/deep_test/deep_test.c index a725902..cb89741 100644 --- a/deep_test/deep_test.c +++ b/deep_test/deep_test.c @@ -1,5 +1,6 @@ #include #include +#include #include "lua.h" #include "lualib.h" @@ -196,18 +197,12 @@ static int clonable_lanesclone( lua_State* L) { switch( lua_gettop( L)) { - case 1: - { - // in case we need it to compute the amount of memory we need - struct s_MyClonableUserdata* self = lua_touserdata( L, 1); - lua_pushinteger( L, sizeof( struct s_MyClonableUserdata)); - } - return 1; - - case 2: + case 3: { struct s_MyClonableUserdata* self = lua_touserdata( L, 1); struct s_MyClonableUserdata* from = lua_touserdata( L, 2); + size_t len = lua_tointeger( L, 3); + assert( len == sizeof(struct s_MyClonableUserdata)); *self = *from; } return 0; diff --git a/deep_test/deep_test.vcxproj.user b/deep_test/deep_test.vcxproj.user index c5d6237..6ffc0ba 100644 --- a/deep_test/deep_test.vcxproj.user +++ b/deep_test/deep_test.vcxproj.user @@ -13,7 +13,7 @@ D:\Boulot\anubis\Lua\bindings\Lanes\lanes\deep_test\ - D:\Boulot\anubis\Lua\framework\lua53.exe + D:\Boulot\anubis\Lua\framework\lua54.exe WindowsLocalDebugger -i deeptest.lua D:\Boulot\anubis\Lua\bindings\Lanes\lanes\deep_test\ diff --git a/deep_test/deeptest.lua b/deep_test/deeptest.lua index 3c89c3d..cbd08de 100644 --- a/deep_test/deeptest.lua +++ b/deep_test/deeptest.lua @@ -6,7 +6,8 @@ local dt = lanes.require "deep_test" local test_deep = true local test_clonable = true -local test_uvtype = "function" +local test_uvtype = "string" +local nupvals = _VERSION == "Lua 5.4" and 2 or 1 local makeUserValue = function( obj_) if test_uvtype == "string" then @@ -21,14 +22,17 @@ local makeUserValue = function( obj_) end local printDeep = function( prefix_, obj_, t_) - local uservalue = obj_:getuv( 1) - print( prefix_) - print ( obj_, uservalue, type( uservalue) == "function" and uservalue() or "") + print( prefix_, obj_) + for uvi = 1, nupvals do + local uservalue = obj_:getuv( 1) + print ( "uv #" .. uvi, uservalue, type( uservalue) == "function" and uservalue() or "") + end if t_ then for k, v in pairs( t_) do print( k, v) end end + print() end local performTest = function( obj_) @@ -38,12 +42,14 @@ local performTest = function( obj_) -- lua 5.3 supports an arbitrary type uservalue obj_:setuv( 1, makeUserValue( obj_)) -- lua 5.4 supports multiple uservalues of arbitrary types - -- obj_:setuv( 2, "ENDUV") + if nupvals > 1 then + obj_:setuv( 2, "ENDUV") + end local t = { ["key"] = obj_, - -- [obj_] = "val" + [obj_] = "val" -- this one won't transfer because we don't support full uservalue as keys } -- read back the contents of the object @@ -76,11 +82,13 @@ local performTest = function( obj_) end if test_deep then + print "================================================================" print "DEEP" - performTest( dt.new_deep()) + performTest( dt.new_deep(nupvals)) end if test_clonable then + print "================================================================" print "CLONABLE" - performTest( dt.new_clonable()) + performTest( dt.new_clonable(nupvals)) end diff --git a/docs/index.html b/docs/index.html index 290383e..0e1a30b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -70,7 +70,7 @@

- This document was revised on 8-Feb-22, and applies to version 3.15.2. + This document was revised on 8-Feb-22, and applies to version 3.16.0.

@@ -1550,27 +1550,20 @@ events to a common Linda, but... :).

Clonable full userdata in your own apps

Starting with version 3.13.0, a new way of passing full userdata across lanes uses a new __lanesclone metamethod. - When a deep userdata is cloned, Lanes calls __lanesclone twice, in the context of the source lane.
- The first call receives the original as light userdata, as in ud:__lanesclone(), and should return the amount of memory used to create the cloned full userdata.
- The second call receives the clone and original as light userdata, as in clone:__lanesclone(original), and should perform the actual cloning.
- A typical implementation would look like: + When a deep userdata is cloned, Lanes calls __lanesclone once, in the context of the source lane.
+ The call receives the clone and original as light userdata, plus the actual userdata size, as in clone:__lanesclone(original,size), and should perform the actual cloning.
+ A typical implementation would look like (BEWARE, THIS CHANGED WITH VERSION 3.16.0):
 static int clonable_lanesclone( lua_State* L)
 {
 	switch( lua_gettop( L))
 	{
-		case 1: // original:__lanesclone()
-		{
-			// the original (as light userdata), in case you need it to compute the size of the clone
-			struct s_MyClonableUserdata* self = lua_touserdata( L, 1);
-			lua_pushinteger( L, sizeof( struct s_MyClonableUserdata));
-		}
-		return 1;
-
-		case 2: // clone:__lanesclone(original)
+		case 3:
 		{
 			struct s_MyClonableUserdata* self = lua_touserdata( L, 1);
 			struct s_MyClonableUserdata* from = lua_touserdata( L, 2);
+			size_t len = lua_tointeger( L, 3);
+			assert( len == sizeof(struct s_MyClonableUserdata));
 			*self = *from;
 		}
 		return 0;
@@ -1736,9 +1729,7 @@ int luaD_new_clonable( lua_State* L)
 

Change log

- v3.14.0: lane:cancel() rework: opt.cancelstep is gone, hook is installed by lane:cancel() if requested. - - For older stuff see CHANGES. + See CHANGES.

@@ -1754,5 +1745,4 @@ int luaD_new_clonable( lua_State* L)

- -
\ No newline at end of file + \ No newline at end of file diff --git a/lanes-3.15.1-0.rockspec b/lanes-3.15.1-0.rockspec deleted file mode 100644 index 5481269..0000000 --- a/lanes-3.15.1-0.rockspec +++ /dev/null @@ -1,78 +0,0 @@ --- --- Lanes rockspec --- --- Ref: --- --- - -package = "Lanes" - -version = "3.15.1-0" - -source= { - url= "https://github.com/LuaLanes/lanes.git", - branch= "v3.15.1" -} - -description = { - summary= "Multithreading support for Lua", - detailed= [[ - Lua Lanes is a portable, message passing multithreading library - providing the possibility to run multiple Lua states in parallel. - ]], - license= "MIT/X11", - homepage="https://github.com/LuaLanes/lanes", - maintainer="Benoit Germain " -} - --- Q: What is the difference of "windows" and "win32"? Seems there is none; --- so should we list either one or both? --- -supported_platforms= { "win32", - "macosx", - "linux", - "freebsd", -- TBD: not tested - "msys", -- TBD: not supported by LuaRocks 1.0 (or is it?) -} - -dependencies= { - "lua >= 5.1", -- builds with either 5.1, 5.2, 5.3 and 5.4 -} - -build = { - type = "builtin", - platforms = - { - linux = - { - modules = - { - ["lanes.core"] = - { - libraries = "pthread" - }, - } - } - }, - modules = - { - ["lanes.core"] = - { - sources = - { - "src/cancel.c", - "src/compat.c", - "src/deep.c", - "src/keeper.c", - "src/lanes.c", - "src/linda.c", - "src/tools.c", - "src/state.c", - "src/threading.c", - "src/universe.c" - }, - incdirs = { "src"}, - }, - lanes = "src/lanes.lua" - } -} diff --git a/lanes-3.16.0-0.rockspec b/lanes-3.16.0-0.rockspec new file mode 100644 index 0000000..170d9eb --- /dev/null +++ b/lanes-3.16.0-0.rockspec @@ -0,0 +1,78 @@ +-- +-- Lanes rockspec +-- +-- Ref: +-- +-- + +package = "Lanes" + +version = "3.16.0-0" + +source= { + url= "https://github.com/LuaLanes/lanes.git", + branch= "v3.16.0" +} + +description = { + summary= "Multithreading support for Lua", + detailed= [[ + Lua Lanes is a portable, message passing multithreading library + providing the possibility to run multiple Lua states in parallel. + ]], + license= "MIT/X11", + homepage="https://github.com/LuaLanes/lanes", + maintainer="Benoit Germain " +} + +-- Q: What is the difference of "windows" and "win32"? Seems there is none; +-- so should we list either one or both? +-- +supported_platforms= { "win32", + "macosx", + "linux", + "freebsd", -- TBD: not tested + "msys", -- TBD: not supported by LuaRocks 1.0 (or is it?) +} + +dependencies= { + "lua >= 5.1", -- builds with either 5.1, 5.2, 5.3 and 5.4 +} + +build = { + type = "builtin", + platforms = + { + linux = + { + modules = + { + ["lanes.core"] = + { + libraries = "pthread" + }, + } + } + }, + modules = + { + ["lanes.core"] = + { + sources = + { + "src/cancel.c", + "src/compat.c", + "src/deep.c", + "src/keeper.c", + "src/lanes.c", + "src/linda.c", + "src/tools.c", + "src/state.c", + "src/threading.c", + "src/universe.c" + }, + incdirs = { "src"}, + }, + lanes = "src/lanes.lua" + } +} diff --git a/src/lanes.h b/src/lanes.h index 4c7cc5b..20524e6 100644 --- a/src/lanes.h +++ b/src/lanes.h @@ -11,8 +11,8 @@ #endif // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) #define LANES_VERSION_MAJOR 3 -#define LANES_VERSION_MINOR 15 -#define LANES_VERSION_PATCH 2 +#define LANES_VERSION_MINOR 16 +#define LANES_VERSION_PATCH 0 #define LANES_MIN_VERSION_REQUIRED(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR>MAJOR) || (LANES_VERSION_MAJOR==MAJOR && (LANES_VERSION_MINOR>MINOR || (LANES_VERSION_MINOR==MINOR && LANES_VERSION_PATCH>=PATCH)))) #define LANES_VERSION_LESS_THAN(MAJOR, MINOR, PATCH) ((LANES_VERSION_MAJOR not clonable - if( !lua_getmetatable( L, i)) // ... mt? + if( !lua_getmetatable( L, source_i_)) // ... mt? { STACK_MID( L, 0); return FALSE; } // no __lanesclone? -> not clonable - lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? + lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? if( lua_isnil( L, -1)) { - lua_pop( L, 2); // ... + lua_pop( L, 2); // ... STACK_MID( L, 0); return FALSE; } + // we need to copy over the uservalues of the userdata as well { - int const mt = lua_absindex( L, -2); - size_t userdata_size = 0; + int const mt = lua_absindex( L, -2); // ... mt __lanesclone + size_t const userdata_size = (size_t) lua_rawlen( L, source_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 - lua_pushlightuserdata( L, source); // ... mt __lanesclone __lanesclone source - lua_call( L, 1, 1); // ... mt __lanesclone size - STACK_MID( L, 3); - 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 + // extract all the uservalues, but don't transfer them yet + int uvi = 0; + while( lua_getiuservalue( L, source_i_, ++ uvi) != LUA_TNONE) {} // ... mt __lanesclone [uv]+ nil + // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now + lua_pop( L, 1); // ... mt __lanesclone [uv]+ + -- uvi; + // create the clone userdata with the required number of uservalue slots + 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 { - // 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]+ nil + if( eLM_ToKeeper == mode_) // ... u sentinel { - ++ uvi; + 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 } - // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now - 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); // ... 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 + else // from keeper or direct // ... u mt { - 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, -2, uvi); // ... u - -- uvi; - } - // 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 - { - lua_pop( L2, 1); // ... userdata_clone_sentinel + ASSERT_L( lua_istable( L2, -1)); + lua_setmetatable( L2, -2); // ... u } STACK_MID( L2, 1); - 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); } + 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, -2, uvi); // ... u + -- uvi; + } + // 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 + { + lua_pop( L2, 1); // ... userdata_clone_sentinel + } + STACK_MID( L2, 1); + 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_pushinteger( L, userdata_size); // ... mt __lanesclone clone source size + lua_call( L, 3, 0); // ... mt + STACK_MID( L, 1); } STACK_END( L2, 1); - lua_pop( L, 1); // ... + lua_pop( L, 1); // ... STACK_END( L, 0); return TRUE; } @@ -1649,18 +1640,18 @@ static bool_t inter_copy_userdata( Universe* U, lua_State* L2, uint_t L2_cache_i return TRUE; } -static bool_t inter_copy_function( 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_) +static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t source_i_, enum e_vt vt, LookupMode mode_, char const* upName_) { if( vt == VT_KEY) { return FALSE; } - STACK_CHECK( L, 0); + STACK_CHECK( L, 0); // L (source) // L2 (destination) 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 from a keeper + if( lua_tocfunction( L, source_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; @@ -1668,10 +1659,10 @@ static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i void* clone; // let's see if we already restored this userdata - lua_getupvalue( L, i, 2); // ... u + lua_getupvalue( L, source_i_, 2); // ... u source = lua_touserdata( L, -1); - lua_pushlightuserdata( L2, source); // ... source - lua_rawget( L2, L2_cache_i); // ... u? + lua_pushlightuserdata( L2, source); // ... source + lua_rawget( L2, L2_cache_i); // ... u? if( !lua_isnil( L2, -1)) { lua_pop( L, 1); // ... @@ -1679,66 +1670,63 @@ static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i STACK_MID( L2, 1); return TRUE; } - lua_pop( L2, 1); // ... + 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); + lookup_table( L2, L, source_i_, mode_, upName_); // ... mt + // originally 'i' slot was the proxy closure, but from now on it indexes the actual userdata we extracted from it + source_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 + userdata_size = (size_t) lua_rawlen( L, -1); { // extract uservalues (don't transfer them yet) int uvi = 0; - while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... u uv - { - ++ uvi; - } + while( lua_getiuservalue( L, source_i_, ++ uvi) != LUA_TNONE) {} // ... u uv // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now lua_pop( L, 1); // ... u [uv]* + -- uvi; 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 u + clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt 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 + lua_pushlightuserdata( L2, source); // ... mt u source + lua_pushvalue( L2, -2); // ... mt u source u + lua_rawset( L2, L2_cache_i); // ... mt u // set metatable - lua_pushvalue( L2, -3); // ... mt __lanesclone u mt - lua_setmetatable( L2, -2); // ... mt __lanesclone u + lua_pushvalue( L2, -2); // ... mt u mt + lua_setmetatable( L2, -2); // ... mt 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]* + inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_); // ... mt u uv + lua_pop( L, 1); // ... u [uv]* // this pops the value from the stack - lua_setiuservalue( L2, -2, uvi); // ... mt __lanesclone u + lua_setiuservalue( L2, -2, uvi); // ... mt u -- uvi; } - // when we are done, all uservalues are popped from the stack + // when we are done, all uservalues are popped from the stack, we can pop the source as well lua_pop( L, 1); // ... STACK_MID( L, 0); - STACK_MID( L2, 3); // ... mt __lanesclone u + STACK_MID( L2, 2); // ... mt 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 + lua_insert( L2, -2); // ... u mt + // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with + lua_getfield(L2, -2, "__lanesclone"); // ... u mt __lanesclone + lua_remove( L2, -2); // ... u __lanesclone + lua_pushlightuserdata( L2, clone); // ... u __lanesclone clone + lua_pushlightuserdata( L2, source); // ... u __lanesclone clone source + lua_pushinteger( L2, userdata_size); // ... u __lanesclone clone source size + // clone:__lanesclone(source, size) + lua_call( L2, 3, 0); // ... u } - else + else // regular function { DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_)); DEBUGSPEW_CODE( ++ U->debugspew_indent_depth); STACK_CHECK( L2, 0); - copy_cached_func( U, L2, L2_cache_i, L, i, mode_, upName_); + copy_cached_func( U, L2, L2_cache_i, L, source_i_, mode_, upName_); // ... f STACK_END( L2, 1); DEBUGSPEW_CODE( -- U->debugspew_indent_depth); } -- cgit v1.2.3-55-g6feb