aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <bnt.germain@gmail.com>2021-06-26 18:25:53 +0200
committerBenoit Germain <bnt.germain@gmail.com>2021-06-26 18:25:53 +0200
commit909470be9f7ec1dd2d09ae1a371d69c9c652e957 (patch)
treecd16d9976e90c1d8a61d8ee3423d4af1671bafc0
parent050e14dd7fa04e2262ae6b1cc984d76c4149b664 (diff)
downloadlanes-909470be9f7ec1dd2d09ae1a371d69c9c652e957.tar.gz
lanes-909470be9f7ec1dd2d09ae1a371d69c9c652e957.tar.bz2
lanes-909470be9f7ec1dd2d09ae1a371d69c9c652e957.zip
fix stack overflow when transfering a clonable userdata referencing itself through a uservalue
-rw-r--r--deep_test/deep_test.c2
-rw-r--r--deep_test/deep_test.vcxproj5
-rw-r--r--deep_test/deep_test.vcxproj.user6
-rw-r--r--deep_test/deeptest.lua47
-rw-r--r--src/deep.c70
-rw-r--r--src/tools.c254
-rw-r--r--src/tools.h13
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 @@
16 16
17// ################################################################################################ 17// ################################################################################################
18 18
19// a lanes-deep userdata. needs DeepPrelude and luaG_newdeepuserdata from Lanes code.
19struct s_MyDeepUserdata 20struct s_MyDeepUserdata
20{ 21{
21 DeepPrelude prelude; // Deep userdata MUST start with this header 22 DeepPrelude prelude; // Deep userdata MUST start with this header
@@ -190,6 +191,7 @@ static int clonable_gc( lua_State* L)
190 191
191// ################################################################################################ 192// ################################################################################################
192 193
194// this is all we need to make a userdata lanes-clonable. no dependency on Lanes code.
193static int clonable_lanesclone( lua_State* L) 195static int clonable_lanesclone( lua_State* L)
194{ 196{
195 switch( lua_gettop( L)) 197 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 @@
115 <OutDir>$(SolutionDir)_Output\$(ProjectName)\$(PlatformName)\$(Configuration)\</OutDir> 115 <OutDir>$(SolutionDir)_Output\$(ProjectName)\$(PlatformName)\$(Configuration)\</OutDir>
116 <IntDir>$(SolutionDir)_Tmp\$(ProjectName)\$(PlatformName)\$(Configuration)\</IntDir> 116 <IntDir>$(SolutionDir)_Tmp\$(ProjectName)\$(PlatformName)\$(Configuration)\</IntDir>
117 </PropertyGroup> 117 </PropertyGroup>
118 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release 5.3|x64'">
119 <OutDir>$(SolutionDir)_Output\$(ProjectName)\$(PlatformName)\$(Configuration)\</OutDir>
120 <IntDir>$(SolutionDir)_Tmp\$(ProjectName)\$(PlatformName)\$(Configuration)\</IntDir>
121 </PropertyGroup>
118 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release 5.3|x64'"> 122 <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release 5.3|x64'">
119 <ClCompile> 123 <ClCompile>
120 <WarningLevel>Level3</WarningLevel> 124 <WarningLevel>Level3</WarningLevel>
@@ -125,6 +129,7 @@
125 <ConformanceMode>true</ConformanceMode> 129 <ConformanceMode>true</ConformanceMode>
126 <AdditionalIncludeDirectories>$(SolutionDir)Lanes\lanes\src;$(SolutionDir)..\lualib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> 130 <AdditionalIncludeDirectories>$(SolutionDir)Lanes\lanes\src;$(SolutionDir)..\lualib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
127 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName> 131 <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
132 <PreprocessorDefinitions>_WINDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
128 </ClCompile> 133 </ClCompile>
129 <Link> 134 <Link>
130 <EnableCOMDATFolding>true</EnableCOMDATFolding> 135 <EnableCOMDATFolding>true</EnableCOMDATFolding>
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 @@
12 <LocalDebuggerCommandArguments>-i deeptest.lua</LocalDebuggerCommandArguments> 12 <LocalDebuggerCommandArguments>-i deeptest.lua</LocalDebuggerCommandArguments>
13 <LocalDebuggerWorkingDirectory>D:\Boulot\anubis\Lua\bindings\Lanes\lanes\deep_test\</LocalDebuggerWorkingDirectory> 13 <LocalDebuggerWorkingDirectory>D:\Boulot\anubis\Lua\bindings\Lanes\lanes\deep_test\</LocalDebuggerWorkingDirectory>
14 </PropertyGroup> 14 </PropertyGroup>
15 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release 5.3|x64'">
16 <LocalDebuggerCommand>D:\Boulot\anubis\Lua\framework\lua53.exe</LocalDebuggerCommand>
17 <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
18 <LocalDebuggerCommandArguments>-i -- deeptest.lua</LocalDebuggerCommandArguments>
19 <LocalDebuggerWorkingDirectory>D:\Boulot\anubis\Lua\bindings\Lanes\lanes\deep_test\</LocalDebuggerWorkingDirectory>
20 </PropertyGroup>
15</Project> \ No newline at end of file 21</Project> \ 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"
5local dt = lanes.require "deep_test" 5local dt = lanes.require "deep_test"
6 6
7local test_deep = true 7local test_deep = true
8local test_clonable = false 8local test_clonable = true
9local test_uvtype = "string" 9local test_uvtype = "function"
10 10
11local makeUserValue = function( obj_) 11local makeUserValue = function( obj_)
12 if test_uvtype == "string" then 12 if test_uvtype == "string" then
@@ -14,12 +14,23 @@ local makeUserValue = function( obj_)
14 elseif test_uvtype == "function" then 14 elseif test_uvtype == "function" then
15 -- a function that pull the userdata as upvalue 15 -- a function that pull the userdata as upvalue
16 local f = function() 16 local f = function()
17 print( obj_) 17 return tostring( obj_)
18 end 18 end
19 return f 19 return f
20 end 20 end
21end 21end
22 22
23local printDeep = function( prefix_, obj_, t_)
24 local uservalue = obj_:getuv( 1)
25 print( prefix_)
26 print ( obj_, uservalue, type( uservalue) == "function" and uservalue() or "")
27 if t_ then
28 for k, v in pairs( t_) do
29 print( k, v)
30 end
31 end
32end
33
23local performTest = function( obj_) 34local performTest = function( obj_)
24 -- setup the userdata with some value and a uservalue 35 -- setup the userdata with some value and a uservalue
25 obj_:set( 666) 36 obj_:set( 666)
@@ -29,15 +40,20 @@ local performTest = function( obj_)
29 -- lua 5.4 supports multiple uservalues of arbitrary types 40 -- lua 5.4 supports multiple uservalues of arbitrary types
30 -- obj_:setuv( 2, "ENDUV") 41 -- obj_:setuv( 2, "ENDUV")
31 42
43 local t =
44 {
45 ["key"] = obj_,
46 -- [obj_] = "val"
47 }
48
32 -- read back the contents of the object 49 -- read back the contents of the object
33 print( "immediate:", obj_, obj_:getuv( 1)) 50 printDeep( "immediate:", obj_, t)
34 51
35 -- send the object in a linda, get it back out, read the contents 52 -- send the object in a linda, get it back out, read the contents
36 l:set( "key", obj_) 53 l:set( "key", obj_, t)
37 -- 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) 54 -- 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)
38 -- when obj_ is a clonable userdata, we get a different clone everytime we cross a linda or lane barrier 55 -- when obj_ is a clonable userdata, we get a different clone everytime we cross a linda or lane barrier
39 local out = l:get( "key") 56 printDeep( "out of linda:", l:get( "key", 2))
40 print( "out of linda:", out, out:getuv( 1))
41 57
42 -- send the object in a lane through parameter passing, the lane body returns it as return value, read the contents 58 -- send the object in a lane through parameter passing, the lane body returns it as return value, read the contents
43 local g = lanes.gen( 59 local g = lanes.gen(
@@ -45,23 +61,26 @@ local performTest = function( obj_)
45 , { 61 , {
46 required = { "deep_test"} -- we will transfer userdata created by this module, so we need to make this lane aware of it 62 required = { "deep_test"} -- we will transfer userdata created by this module, so we need to make this lane aware of it
47 } 63 }
48 , function( param_) 64 , function( arg_, t_)
49 -- read contents inside lane 65 -- read contents inside lane: arg_ and t_ by argument
50 print( "in lane:", param_, param_:getuv( 1)) 66 printDeep( "in lane, as arguments:", arg_, t_)
51 return param_ 67 -- read contents inside lane: obj_ and t by upvalue
68 printDeep( "in lane, as upvalues:", obj_, t)
69 return arg_, t_
52 end 70 end
53 ) 71 )
54 h = g( obj_) 72 h = g( obj_, t)
55 -- 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) 73 -- 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)
56 -- when obj_ is a clonable userdata, we get a different clone everytime we cross a linda or lane barrier 74 -- when obj_ is a clonable userdata, we get a different clone everytime we cross a linda or lane barrier
57 local from_lane = h[1] 75 printDeep( "from lane:", h[1], h[2])
58 print( "from lane:", from_lane, from_lane:getuv( 1))
59end 76end
60 77
61if test_deep then 78if test_deep then
79 print "DEEP"
62 performTest( dt.new_deep()) 80 performTest( dt.new_deep())
63end 81end
64 82
65if test_clonable then 83if test_clonable then
84 print "CLONABLE"
66 performTest( dt.new_clonable()) 85 performTest( dt.new_clonable())
67end 86end
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.
49 49
50/*-- Metatable copying --*/ 50/*-- Metatable copying --*/
51 51
52/*
53 * 'reg[ REG_MT_KNOWN ]'= {
54 * [ table ]= id_uint,
55 * ...
56 * [ id_uint ]= table,
57 * ...
58 * }
59 */
60
61/*
62* Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it
63*/
64static void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_)
65{
66 STACK_GROW( L, 3);
67 STACK_CHECK( L, 0);
68
69 REGISTRY_GET( L, key_); // {}|nil
70 STACK_MID( L, 1);
71
72 if( lua_isnil( L, -1))
73 {
74 lua_pop( L, 1); //
75 lua_newtable( L); // {}
76 // _R[key_] = {}
77 REGISTRY_SET( L, key_, lua_pushvalue( L, -2)); // {}
78 STACK_MID( L, 1);
79
80 // Set its metatable if requested
81 if( mode_)
82 {
83 lua_newtable( L); // {} mt
84 lua_pushliteral( L, "__mode"); // {} mt "__mode"
85 lua_pushstring( L, mode_); // {} mt "__mode" mode
86 lua_rawset( L, -3); // {} mt
87 lua_setmetatable( L, -2); // {}
88 }
89 }
90 STACK_END( L, 1);
91 ASSERT_L( lua_istable( L, -1));
92}
93
94
95/*
96* Push a registry subtable (keyed by unique 'key_') onto the stack.
97* If the subtable does not exist, it is created and chained.
98*/
99void push_registry_subtable( lua_State* L, UniqueKey key_)
100{
101 push_registry_subtable_mode( L, key_, NULL);
102}
103
104
105/*---=== Deep userdata ===---*/ 52/*---=== Deep userdata ===---*/
106 53
107/* 54/*
@@ -503,10 +450,10 @@ void* luaG_todeep( lua_State* L, luaG_IdFunction idfunc, int index)
503 * the id function of the copied value, or NULL for non-deep userdata 450 * the id function of the copied value, or NULL for non-deep userdata
504 * (not copied) 451 * (not copied)
505 */ 452 */
506bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode mode_) 453bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
507{ 454{
508 char const* errmsg; 455 char const* errmsg;
509 luaG_IdFunction idfunc = get_idfunc( L, index, mode_); 456 luaG_IdFunction idfunc = get_idfunc( L, i, mode_);
510 int nuv = 0; 457 int nuv = 0;
511 458
512 if( idfunc == NULL) 459 if( idfunc == NULL)
@@ -518,24 +465,25 @@ bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode
518 STACK_CHECK( L2, 0); 465 STACK_CHECK( L2, 0);
519 466
520 // extract all uservalues of the source 467 // extract all uservalues of the source
521 while( lua_getiuservalue( L, index, nuv + 1) != LUA_TNONE) // ... u [uv]+ nil 468 while( lua_getiuservalue( L, i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil
522 { 469 {
523 ++ nuv; 470 ++ nuv;
524 } 471 }
525 // last call returned TNONE and pushed nil, that we don't need 472 // last call returned TNONE and pushed nil, that we don't need
526 lua_pop( L, 1); // ... u [uv]+ 473 lua_pop( L, 1); // ... u [uv]*
527 STACK_MID( L, nuv); 474 STACK_MID( L, nuv);
528 475
529 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, index), nuv, mode_); // u 476 errmsg = push_deep_proxy( U, L2, *(DeepPrelude**) lua_touserdata( L, i), nuv, mode_); // u
530 477
531 // transfer all uservalues of the source in the destination 478 // transfer all uservalues of the source in the destination
532 { 479 {
533 int const clone_i = lua_gettop( L2); 480 int const clone_i = lua_gettop( L2);
534 luaG_inter_move( U, L, L2, nuv, mode_); // ... u // u [uv]+ 481 while( nuv)
535 while( nuv > 0)
536 { 482 {
483 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // u uv
484 lua_pop( L, 1); // ... u [uv]*
537 // this pops the value from the stack 485 // this pops the value from the stack
538 lua_setiuservalue( L2, clone_i, nuv); // ... u // u 486 lua_setiuservalue( L2, clone_i, nuv); // u
539 -- nuv; 487 -- nuv;
540 } 488 }
541 } 489 }
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.
48#include "uniquekey.h" 48#include "uniquekey.h"
49 49
50// functions implemented in deep.c 50// functions implemented in deep.c
51extern bool_t copydeep( Universe* U, lua_State* L, lua_State* L2, int index, LookupMode mode_); 51extern bool_t copydeep( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_);
52extern void push_registry_subtable( lua_State* L, UniqueKey key_); 52extern void push_registry_subtable( lua_State* L, UniqueKey key_);
53 53
54DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); 54DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+");
55 55
56 56
57// ################################################################################################
58
59/*
60 * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it
61 */
62void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_)
63{
64 STACK_GROW( L, 3);
65 STACK_CHECK( L, 0);
66
67 REGISTRY_GET( L, key_); // {}|nil
68 STACK_MID( L, 1);
69
70 if( lua_isnil( L, -1))
71 {
72 lua_pop( L, 1); //
73 lua_newtable( L); // {}
74 // _R[key_] = {}
75 REGISTRY_SET( L, key_, lua_pushvalue( L, -2)); // {}
76 STACK_MID( L, 1);
77
78 // Set its metatable if requested
79 if( mode_)
80 {
81 lua_newtable( L); // {} mt
82 lua_pushliteral( L, "__mode"); // {} mt "__mode"
83 lua_pushstring( L, mode_); // {} mt "__mode" mode
84 lua_rawset( L, -3); // {} mt
85 lua_setmetatable( L, -2); // {}
86 }
87 }
88 STACK_END( L, 1);
89 ASSERT_L( lua_istable( L, -1));
90}
91
92// ################################################################################################
93
94/*
95 * Push a registry subtable (keyed by unique 'key_') onto the stack.
96 * If the subtable does not exist, it is created and chained.
97 */
98void push_registry_subtable( lua_State* L, UniqueKey key_)
99{
100 push_registry_subtable_mode( L, key_, NULL);
101}
102
103// ################################################################################################
104
57/*---=== luaG_dump ===---*/ 105/*---=== luaG_dump ===---*/
58#ifdef _DEBUG 106#ifdef _DEBUG
59void luaG_dump( lua_State* L) 107void 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
695 return FALSE; 743 return FALSE;
696 } 744 }
697 // push the equivalent table in the destination's stack, retrieved from the lookup table 745 // push the equivalent table in the destination's stack, retrieved from the lookup table
698 STACK_CHECK( L2, 0); // L // L2 746 STACK_CHECK( L2, 0); // L // L2
699 STACK_GROW( L2, 3); // up to 3 slots are necessary on error 747 STACK_GROW( L2, 3); // up to 3 slots are necessary on error
700 switch( mode_) 748 switch( mode_)
701 { 749 {
@@ -1066,13 +1114,6 @@ static void lookup_native_func( lua_State* L2, lua_State* L, uint_t i, LookupMod
1066 * Copy a function over, which has not been found in the cache. 1114 * Copy a function over, which has not been found in the cache.
1067 * L2 has the cache key for this function at the top of the stack 1115 * L2 has the cache key for this function at the top of the stack
1068*/ 1116*/
1069enum e_vt
1070{
1071 VT_NORMAL,
1072 VT_KEY,
1073 VT_METATABLE
1074};
1075static 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_);
1076 1117
1077#if USE_DEBUG_SPEW 1118#if USE_DEBUG_SPEW
1078static char const* lua_type_names[] = 1119static char const* lua_type_names[] =
@@ -1412,17 +1453,41 @@ static void inter_copy_keyvaluepair( Universe* U, lua_State* L2, uint_t L2_cache
1412 } 1453 }
1413} 1454}
1414 1455
1456/*
1457* The clone cache is a weak valued table listing all clones, indexed by their userdatapointer
1458* fnv164 of string "CLONABLES_CACHE_KEY" generated at https://www.pelock.com/products/hash-calculator
1459*/
1460static DECLARE_CONST_UNIQUE_KEY( CLONABLES_CACHE_KEY, 0xD04EE018B3DEE8F5);
1461
1415static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_) 1462static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_State* L, uint_t i, LookupMode mode_, char const* upName_)
1416{ 1463{
1464 void* const source = lua_touserdata( L, i);
1465
1417 STACK_CHECK( L, 0); 1466 STACK_CHECK( L, 0);
1418 STACK_CHECK( L2, 0); 1467 STACK_CHECK( L2, 0);
1419 1468
1469 // Check if the source was already cloned during this copy
1470 lua_pushlightuserdata( L2, source); // ... source
1471 lua_rawget( L2, L2_cache_i); // ... clone?
1472 if ( !lua_isnil( L2, -1))
1473 {
1474 STACK_MID( L2, 1);
1475 return TRUE;
1476 }
1477 else
1478 {
1479 lua_pop( L2, 1); // ...
1480 }
1481 STACK_MID( L2, 0);
1482
1483 // no metatable? -> not clonable
1420 if( !lua_getmetatable( L, i)) // ... mt? 1484 if( !lua_getmetatable( L, i)) // ... mt?
1421 { 1485 {
1422 STACK_MID( L, 0); 1486 STACK_MID( L, 0);
1423 return FALSE; 1487 return FALSE;
1424 } 1488 }
1425 1489
1490 // no __lanesclone? -> not clonable
1426 lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? 1491 lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone?
1427 if( lua_isnil( L, -1)) 1492 if( lua_isnil( L, -1))
1428 { 1493 {
@@ -1432,8 +1497,8 @@ static bool_t copyclone( Universe* U, lua_State* L2, uint_t L2_cache_i, lua_Stat
1432 } 1497 }
1433 1498
1434 { 1499 {
1500 int const mt = lua_absindex( L, -2);
1435 size_t userdata_size = 0; 1501 size_t userdata_size = 0;
1436 void* const source = lua_touserdata( L, i);
1437 void* clone = NULL; 1502 void* clone = NULL;
1438 lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone 1503 lua_pushvalue( L, -1); // ... mt __lanesclone __lanesclone
1439 // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone 1504 // 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
1443 userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size 1508 userdata_size = (size_t) lua_tointeger( L, -1); // ... mt __lanesclone size
1444 lua_pop( L, 1); // ... mt __lanesclone 1509 lua_pop( L, 1); // ... mt __lanesclone
1445 // we need to copy over the uservalues of the userdata as well 1510 // we need to copy over the uservalues of the userdata as well
1446 lua_pushnil( L2); // ... nil
1447 { 1511 {
1448 int const clone_i = lua_gettop( L2); 1512 // extract all the uservalues, but don't transfer them yet
1449 int uvi = 0; 1513 int uvi = 0;
1450 while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... mt __lanesclone uv 1514 while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... mt __lanesclone [uv]+ nil
1451 { 1515 {
1452 luaG_inter_move( U, L, L2, 1, mode_); // ... mt __lanesclone // ... nil [uv]+
1453 ++ uvi; 1516 ++ uvi;
1454 } 1517 }
1455 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now 1518 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
1456 lua_pop( L, 1); // ... mt __lanesclone 1519 lua_pop( L, 1); // ... mt __lanesclone [uv]+
1457 // create the clone userdata with the required number of uservalue slots 1520 // create the clone userdata with the required number of uservalue slots
1458 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... nil [uv]+ u 1521 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... u
1459 lua_replace( L2, clone_i); // ... u [uv]+ 1522 // copy the metatable in the target state, and give it to the clone we put there
1523 if( inter_copy_one( U, L2, L2_cache_i, L, mt, VT_NORMAL, mode_, upName_)) // ... u mt|sentinel
1524 {
1525 if( eLM_ToKeeper == mode_) // ... u sentinel
1526 {
1527 ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel);
1528 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
1529 lua_getupvalue( L2, -1, 1); // ... u sentinel fqn
1530 lua_remove( L2, -2); // ... u fqn
1531 lua_insert( L2, -2); // ... fqn u
1532 lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel
1533 }
1534 else // from keeper or direct // ... u mt
1535 {
1536 ASSERT_L( lua_istable( L2, -1));
1537 lua_setmetatable( L2, -2); // ... u
1538 }
1539 STACK_MID( L2, 1);
1540 }
1541 else
1542 {
1543 (void) luaL_error( L, "Error copying a metatable");
1544 }
1545 // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel
1546 lua_pushlightuserdata( L2, source); // ... u source
1547 lua_pushvalue( L2, -2); // ... u source u
1548 lua_rawset( L2, L2_cache_i); // ... u
1549 // make sure we have the userdata now
1550 if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel
1551 {
1552 lua_getupvalue( L2, -1, 2); // ... userdata_clone_sentinel u
1553 }
1460 // assign uservalues 1554 // assign uservalues
1461 while( uvi > 0) 1555 while( uvi > 0)
1462 { 1556 {
1557 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_); // ... u uv
1558 lua_pop( L, 1); // ... mt __lanesclone [uv]*
1463 // this pops the value from the stack 1559 // this pops the value from the stack
1464 lua_setiuservalue( L2, clone_i, uvi); // ... u [uv]+ 1560 lua_setiuservalue( L2, -2, uvi); // ... u
1465 -- uvi; 1561 -- uvi;
1466 } 1562 }
1467 // when we are done, all uservalues are popped from the stack 1563 // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination
1468 STACK_MID( L2, 1); // ... u 1564 if( eLM_ToKeeper == mode_) // ... userdata_clone_sentinel u
1469 }
1470 STACK_MID( L, 2); // ... mt __lanesclone
1471 // call cloning function in source state to perform the actual memory cloning
1472 lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone
1473 lua_pushlightuserdata( L, source); // ... mt __lanesclone source
1474 lua_call( L, 2, 0); // ... mt
1475 STACK_MID( L, 1);
1476 // copy the metatable in the target state
1477 if( inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), VT_NORMAL, mode_, upName_)) // ... u mt?
1478 {
1479 lua_pop( L, 1); // ...
1480 STACK_MID( L, 0);
1481 // when writing to a keeper state, we have here a sentinel function with the metatable's fqn as upvalue
1482 if( eLM_ToKeeper == mode_) // ... u sentinel
1483 { 1565 {
1484 ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel); 1566 lua_pop( L2, 1); // ... userdata_clone_sentinel
1485 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
1486 lua_getupvalue( L2, -1, 1); // ... u sentinel fqn
1487 lua_remove( L2, -2); // ... u fqn
1488 lua_insert( L2, -2); // ... fqn u
1489 lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel
1490 }
1491 else // from keeper or direct, we have the userdata and the metatable
1492 {
1493 ASSERT_L( lua_istable( L2, -1));
1494 lua_setmetatable( L2, -2); // ... u
1495 } 1567 }
1496 STACK_MID( L2, 1); 1568 STACK_MID( L2, 1);
1497 STACK_MID( L, 0); 1569 STACK_MID( L, 2);
1498 return TRUE; 1570 // call cloning function in source state to perform the actual memory cloning
1499 } 1571 lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone
1500 else 1572 lua_pushlightuserdata( L, source); // ... mt __lanesclone clone source
1501 { 1573 lua_call( L, 2, 0); // ... mt
1502 (void) luaL_error( L, "Error copying a metatable"); 1574 STACK_MID( L, 1);
1503 } 1575 }
1504 } 1576 }
1505 1577
1506 STACK_END( L2, 1); 1578 STACK_END( L2, 1);
1579 lua_pop( L, 1); // ...
1507 STACK_END( L, 0); 1580 STACK_END( L, 0);
1508 return FALSE; 1581 return TRUE;
1509} 1582}
1510 1583
1511static 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_) 1584static 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
1516 { 1589 {
1517 return FALSE; 1590 return FALSE;
1518 } 1591 }
1519 // Allow only deep userdata entities to be copied across 1592
1520 DEBUGSPEW_CODE( fprintf( stderr, "USERDATA\n")); 1593 // try clonable userdata first
1521 if( copydeep( U, L, L2, i, mode_)) 1594 if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_))
1522 { 1595 {
1596 STACK_MID( L, 0);
1597 STACK_MID( L2, 1);
1523 return TRUE; 1598 return TRUE;
1524 } 1599 }
1525 1600
1526 STACK_MID( L, 0); 1601 STACK_MID( L, 0);
1527 STACK_MID( L2, 0); 1602 STACK_MID( L2, 0);
1528 1603
1529 if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_)) 1604 // Allow only deep userdata entities to be copied across
1605 DEBUGSPEW_CODE( fprintf( stderr, "USERDATA\n"));
1606 if( copydeep( U, L2, L2_cache_i, L, i, mode_, upName_))
1530 { 1607 {
1531 STACK_MID( L, 0); 1608 STACK_MID( L, 0);
1532 STACK_MID( L2, 1); 1609 STACK_MID( L2, 1);
@@ -1563,54 +1640,78 @@ static bool_t inter_copy_function( Universe* U, lua_State* L2, uint_t L2_cache_i
1563 STACK_CHECK( L2, 0); 1640 STACK_CHECK( L2, 0);
1564 DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_)); 1641 DEBUGSPEW_CODE( fprintf( stderr, "FUNCTION %s\n", upName_));
1565 1642
1566 if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata 1643 if( lua_tocfunction( L, i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper
1567 { 1644 {
1568 // clone the full userdata again 1645 // clone the full userdata again
1569 size_t userdata_size = 0; 1646 size_t userdata_size = 0;
1570 void* source; 1647 void* source;
1571 void* clone; 1648 void* clone;
1572 // this function has 2 upvalues: the fqn of its metatable, and the userdata itself 1649
1650 // let's see if we already restored this userdata
1573 lua_getupvalue( L, i, 2); // ... u 1651 lua_getupvalue( L, i, 2); // ... u
1574 source = lua_touserdata( L, -1); 1652 source = lua_touserdata( L, -1);
1575 lookup_table( L2, L, i, mode_, upName_); // ... u // ... mt 1653 lua_pushlightuserdata( L2, source); // ... source
1654 lua_rawget( L2, L2_cache_i); // ... u?
1655 if( !lua_isnil( L2, -1))
1656 {
1657 lua_pop( L, 1); // ...
1658 STACK_MID( L, 0);
1659 STACK_MID( L2, 1);
1660 return TRUE;
1661 }
1662 lua_pop( L2, 1); // ...
1663
1664 // this function has 2 upvalues: the fqn of its metatable, and the userdata itself
1665 lookup_table( L2, L, i, mode_, upName_); // ... mt
1576 // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with 1666 // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with
1577 lua_getfield( L2, -1, "__lanesclone"); // ... mt __lanesclone 1667 lua_getfield( L2, -1, "__lanesclone"); // ... mt __lanesclone
1578 lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone 1668 lua_pushvalue( L2, -1); // ... mt __lanesclone __lanesclone
1669 // 'i' slot is the closure, but from now on it is the actual userdata
1670 i = lua_gettop( L);
1671 source = lua_touserdata( L, -1);
1579 // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone 1672 // call the cloning function with 1 argument, should return the number of bytes to allocate for the clone
1580 lua_pushlightuserdata( L2, source); // ... mt __lanesclone __lanesclone source 1673 lua_pushlightuserdata( L2, source); // ... mt __lanesclone __lanesclone source
1581 lua_call( L2, 1, 1); // ... mt __lanesclone size 1674 lua_call( L2, 1, 1); // ... mt __lanesclone size
1582 userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size 1675 userdata_size = (size_t) lua_tointeger( L2, -1); // ... mt __lanesclone size
1583 lua_pop( L2, 1); // ... mt __lanesclone 1676 lua_pop( L2, 1); // ... mt __lanesclone
1584 lua_pushnil( L2); // ... mt __lanesclone nil
1585 { 1677 {
1586 int const clone_i = lua_gettop( L2); 1678 // extract uservalues (don't transfer them yet)
1587 int uvi = 0; 1679 int uvi = 0;
1588 while( lua_getiuservalue( L, -1, uvi + 1) != LUA_TNONE) // ... u uv 1680 while( lua_getiuservalue( L, i, uvi + 1) != LUA_TNONE) // ... u uv
1589 { 1681 {
1590 luaG_inter_move( U, L, L2, 1, mode_); // ... u // ... mt __lanesclone nil [uv]+
1591 ++ uvi; 1682 ++ uvi;
1592 } 1683 }
1593 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now at the same time as the rest 1684 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
1594 lua_pop( L, 2); // ... u 1685 lua_pop( L, 1); // ... u [uv]*
1595 STACK_MID( L, 0); // ... 1686 STACK_MID( L, uvi + 1);
1596 // create the clone userdata with the required number of uservalue slots 1687 // create the clone userdata with the required number of uservalue slots
1597 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt __lanesclone nil [uv]+ u 1688 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... mt __lanesclone u
1598 lua_replace( L2, clone_i); // ... mt __lanesclone u [uv]+ 1689 // add it in the cache
1599 // assign uservalues 1690 lua_pushlightuserdata( L2, source); // ... mt __lanesclone u source
1691 lua_pushvalue( L2, -2); // ... mt __lanesclone u source u
1692 lua_rawset( L2, L2_cache_i); // ... mt __lanesclone u
1693 // set metatable
1694 lua_pushvalue( L2, -3); // ... mt __lanesclone u mt
1695 lua_setmetatable( L2, -2); // ... mt __lanesclone u
1696 // transfer and assign uservalues
1600 while( uvi > 0) 1697 while( uvi > 0)
1601 { 1698 {
1699 inter_copy_one( U, L2, L2_cache_i, L, lua_absindex( L, -1), vt, mode_, upName_); // ... mt __lanesclone u uv
1700 lua_pop( L, 1); // ... u [uv]*
1602 // this pops the value from the stack 1701 // this pops the value from the stack
1603 lua_setiuservalue( L2, clone_i, uvi); // ... mt __lanesclone u [uv]+ 1702 lua_setiuservalue( L2, -2, uvi); // ... mt __lanesclone u
1604 -- uvi; 1703 -- uvi;
1605 } 1704 }
1606 // when we are done, all uservalues are popped from the stack 1705 // when we are done, all uservalues are popped from the stack
1706 lua_pop( L, 1); // ...
1707 STACK_MID( L, 0);
1607 STACK_MID( L2, 3); // ... mt __lanesclone u 1708 STACK_MID( L2, 3); // ... mt __lanesclone u
1608 } 1709 }
1609 lua_insert( L2, -3); // ... u mt __lanesclone 1710 // perform the custom cloning part
1610 lua_pushlightuserdata( L2, clone); // ... u mt __lanesclone clone 1711 lua_replace( L2, -3); // ... u __lanesclone
1611 lua_pushlightuserdata( L2, source); // ... u mt __lanesclone clone source 1712 lua_pushlightuserdata( L2, clone); // ... u __lanesclone clone
1612 lua_call( L2, 2, 0); // ... u mt 1713 lua_pushlightuserdata( L2, source); // ... u __lanesclone clone source
1613 lua_setmetatable( L2, -2); // ... u 1714 lua_call( L2, 2, 0); // ... u
1614 } 1715 }
1615 else 1716 else
1616 { 1717 {
@@ -1696,7 +1797,7 @@ static bool_t inter_copy_table( Universe* U, lua_State* L2, uint_t L2_cache_i, l
1696* 1797*
1697* Returns TRUE if value was pushed, FALSE if its type is non-supported. 1798* Returns TRUE if value was pushed, FALSE if its type is non-supported.
1698*/ 1799*/
1699static 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_) 1800bool_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_)
1700{ 1801{
1701 bool_t ret = TRUE; 1802 bool_t ret = TRUE;
1702 int val_type = lua_type( L, i); 1803 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
1812 return ret; 1913 return ret;
1813} 1914}
1814 1915
1815
1816/* 1916/*
1817* Akin to 'lua_xmove' but copies values between _any_ Lua states. 1917* Akin to 'lua_xmove' but copies values between _any_ Lua states.
1818* 1918*
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);
22 22
23// ################################################################################################ 23// ################################################################################################
24 24
25void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode_);
26void push_registry_subtable( lua_State* L, UniqueKey key_);
27
28enum e_vt
29{
30 VT_NORMAL,
31 VT_KEY,
32 VT_METATABLE
33};
34bool_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_);
35
36// ################################################################################################
37
25int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_); 38int luaG_inter_copy_package( Universe* U, lua_State* L, lua_State* L2, int package_idx_, LookupMode mode_);
26 39
27int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_); 40int luaG_inter_copy( Universe* U, lua_State* L, lua_State* L2, uint_t n, LookupMode mode_);