aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-04-15 18:31:30 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-04-15 18:31:30 +0200
commite7276bee137b87a2323b125a6a8c792de73819eb (patch)
tree0457187b99121a8935823e0b6c858808ffeb1d29
parentc21b52ad18bcabf084f8dba40d1a09d9e52051bd (diff)
downloadlanes-e7276bee137b87a2323b125a6a8c792de73819eb.tar.gz
lanes-e7276bee137b87a2323b125a6a8c792de73819eb.tar.bz2
lanes-e7276bee137b87a2323b125a6a8c792de73819eb.zip
C++ migration: inter-state transfer managed by a new class InterCopyContext
-rw-r--r--deep_test/deeptest.lua2
-rw-r--r--src/deep.cpp30
-rw-r--r--src/lanes.cpp2
-rw-r--r--src/lanes.h4
-rw-r--r--src/linda.cpp4
-rw-r--r--src/tools.cpp621
-rw-r--r--src/tools.h31
-rw-r--r--tests/rupval.lua17
8 files changed, 385 insertions, 326 deletions
diff --git a/deep_test/deeptest.lua b/deep_test/deeptest.lua
index ec62b1e..33de003 100644
--- a/deep_test/deeptest.lua
+++ b/deep_test/deeptest.lua
@@ -24,7 +24,7 @@ end
24local printDeep = function( prefix_, obj_, t_) 24local printDeep = function( prefix_, obj_, t_)
25 print( prefix_, obj_) 25 print( prefix_, obj_)
26 for uvi = 1, nupvals do 26 for uvi = 1, nupvals do
27 local uservalue = obj_:getuv( 1) 27 local uservalue = obj_:getuv(uvi)
28 print ( "uv #" .. uvi, uservalue, type( uservalue) == "function" and uservalue() or "") 28 print ( "uv #" .. uvi, uservalue, type( uservalue) == "function" and uservalue() or "")
29 end 29 end
30 if t_ then 30 if t_ then
diff --git a/src/deep.cpp b/src/deep.cpp
index 3b0e015..3eb38dc 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -411,39 +411,41 @@ DeepPrelude* DeepFactory::toDeep(lua_State* L, int index) const
411 * the id function of the copied value, or nullptr for non-deep userdata 411 * the id function of the copied value, or nullptr for non-deep userdata
412 * (not copied) 412 * (not copied)
413 */ 413 */
414bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_) 414[[nodiscard]] bool InterCopyContext::copydeep() const
415{ 415{
416 DeepFactory* const factory { get_factory(L, i, mode_) }; 416 DeepFactory* const factory { get_factory(L1, L1_i, mode) };
417 if (factory == nullptr) 417 if (factory == nullptr)
418 { 418 {
419 return false; // not a deep userdata 419 return false; // not a deep userdata
420 } 420 }
421 421
422 STACK_CHECK_START_REL(L, 0); 422 STACK_CHECK_START_REL(L1, 0);
423 STACK_CHECK_START_REL(L2, 0); 423 STACK_CHECK_START_REL(L2, 0);
424 424
425 // extract all uservalues of the source 425 // extract all uservalues of the source
426 int nuv = 0; 426 int nuv = 0;
427 while (lua_getiuservalue(L, i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil 427 while (lua_getiuservalue(L1, L1_i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil
428 { 428 {
429 ++ nuv; 429 ++ nuv;
430 } 430 }
431 // last call returned TNONE and pushed nil, that we don't need 431 // last call returned TNONE and pushed nil, that we don't need
432 lua_pop( L, 1); // ... u [uv]* 432 lua_pop(L1, 1); // ... u [uv]*
433 STACK_CHECK( L, nuv); 433 STACK_CHECK(L1, nuv);
434 434
435 char const* errmsg{ DeepFactory::PushDeepProxy(L2, *lua_tofulluserdata<DeepPrelude*>(L, i), nuv, mode_) }; // u 435 char const* errmsg{ DeepFactory::PushDeepProxy(L2, *lua_tofulluserdata<DeepPrelude*>(L1, L1_i), nuv, mode) }; // u
436 436
437 // transfer all uservalues of the source in the destination 437 // transfer all uservalues of the source in the destination
438 { 438 {
439 int const clone_i = lua_gettop( L2); 439 InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name };
440 while( nuv) 440 int const clone_i{ lua_gettop(L2) };
441 while (nuv)
441 { 442 {
442 if (!inter_copy_one(U, L2, L2_cache_i, L, lua_absindex(L, -1), VT::NORMAL, mode_, upName_)) // u uv 443 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
444 if (!c.inter_copy_one()) // u uv
443 { 445 {
444 return luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1)); 446 luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); // doesn't return
445 } 447 }
446 lua_pop( L, 1); // ... u [uv]* 448 lua_pop(L1, 1); // ... u [uv]*
447 // this pops the value from the stack 449 // this pops the value from the stack
448 lua_setiuservalue(L2, clone_i, nuv); // u 450 lua_setiuservalue(L2, clone_i, nuv); // u
449 -- nuv; 451 -- nuv;
@@ -451,12 +453,12 @@ bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode
451 } 453 }
452 454
453 STACK_CHECK(L2, 1); 455 STACK_CHECK(L2, 1);
454 STACK_CHECK(L, 0); 456 STACK_CHECK(L1, 0);
455 457
456 if (errmsg != nullptr) 458 if (errmsg != nullptr)
457 { 459 {
458 // raise the error in the proper state (not the keeper) 460 // raise the error in the proper state (not the keeper)
459 lua_State* const errL{ (mode_ == LookupMode::FromKeeper) ? L2 : L }; 461 lua_State* const errL{ (mode == LookupMode::FromKeeper) ? L2 : L1 };
460 luaL_error(errL, errmsg); // doesn't return 462 luaL_error(errL, errmsg); // doesn't return
461 } 463 }
462 return true; 464 return true;
diff --git a/src/lanes.cpp b/src/lanes.cpp
index d9262cf..8b4410a 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -1382,7 +1382,7 @@ void push_thread_status(lua_State* L, Lane* lane_)
1382 char const* const str{ thread_status_string(lane_) }; 1382 char const* const str{ thread_status_string(lane_) };
1383 ASSERT_L(str); 1383 ASSERT_L(str);
1384 1384
1385 std::ignore = lua_pushstring(L, str); 1385 lua_pushstring(L, str);
1386} 1386}
1387 1387
1388// ################################################################################################# 1388// #################################################################################################
diff --git a/src/lanes.h b/src/lanes.h
index bc8de55..6f8a084 100644
--- a/src/lanes.h
+++ b/src/lanes.h
@@ -10,6 +10,8 @@ extern "C" {
10 10
11#include "lanesconf.h" 11#include "lanesconf.h"
12 12
13#include <type_traits>
14
13#define LANES_VERSION_MAJOR 4 15#define LANES_VERSION_MAJOR 4
14#define LANES_VERSION_MINOR 0 16#define LANES_VERSION_MINOR 0
15#define LANES_VERSION_PATCH 0 17#define LANES_VERSION_PATCH 0
@@ -24,3 +26,5 @@ LANES_API [[nodiscard]] int luaopen_lanes_core(lua_State* L);
24 26
25// Call this to work with embedded Lanes instead of calling luaopen_lanes_core() 27// Call this to work with embedded Lanes instead of calling luaopen_lanes_core()
26LANES_API void luaopen_lanes_embedded(lua_State* L, lua_CFunction _luaopen_lanes); 28LANES_API void luaopen_lanes_embedded(lua_State* L, lua_CFunction _luaopen_lanes);
29using luaopen_lanes_embedded_t = void (*)(lua_State* L, lua_CFunction luaopen_lanes_);
30static_assert(std::is_same_v<decltype(&luaopen_lanes_embedded), luaopen_lanes_embedded_t>, "signature changed: check all uses of luaopen_lanes_embedded_t");
diff --git a/src/linda.cpp b/src/linda.cpp
index 8005211..7b3973d 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -1020,8 +1020,8 @@ LUAG_FUNC(linda)
1020 luaL_argcheck(L, top <= 2, top, "too many arguments"); 1020 luaL_argcheck(L, top <= 2, top, "too many arguments");
1021 if (top == 1) 1021 if (top == 1)
1022 { 1022 {
1023 int const t{ lua_type(L, 1) }; 1023 LuaType const t{ lua_type_as_enum(L, 1) };
1024 luaL_argcheck(L, t == LUA_TSTRING || t == LUA_TNUMBER, 1, "wrong parameter (should be a string or a number)"); 1024 luaL_argcheck(L, t == LuaType::STRING || t == LuaType::NUMBER, 1, "wrong parameter (should be a string or a number)");
1025 } 1025 }
1026 else if (top == 2) 1026 else if (top == 2)
1027 { 1027 {
diff --git a/src/tools.cpp b/src/tools.cpp
index a0a3018..c995fdd 100644
--- a/src/tools.cpp
+++ b/src/tools.cpp
@@ -36,7 +36,6 @@ THE SOFTWARE.
36#include "universe.h" 36#include "universe.h"
37 37
38// functions implemented in deep.c 38// functions implemented in deep.c
39extern bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_);
40extern void push_registry_subtable( lua_State* L, UniqueKey key_); 39extern void push_registry_subtable( lua_State* L, UniqueKey key_);
41 40
42DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); 41DEBUGSPEW_CODE( char const* debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+");
@@ -753,11 +752,11 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
753/* 752/*
754 * Push a looked-up table, or nothing if we found nothing 753 * Push a looked-up table, or nothing if we found nothing
755 */ 754 */
756[[nodiscard]] static bool lookup_table(Dest L2, Source L, int i, LookupMode mode_, char const* upName_) 755[[nodiscard]] static bool lookup_table(Dest L2, Source L1, SourceIndex i_, LookupMode mode_, char const* upName_)
757{ 756{
758 // get the name of the table we want to send 757 // get the name of the table we want to send
759 size_t len; 758 size_t len;
760 char const* fqn = find_lookup_name( L, i, mode_, upName_, &len); 759 char const* fqn = find_lookup_name(L1, i_, mode_, upName_, &len);
761 if (nullptr == fqn) // name not found, it is some user-created table 760 if (nullptr == fqn) // name not found, it is some user-created table
762 { 761 {
763 return false; 762 return false;
@@ -768,8 +767,8 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
768 switch( mode_) 767 switch( mode_)
769 { 768 {
770 default: // shouldn't happen, in theory... 769 default: // shouldn't happen, in theory...
771 (void) luaL_error( L, "internal error: unknown lookup mode"); 770 luaL_error(L1, "internal error: unknown lookup mode"); // doesn't return
772 return false; 771 break;
773 772
774 case LookupMode::ToKeeper: 773 case LookupMode::ToKeeper:
775 // push a sentinel closure that holds the lookup name as upvalue 774 // push a sentinel closure that holds the lookup name as upvalue
@@ -781,7 +780,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
781 case LookupMode::FromKeeper: 780 case LookupMode::FromKeeper:
782 LOOKUP_REGKEY.pushValue(L2); // {} 781 LOOKUP_REGKEY.pushValue(L2); // {}
783 STACK_CHECK(L2, 1); 782 STACK_CHECK(L2, 1);
784 ASSERT_L(lua_istable(L2, -1)); 783 _ASSERT_L(L1, lua_istable(L2, -1));
785 lua_pushlstring(L2, fqn, len); // {} "f.q.n" 784 lua_pushlstring(L2, fqn, len); // {} "f.q.n"
786 lua_rawget(L2, -2); // {} t 785 lua_rawget(L2, -2); // {} t
787 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) 786 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead)
@@ -792,23 +791,23 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
792 STACK_CHECK(L2, 0); 791 STACK_CHECK(L2, 0);
793 return false; 792 return false;
794 } 793 }
795 else if( !lua_istable(L2, -1)) 794 else if (!lua_istable(L2, -1))
796 { 795 {
797 char const* from, *to; 796 char const* from, *to;
798 lua_getglobal(L, "decoda_name"); // ... t ... decoda_name 797 lua_getglobal(L1, "decoda_name"); // ... t ... decoda_name
799 from = lua_tostring(L, -1); 798 from = lua_tostring(L1, -1);
800 lua_pop(L, 1); // ... t ... 799 lua_pop(L1, 1); // ... t ...
801 lua_getglobal(L2, "decoda_name"); // {} t decoda_name 800 lua_getglobal(L2, "decoda_name"); // {} t decoda_name
802 to = lua_tostring( L2, -1); 801 to = lua_tostring(L2, -1);
803 lua_pop(L2, 1); // {} t 802 lua_pop(L2, 1); // {} t
804 // when mode_ == LookupMode::FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error 803 // when mode_ == LookupMode::FromKeeper, L is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error
805 (void) luaL_error( 804 luaL_error(
806 (mode_ == LookupMode::FromKeeper) ? L2 : L 805 (mode_ == LookupMode::FromKeeper) ? L2 : L1
807 , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database." 806 , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database."
808 , from ? from : "main" 807 , from ? from : "main"
809 , fqn 808 , fqn
810 , to ? to : "main" 809 , to ? to : "main"
811 ); 810 ); // doesn't return
812 return false; 811 return false;
813 } 812 }
814 lua_remove(L2, -2); // t 813 lua_remove(L2, -2); // t
@@ -830,33 +829,34 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
830 * Returns true if the table was cached (no need to fill it!); false if 829 * Returns true if the table was cached (no need to fill it!); false if
831 * it's a virgin. 830 * it's a virgin.
832 */ 831 */
833[[nodiscard]] static bool push_cached_table(Dest L2, int L2_cache_i, Source L, int i) 832[[nodiscard]] static bool push_cached_table(Dest L2, CacheIndex L2_cache_i, Source L1, SourceIndex i)
834{ 833{
835 void const* p{ lua_topointer(L, i) }; 834 void const* p{ lua_topointer(L1, i) };
836 835
837 ASSERT_L( L2_cache_i != 0); 836 _ASSERT_L(L1, L2_cache_i != 0);
838 STACK_GROW( L2, 3); // L2 837 STACK_GROW(L2, 3); // L2
839 STACK_CHECK_START_REL(L2, 0); 838 STACK_CHECK_START_REL(L2, 0);
840 839
841 // We don't need to use the from state ('L') in ID since the life span 840 // We don't need to use the from state ('L1') in ID since the life span
842 // is only for the duration of a copy (both states are locked). 841 // is only for the duration of a copy (both states are locked).
843 // push a light userdata uniquely representing the table 842 // push a light userdata uniquely representing the table
844 lua_pushlightuserdata(L2, const_cast<void*>(p)); // ... p 843 lua_pushlightuserdata(L2, const_cast<void*>(p)); // ... p
845 844
846 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); 845 //fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1));
847 846
848 lua_rawget( L2, L2_cache_i); // ... {cached|nil} 847 lua_rawget(L2, L2_cache_i); // ... {cached|nil}
849 bool const not_found_in_cache{ lua_isnil(L2, -1) }; 848 bool const not_found_in_cache{ lua_isnil(L2, -1) };
850 if( not_found_in_cache) 849 if (not_found_in_cache)
851 { 850 {
852 lua_pop( L2, 1); // ... 851 // create a new entry in the cache
853 lua_newtable( L2); // ... {} 852 lua_pop(L2, 1); // ...
854 lua_pushlightuserdata(L2, const_cast<void*>(p)); // ... {} p 853 lua_newtable(L2); // ... {}
855 lua_pushvalue( L2, -2); // ... {} p {} 854 lua_pushlightuserdata(L2, const_cast<void*>(p)); // ... {} p
856 lua_rawset( L2, L2_cache_i); // ... {} 855 lua_pushvalue(L2, -2); // ... {} p {}
856 lua_rawset(L2, L2_cache_i); // ... {}
857 } 857 }
858 STACK_CHECK( L2, 1); 858 STACK_CHECK(L2, 1);
859 ASSERT_L( lua_istable( L2, -1)); 859 _ASSERT_L(L1, lua_istable( L2, -1));
860 return !not_found_in_cache; 860 return !not_found_in_cache;
861} 861}
862 862
@@ -1181,23 +1181,19 @@ static char const* vt_names[] =
1181 1181
1182// ################################################################################################# 1182// #################################################################################################
1183 1183
1184static void copy_func(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_) 1184void InterCopyContext::copy_func() const
1185{ 1185{
1186 int n, needToPush; 1186 _ASSERT_L(L1, L2_cache_i != 0); // ... {cache} ... p
1187 luaL_Buffer B; 1187 STACK_GROW(L1, 2);
1188 B.L = nullptr; 1188 STACK_CHECK_START_REL(L1, 0);
1189
1190 ASSERT_L( L2_cache_i != 0); // ... {cache} ... p
1191 STACK_GROW( L, 2);
1192 STACK_CHECK_START_REL(L, 0);
1193 1189
1194 1190
1195 // 'lua_dump()' needs the function at top of stack 1191 // 'lua_dump()' needs the function at top of stack
1196 // if already on top of the stack, no need to push again 1192 // if already on top of the stack, no need to push again
1197 needToPush = (i != lua_gettop( L)); 1193 bool const needToPush{ L1_i != lua_gettop(L1) };
1198 if( needToPush) 1194 if (needToPush)
1199 { 1195 {
1200 lua_pushvalue( L, i); // ... f 1196 lua_pushvalue(L1, L1_i); // ... f
1201 } 1197 }
1202 1198
1203 // 1199 //
@@ -1205,18 +1201,20 @@ static void copy_func(Universe* U, Dest L2, int L2_cache_i, Source L, int i, Loo
1205 // to the writer" (and we only return 0) 1201 // to the writer" (and we only return 0)
1206 // not sure this could ever fail but for memory shortage reasons 1202 // not sure this could ever fail but for memory shortage reasons
1207 // last parameter is Lua 5.4-specific (no stripping) 1203 // last parameter is Lua 5.4-specific (no stripping)
1208 if( lua504_dump( L, buf_writer, &B, 0) != 0) 1204 luaL_Buffer B;
1205 B.L = nullptr;
1206 if (lua504_dump(L1, buf_writer, &B, 0) != 0)
1209 { 1207 {
1210 luaL_error( L, "internal error: function dump failed."); 1208 luaL_error(L1, "internal error: function dump failed."); // doesn't return
1211 } 1209 }
1212 1210
1213 // pushes dumped string on 'L' 1211 // pushes dumped string on 'L1'
1214 luaL_pushresult( &B); // ... f b 1212 luaL_pushresult(&B); // ... f b
1215 1213
1216 // if not pushed, no need to pop 1214 // if not pushed, no need to pop
1217 if( needToPush) 1215 if (needToPush)
1218 { 1216 {
1219 lua_remove( L, -2); // ... b 1217 lua_remove(L1, -2); // ... b
1220 } 1218 }
1221 1219
1222 // transfer the bytecode, then the upvalues, to create a similar closure 1220 // transfer the bytecode, then the upvalues, to create a similar closure
@@ -1238,93 +1236,94 @@ static void copy_func(Universe* U, Dest L2, int L2_cache_i, Source L, int i, Loo
1238 #endif // LOG_FUNC_INFO 1236 #endif // LOG_FUNC_INFO
1239 { 1237 {
1240 size_t sz; 1238 size_t sz;
1241 char const* s = lua_tolstring( L, -1, &sz); // ... b 1239 char const* s = lua_tolstring(L1, -1, &sz); // ... b
1242 ASSERT_L( s && sz); 1240 _ASSERT_L(L1, s && sz);
1243 STACK_GROW( L2, 2); 1241 STACK_GROW(L2, 2);
1244 // Note: Line numbers seem to be taken precisely from the 1242 // Note: Line numbers seem to be taken precisely from the
1245 // original function. 'name' is not used since the chunk 1243 // original function. 'name' is not used since the chunk
1246 // is precompiled (it seems...). 1244 // is precompiled (it seems...).
1247 // 1245 //
1248 // TBD: Can we get the function's original name through, as well? 1246 // TBD: Can we get the function's original name through, as well?
1249 // 1247 //
1250 if( luaL_loadbuffer( L2, s, sz, name) != 0) // ... {cache} ... p function 1248 if (luaL_loadbuffer(L2, s, sz, name) != 0) // ... {cache} ... p function
1251 { 1249 {
1252 // chunk is precompiled so only LUA_ERRMEM can happen 1250 // chunk is precompiled so only LUA_ERRMEM can happen
1253 // "Otherwise, it pushes an error message" 1251 // "Otherwise, it pushes an error message"
1254 // 1252 //
1255 STACK_GROW( L, 1); 1253 STACK_GROW(L1, 1);
1256 luaL_error( L, "%s: %s", upName_, lua_tostring( L2, -1)); 1254 luaL_error(L1, "%s: %s", name, lua_tostring(L2, -1)); // doesn't return
1257 } 1255 }
1258 // remove the dumped string 1256 // remove the dumped string
1259 lua_pop( L, 1); // ... 1257 lua_pop(L1, 1); // ...
1260 // now set the cache as soon as we can. 1258 // now set the cache as soon as we can.
1261 // this is necessary if one of the function's upvalues references it indirectly 1259 // this is necessary if one of the function's upvalues references it indirectly
1262 // we need to find it in the cache even if it isn't fully transfered yet 1260 // we need to find it in the cache even if it isn't fully transfered yet
1263 lua_insert( L2, -2); // ... {cache} ... function p 1261 lua_insert(L2, -2); // ... {cache} ... function p
1264 lua_pushvalue( L2, -2); // ... {cache} ... function p function 1262 lua_pushvalue(L2, -2); // ... {cache} ... function p function
1265 // cache[p] = function 1263 // cache[p] = function
1266 lua_rawset( L2, L2_cache_i); // ... {cache} ... function 1264 lua_rawset(L2, L2_cache_i); // ... {cache} ... function
1267 } 1265 }
1268 STACK_CHECK( L, 0); 1266 STACK_CHECK(L1, 0);
1269 1267
1270 /* push over any upvalues; references to this function will come from 1268 /* push over any upvalues; references to this function will come from
1271 * cache so we don't end up in eternal loop. 1269 * cache so we don't end up in eternal loop.
1272 * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy! 1270 * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy!
1273 * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! 1271 * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state!
1274 */ 1272 */
1273 int n{ 0 };
1275 { 1274 {
1276 char const* upname; 1275 InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
1277#if LUA_VERSION_NUM >= 502 1276#if LUA_VERSION_NUM >= 502
1278 // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) 1277 // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default)
1279 // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... 1278 // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state...
1280 // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table 1279 // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
1281 lua_pushglobaltable( L); // ... _G 1280 lua_pushglobaltable(L1); // ... _G
1282#endif // LUA_VERSION_NUM 1281#endif // LUA_VERSION_NUM
1283 for (n = 0; (upname = lua_getupvalue(L, i, 1 + n)) != nullptr; ++n) 1282 for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n)
1284 { // ... _G up[n] 1283 { // ... _G up[n]
1285 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END, n, upname)); 1284 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END, n, c.name));
1286#if LUA_VERSION_NUM >= 502 1285#if LUA_VERSION_NUM >= 502
1287 if( lua_rawequal( L, -1, -2)) // is the upvalue equal to the global table? 1286 if( lua_rawequal(L1, -1, -2)) // is the upvalue equal to the global table?
1288 { 1287 {
1289 DEBUGSPEW_CODE( fprintf( stderr, "pushing destination global scope\n")); 1288 DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n"));
1290 lua_pushglobaltable( L2); // ... {cache} ... function <upvalues> 1289 lua_pushglobaltable(L2); // ... {cache} ... function <upvalues>
1291 } 1290 }
1292 else 1291 else
1293#endif // LUA_VERSION_NUM 1292#endif // LUA_VERSION_NUM
1294 { 1293 {
1295 DEBUGSPEW_CODE( fprintf( stderr, "copying value\n")); 1294 DEBUGSPEW_CODE(fprintf(stderr, "copying value\n"));
1296 if( !inter_copy_one( U, L2, L2_cache_i, L, lua_gettop( L), VT::NORMAL, mode_, upname)) // ... {cache} ... function <upvalues> 1295 c.L1_i = SourceIndex{ lua_gettop(L1) };
1296 if (!c.inter_copy_one()) // ... {cache} ... function <upvalues>
1297 { 1297 {
1298 luaL_error( L, "Cannot copy upvalue type '%s'", luaL_typename( L, -1)); 1298 luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); // doesn't return
1299 } 1299 }
1300 } 1300 }
1301 lua_pop( L, 1); // ... _G 1301 lua_pop(L1, 1); // ... _G
1302 } 1302 }
1303#if LUA_VERSION_NUM >= 502 1303#if LUA_VERSION_NUM >= 502
1304 lua_pop( L, 1); // ... 1304 lua_pop(L1, 1); // ...
1305#endif // LUA_VERSION_NUM 1305#endif // LUA_VERSION_NUM
1306 } 1306 }
1307 // L2: function + 'n' upvalues (>=0) 1307 // L2: function + 'n' upvalues (>=0)
1308 1308
1309 STACK_CHECK( L, 0); 1309 STACK_CHECK(L1, 0);
1310 1310
1311 // Set upvalues (originally set to 'nil' by 'lua_load') 1311 // Set upvalues (originally set to 'nil' by 'lua_load')
1312 { 1312 {
1313 int func_index = lua_gettop( L2) - n; 1313 for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n)
1314 for( ; n > 0; -- n)
1315 { 1314 {
1316 char const* rc = lua_setupvalue( L2, func_index, n); // ... {cache} ... function 1315 char const* rc{ lua_setupvalue(L2, func_index, n) }; // ... {cache} ... function
1317 // 1316 //
1318 // "assigns the value at the top of the stack to the upvalue and returns its name. 1317 // "assigns the value at the top of the stack to the upvalue and returns its name.
1319 // It also pops the value from the stack." 1318 // It also pops the value from the stack."
1320 1319
1321 ASSERT_L( rc); // not having enough slots? 1320 _ASSERT_L(L1, rc); // not having enough slots?
1322 } 1321 }
1323 // once all upvalues have been set we are left 1322 // once all upvalues have been set we are left
1324 // with the function at the top of the stack // ... {cache} ... function 1323 // with the function at the top of the stack // ... {cache} ... function
1325 } 1324 }
1326 } 1325 }
1327 STACK_CHECK( L, 0); 1326 STACK_CHECK(L1, 0);
1328} 1327}
1329 1328
1330// ################################################################################################# 1329// #################################################################################################
@@ -1335,17 +1334,17 @@ static void copy_func(Universe* U, Dest L2, int L2_cache_i, Source L, int i, Loo
1335 * 1334 *
1336 * Always pushes a function to 'L2'. 1335 * Always pushes a function to 'L2'.
1337 */ 1336 */
1338static void copy_cached_func(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_) 1337void InterCopyContext::copy_cached_func() const
1339{ 1338{
1340 FuncSubType funcSubType; 1339 FuncSubType funcSubType;
1341 std::ignore = luaG_tocfunction(L, i, &funcSubType); // nullptr for LuaJIT-fast && bytecode functions 1340 std::ignore = luaG_tocfunction(L1, L1_i, &funcSubType); // nullptr for LuaJIT-fast && bytecode functions
1342 if( funcSubType == FST_Bytecode) 1341 if (funcSubType == FST_Bytecode)
1343 { 1342 {
1344 void* const aspointer = (void*)lua_topointer( L, i); 1343 void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i));
1345 // TBD: Merge this and same code for tables 1344 // TBD: Merge this and same code for tables
1346 ASSERT_L( L2_cache_i != 0); 1345 _ASSERT_L(L1, L2_cache_i != 0);
1347 1346
1348 STACK_GROW( L2, 2); 1347 STACK_GROW(L2, 2);
1349 1348
1350 // L2_cache[id_str]= function 1349 // L2_cache[id_str]= function
1351 // 1350 //
@@ -1356,51 +1355,51 @@ static void copy_cached_func(Universe* U, Dest L2, int L2_cache_i, Source L, int
1356 // 1355 //
1357 1356
1358 // push a light userdata uniquely representing the function 1357 // push a light userdata uniquely representing the function
1359 lua_pushlightuserdata( L2, aspointer); // ... {cache} ... p 1358 lua_pushlightuserdata(L2, aspointer); // ... {cache} ... p
1360 1359
1361 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); 1360 //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
1362 1361
1363 lua_pushvalue( L2, -1); // ... {cache} ... p p 1362 lua_pushvalue(L2, -1); // ... {cache} ... p p
1364 lua_rawget( L2, L2_cache_i); // ... {cache} ... p function|nil|true 1363 lua_rawget(L2, L2_cache_i); // ... {cache} ... p function|nil|true
1365 1364
1366 if( lua_isnil( L2, -1)) // function is unknown 1365 if (lua_isnil(L2, -1)) // function is unknown
1367 { 1366 {
1368 lua_pop( L2, 1); // ... {cache} ... p 1367 lua_pop(L2, 1); // ... {cache} ... p
1369 1368
1370 // Set to 'true' for the duration of creation; need to find self-references 1369 // Set to 'true' for the duration of creation; need to find self-references
1371 // via upvalues 1370 // via upvalues
1372 // 1371 //
1373 // pushes a copy of the func, stores a reference in the cache 1372 // pushes a copy of the func, stores a reference in the cache
1374 copy_func( U, L2, L2_cache_i, L, i, mode_, upName_); // ... {cache} ... function 1373 copy_func(); // ... {cache} ... function
1375 } 1374 }
1376 else // found function in the cache 1375 else // found function in the cache
1377 { 1376 {
1378 lua_remove( L2, -2); // ... {cache} ... function 1377 lua_remove(L2, -2); // ... {cache} ... function
1379 } 1378 }
1380 STACK_CHECK( L2, 1); 1379 STACK_CHECK(L2, 1);
1381 ASSERT_L( lua_isfunction( L2, -1)); 1380 _ASSERT_L(L1, lua_isfunction(L2, -1));
1382 } 1381 }
1383 else // function is native/LuaJIT: no need to cache 1382 else // function is native/LuaJIT: no need to cache
1384 { 1383 {
1385 lookup_native_func( L2, L, i, mode_, upName_); // ... {cache} ... function 1384 lookup_native_func(L2, L1, L1_i, mode, name); // ... {cache} ... function
1386 // if the function was in fact a lookup sentinel, we can either get a function or a table here 1385 // if the function was in fact a lookup sentinel, we can either get a function or a table here
1387 ASSERT_L( lua_isfunction( L2, -1) || lua_istable( L2, -1)); 1386 _ASSERT_L(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1));
1388 } 1387 }
1389} 1388}
1390 1389
1391// ################################################################################################# 1390// #################################################################################################
1392 1391
1393[[nodiscard]] static bool push_cached_metatable(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_) 1392[[nodiscard]] bool InterCopyContext::push_cached_metatable() const
1394{ 1393{
1395 STACK_CHECK_START_REL(L, 0); 1394 STACK_CHECK_START_REL(L1, 0);
1396 if (!lua_getmetatable(L, i)) // ... mt 1395 if (!lua_getmetatable(L1, L1_i)) // ... mt
1397 { 1396 {
1398 STACK_CHECK( L, 0); 1397 STACK_CHECK(L1, 0);
1399 return false; 1398 return false;
1400 } 1399 }
1401 STACK_CHECK(L, 1); 1400 STACK_CHECK(L1, 1);
1402 1401
1403 lua_Integer const mt_id{ get_mt_id(U, L, -1) }; // Unique id for the metatable 1402 lua_Integer const mt_id{ get_mt_id(U, L1, -1) }; // Unique id for the metatable
1404 1403
1405 STACK_CHECK_START_REL(L2, 0); 1404 STACK_CHECK_START_REL(L2, 0);
1406 STACK_GROW(L2, 4); 1405 STACK_GROW(L2, 4);
@@ -1413,9 +1412,10 @@ static void copy_cached_func(Universe* U, Dest L2, int L2_cache_i, Source L, int
1413 if (lua_isnil(L2, -1)) 1412 if (lua_isnil(L2, -1))
1414 { // L2 did not know the metatable 1413 { // L2 did not know the metatable
1415 lua_pop(L2, 1); // _R[REG_MTID] 1414 lua_pop(L2, 1); // _R[REG_MTID]
1416 if (!inter_copy_one(U, L2, L2_cache_i, L, lua_gettop(L), VT::METATABLE, mode_, upName_)) // _R[REG_MTID] mt? 1415 InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name };
1416 if (!c.inter_copy_one()) // _R[REG_MTID] mt?
1417 { 1417 {
1418 luaL_error(L, "Error copying a metatable"); // doesn't return 1418 luaL_error(L1, "Error copying a metatable"); // doesn't return
1419 } 1419 }
1420 1420
1421 STACK_CHECK(L2, 2); // _R[REG_MTID] mt 1421 STACK_CHECK(L2, 2); // _R[REG_MTID] mt
@@ -1432,76 +1432,85 @@ static void copy_cached_func(Universe* U, Dest L2, int L2_cache_i, Source L, int
1432 } 1432 }
1433 lua_remove(L2, -2); // mt 1433 lua_remove(L2, -2); // mt
1434 1434
1435 lua_pop(L, 1); // ... 1435 lua_pop(L1, 1); // ...
1436 STACK_CHECK(L2, 1); 1436 STACK_CHECK(L2, 1);
1437 STACK_CHECK(L, 0); 1437 STACK_CHECK(L1, 0);
1438 return true; 1438 return true;
1439} 1439}
1440 1440
1441// ################################################################################################# 1441// #################################################################################################
1442 1442
1443[[nodiscard]] static void inter_copy_keyvaluepair(Universe* U, Dest L2, int L2_cache_i, Source L, VT vt_, LookupMode mode_, char const* upName_) 1443void InterCopyContext::inter_copy_keyvaluepair() const
1444{ 1444{
1445 int val_i = lua_gettop(L); 1445 SourceIndex const val_i{ lua_gettop(L1) };
1446 int key_i = val_i - 1; 1446 SourceIndex const key_i{ val_i - 1 };
1447 1447
1448 // Only basic key types are copied over; others ignored 1448 // Only basic key types are copied over; others ignored
1449 if (inter_copy_one(U, L2, L2_cache_i, L, key_i, VT::KEY, mode_, upName_)) 1449 InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name };
1450 if (!c.inter_copy_one())
1450 { 1451 {
1451 char* valPath = (char*) upName_; 1452 return;
1452 if( U->verboseErrors) 1453 // we could raise an error instead of ignoring the table entry, like so:
1454 //luaL_error(L1, "Unable to copy %s key '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", name, luaL_typename(L1, key_i)); // doesn't return
1455 // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer?
1456 }
1457
1458 char* valPath{ nullptr };
1459 if( U->verboseErrors)
1460 {
1461 // for debug purposes, let's try to build a useful name
1462 if (lua_type(L1, key_i) == LUA_TSTRING)
1453 { 1463 {
1454 // for debug purposes, let's try to build a useful name 1464 char const* key{ lua_tostring(L1, key_i) };
1455 if( lua_type( L, key_i) == LUA_TSTRING) 1465 size_t const keyRawLen = lua_rawlen(L1, key_i);
1456 { 1466 size_t const bufLen = strlen(name) + keyRawLen + 2;
1457 char const* key = lua_tostring( L, key_i); 1467 valPath = (char*) alloca( bufLen);
1458 size_t const keyRawLen = lua_rawlen( L, key_i); 1468 sprintf( valPath, "%s.%*s", name, (int) keyRawLen, key);
1459 size_t const bufLen = strlen( upName_) + keyRawLen + 2; 1469 key = nullptr;
1460 valPath = (char*) alloca( bufLen); 1470 }
1461 sprintf( valPath, "%s.%*s", upName_, (int) keyRawLen, key);
1462 key = nullptr;
1463 }
1464#if defined LUA_LNUM || LUA_VERSION_NUM >= 503 1471#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
1465 else if( lua_isinteger( L, key_i)) 1472 else if (lua_isinteger(L1, key_i))
1466 { 1473 {
1467 lua_Integer key = lua_tointeger( L, key_i); 1474 lua_Integer const key{ lua_tointeger(L1, key_i) };
1468 valPath = (char*) alloca( strlen( upName_) + 32 + 3); 1475 valPath = (char*) alloca(strlen(name) + 32 + 3);
1469 sprintf( valPath, "%s[" LUA_INTEGER_FMT "]", upName_, key); 1476 sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key);
1470 } 1477 }
1471#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 1478#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
1472 else if( lua_type( L, key_i) == LUA_TNUMBER) 1479 else if (lua_type(L1, key_i) == LUA_TNUMBER)
1473 { 1480 {
1474 lua_Number key = lua_tonumber( L, key_i); 1481 lua_Number const key{ lua_tonumber(L1, key_i) };
1475 valPath = (char*) alloca( strlen( upName_) + 32 + 3); 1482 valPath = (char*) alloca(strlen(name) + 32 + 3);
1476 sprintf( valPath, "%s[" LUA_NUMBER_FMT "]", upName_, key); 1483 sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key);
1477 }
1478 else if( lua_type( L, key_i) == LUA_TLIGHTUSERDATA)
1479 {
1480 void* key = lua_touserdata( L, key_i);
1481 valPath = (char*) alloca( strlen( upName_) + 16 + 5);
1482 sprintf( valPath, "%s[U:%p]", upName_, key);
1483 }
1484 else if( lua_type( L, key_i) == LUA_TBOOLEAN)
1485 {
1486 int key = lua_toboolean( L, key_i);
1487 valPath = (char*) alloca( strlen( upName_) + 8);
1488 sprintf( valPath, "%s[%s]", upName_, key ? "true" : "false");
1489 }
1490 } 1484 }
1491 /* 1485 else if (lua_type( L1, key_i) == LUA_TLIGHTUSERDATA)
1492 * Contents of metatables are copied with cache checking;
1493 * important to detect loops.
1494 */
1495 if (inter_copy_one(U, L2, L2_cache_i, L, val_i, VT::NORMAL, mode_, valPath))
1496 { 1486 {
1497 ASSERT_L( lua_istable( L2, -3)); 1487 void* const key{ lua_touserdata(L1, key_i) };
1498 lua_rawset( L2, -3); // add to table (pops key & val) 1488 valPath = (char*) alloca(strlen(name) + 16 + 5);
1489 sprintf(valPath, "%s[U:%p]", name, key);
1499 } 1490 }
1500 else 1491 else if( lua_type( L1, key_i) == LUA_TBOOLEAN)
1501 { 1492 {
1502 luaL_error(L, "Unable to copy %s entry '%s' because of value is of type '%s'", (vt_ == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L, val_i)); 1493 int const key{ lua_toboolean(L1, key_i) };
1494 valPath = (char*) alloca(strlen(name) + 8);
1495 sprintf(valPath, "%s[%s]", name, key ? "true" : "false");
1503 } 1496 }
1504 } 1497 }
1498 /*
1499 * Contents of metatables are copied with cache checking;
1500 * important to detect loops.
1501 */
1502 c.L1_i = SourceIndex{ val_i };
1503 c.name = valPath ? valPath : name;
1504 c.vt = VT::NORMAL;
1505 if (c.inter_copy_one())
1506 {
1507 _ASSERT_L(L1, lua_istable( L2, -3));
1508 lua_rawset(L2, -3); // add to table (pops key & val)
1509 }
1510 else
1511 {
1512 luaL_error(L1, "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i));
1513 }
1505} 1514}
1506 1515
1507// ################################################################################################# 1516// #################################################################################################
@@ -1512,219 +1521,225 @@ static void copy_cached_func(Universe* U, Dest L2, int L2_cache_i, Source L, int
1512*/ 1521*/
1513static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull }; 1522static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1514 1523
1515[[nodiscard]] static bool copyclone(Universe* U, Dest L2, int L2_cache_i, Source L, int source_i_, LookupMode mode_, char const* upName_) 1524[[nodiscard]] bool InterCopyContext::copyclone() const
1516{ 1525{
1517 void* const source = lua_touserdata( L, source_i_); 1526 SourceIndex const L1_i{ lua_absindex(L1, this->L1_i) };
1518 source_i_ = lua_absindex( L, source_i_); 1527 void* const source{ lua_touserdata(L1, L1_i) };
1519 1528
1520 STACK_CHECK_START_REL(L, 0); // L (source) // L2 (destination) 1529 STACK_CHECK_START_REL(L1, 0); // L1 (source) // L2 (destination)
1521 STACK_CHECK_START_REL(L2, 0); 1530 STACK_CHECK_START_REL(L2, 0);
1522 1531
1523 // Check if the source was already cloned during this copy 1532 // Check if the source was already cloned during this copy
1524 lua_pushlightuserdata( L2, source); // ... source 1533 lua_pushlightuserdata(L2, source); // ... source
1525 lua_rawget( L2, L2_cache_i); // ... clone? 1534 lua_rawget(L2, L2_cache_i); // ... clone?
1526 if ( !lua_isnil( L2, -1)) 1535 if (!lua_isnil(L2, -1))
1527 { 1536 {
1528 STACK_CHECK( L2, 1); 1537 STACK_CHECK(L2, 1);
1529 return true; 1538 return true;
1530 } 1539 }
1531 else 1540 else
1532 { 1541 {
1533 lua_pop( L2, 1); // ... 1542 lua_pop(L2, 1); // ...
1534 } 1543 }
1535 STACK_CHECK( L2, 0); 1544 STACK_CHECK(L2, 0);
1536 1545
1537 // no metatable? -> not clonable 1546 // no metatable? -> not clonable
1538 if( !lua_getmetatable( L, source_i_)) // ... mt? 1547 if( !lua_getmetatable(L1, L1_i)) // ... mt?
1539 { 1548 {
1540 STACK_CHECK( L, 0); 1549 STACK_CHECK(L1, 0);
1541 return false; 1550 return false;
1542 } 1551 }
1543 1552
1544 // no __lanesclone? -> not clonable 1553 // no __lanesclone? -> not clonable
1545 lua_getfield( L, -1, "__lanesclone"); // ... mt __lanesclone? 1554 lua_getfield(L1, -1, "__lanesclone"); // ... mt __lanesclone?
1546 if( lua_isnil( L, -1)) 1555 if( lua_isnil(L1, -1))
1547 { 1556 {
1548 lua_pop( L, 2); // ... 1557 lua_pop(L1, 2); // ...
1549 STACK_CHECK( L, 0); 1558 STACK_CHECK(L1, 0);
1550 return false; 1559 return false;
1551 } 1560 }
1552 1561
1553 // we need to copy over the uservalues of the userdata as well 1562 // we need to copy over the uservalues of the userdata as well
1554 { 1563 {
1555 int const mt = lua_absindex( L, -2); // ... mt __lanesclone 1564 int const mt{ lua_absindex(L1, -2) }; // ... mt __lanesclone
1556 size_t const userdata_size = (size_t) lua_rawlen( L, source_i_); 1565 size_t const userdata_size{ lua_rawlen(L1, L1_i) };
1557 void* clone = nullptr;
1558 // extract all the uservalues, but don't transfer them yet 1566 // extract all the uservalues, but don't transfer them yet
1559 int uvi = 0; 1567 int uvi = 0;
1560 while( lua_getiuservalue( L, source_i_, ++ uvi) != LUA_TNONE) {} // ... mt __lanesclone [uv]+ nil 1568 while (lua_getiuservalue( L1, L1_i, ++ uvi) != LUA_TNONE) {} // ... mt __lanesclone [uv]+ nil
1561 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now 1569 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
1562 lua_pop( L, 1); // ... mt __lanesclone [uv]+ 1570 lua_pop(L1, 1); // ... mt __lanesclone [uv]+
1563 -- uvi; 1571 -- uvi;
1564 // create the clone userdata with the required number of uservalue slots 1572 // create the clone userdata with the required number of uservalue slots
1565 clone = lua_newuserdatauv( L2, userdata_size, uvi); // ... u 1573 void* clone = lua_newuserdatauv(L2, userdata_size, uvi); // ... u
1566 // copy the metatable in the target state, and give it to the clone we put there 1574 // copy the metatable in the target state, and give it to the clone we put there
1567 if (inter_copy_one(U, L2, L2_cache_i, L, mt, VT::NORMAL, mode_, upName_)) // ... u mt|sentinel 1575 InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name };
1576 if (c.inter_copy_one()) // ... u mt|sentinel
1568 { 1577 {
1569 if( LookupMode::ToKeeper == mode_) // ... u sentinel 1578 if( LookupMode::ToKeeper == mode) // ... u sentinel
1570 { 1579 {
1571 ASSERT_L( lua_tocfunction( L2, -1) == table_lookup_sentinel); 1580 _ASSERT_L(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel);
1572 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn 1581 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
1573 lua_getupvalue( L2, -1, 1); // ... u sentinel fqn 1582 lua_getupvalue(L2, -1, 1); // ... u sentinel fqn
1574 lua_remove( L2, -2); // ... u fqn 1583 lua_remove(L2, -2); // ... u fqn
1575 lua_insert( L2, -2); // ... fqn u 1584 lua_insert(L2, -2); // ... fqn u
1576 lua_pushcclosure( L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel 1585 lua_pushcclosure(L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel
1577 } 1586 }
1578 else // from keeper or direct // ... u mt 1587 else // from keeper or direct // ... u mt
1579 { 1588 {
1580 ASSERT_L( lua_istable( L2, -1)); 1589 _ASSERT_L(L1, lua_istable(L2, -1));
1581 lua_setmetatable( L2, -2); // ... u 1590 lua_setmetatable(L2, -2); // ... u
1582 } 1591 }
1583 STACK_CHECK( L2, 1); 1592 STACK_CHECK(L2, 1);
1584 } 1593 }
1585 else 1594 else
1586 { 1595 {
1587 (void) luaL_error( L, "Error copying a metatable"); 1596 luaL_error(L1, "Error copying a metatable"); // doesn't return
1588 } 1597 }
1589 // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel 1598 // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel
1590 lua_pushlightuserdata( L2, source); // ... u source 1599 lua_pushlightuserdata( L2, source); // ... u source
1591 lua_pushvalue( L2, -2); // ... u source u 1600 lua_pushvalue( L2, -2); // ... u source u
1592 lua_rawset( L2, L2_cache_i); // ... u 1601 lua_rawset( L2, L2_cache_i); // ... u
1593 // make sure we have the userdata now 1602 // make sure we have the userdata now
1594 if( LookupMode::ToKeeper == mode_) // ... userdata_clone_sentinel 1603 if (LookupMode::ToKeeper == mode) // ... userdata_clone_sentinel
1595 { 1604 {
1596 lua_getupvalue( L2, -1, 2); // ... userdata_clone_sentinel u 1605 lua_getupvalue(L2, -1, 2); // ... userdata_clone_sentinel u
1597 } 1606 }
1598 // assign uservalues 1607 // assign uservalues
1599 while( uvi > 0) 1608 while (uvi > 0)
1600 { 1609 {
1601 if (!inter_copy_one(U, L2, L2_cache_i, L, lua_absindex(L, -1), VT::NORMAL, mode_, upName_)) // ... u uv 1610 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
1611 if (!c.inter_copy_one()) // ... u uv
1602 { 1612 {
1603 luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1)); // doesn't return 1613 luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); // doesn't return
1604 } 1614 }
1605 lua_pop( L, 1); // ... mt __lanesclone [uv]* 1615 lua_pop(L1, 1); // ... mt __lanesclone [uv]*
1606 // this pops the value from the stack 1616 // this pops the value from the stack
1607 lua_setiuservalue( L2, -2, uvi); // ... u 1617 lua_setiuservalue(L2, -2, uvi); // ... u
1608 -- uvi; 1618 -- uvi;
1609 } 1619 }
1610 // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination 1620 // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination
1611 if( LookupMode::ToKeeper == mode_) // ... userdata_clone_sentinel u 1621 if (LookupMode::ToKeeper == mode) // ... userdata_clone_sentinel u
1612 { 1622 {
1613 lua_pop( L2, 1); // ... userdata_clone_sentinel 1623 lua_pop(L2, 1); // ... userdata_clone_sentinel
1614 } 1624 }
1615 STACK_CHECK( L2, 1); 1625 STACK_CHECK(L2, 1);
1616 STACK_CHECK( L, 2); 1626 STACK_CHECK(L1, 2);
1617 // call cloning function in source state to perform the actual memory cloning 1627 // call cloning function in source state to perform the actual memory cloning
1618 lua_pushlightuserdata( L, clone); // ... mt __lanesclone clone 1628 lua_pushlightuserdata(L1, clone); // ... mt __lanesclone clone
1619 lua_pushlightuserdata( L, source); // ... mt __lanesclone clone source 1629 lua_pushlightuserdata(L1, source); // ... mt __lanesclone clone source
1620 lua_pushinteger( L, userdata_size); // ... mt __lanesclone clone source size 1630 lua_pushinteger(L1, static_cast<lua_Integer>(userdata_size)); // ... mt __lanesclone clone source size
1621 lua_call( L, 3, 0); // ... mt 1631 lua_call(L1, 3, 0); // ... mt
1622 STACK_CHECK( L, 1); 1632 STACK_CHECK(L1, 1);
1623 } 1633 }
1624 1634
1625 STACK_CHECK( L2, 1); 1635 STACK_CHECK(L2, 1);
1626 lua_pop( L, 1); // ... 1636 lua_pop(L1, 1); // ...
1627 STACK_CHECK( L, 0); 1637 STACK_CHECK(L1, 0);
1628 return true; 1638 return true;
1629} 1639}
1630 1640
1631// ################################################################################################# 1641// #################################################################################################
1632 1642
1633[[nodiscard]] static bool inter_copy_userdata(Universe* U, Dest L2, int L2_cache_i, Source L, int i, VT vt_, LookupMode mode_, char const* upName_) 1643[[nodiscard]] bool InterCopyContext::inter_copy_userdata() const
1634{ 1644{
1635 STACK_CHECK_START_REL(L, 0); 1645 STACK_CHECK_START_REL(L1, 0);
1636 STACK_CHECK_START_REL(L2, 0); 1646 STACK_CHECK_START_REL(L2, 0);
1637 if (vt_ == VT::KEY) 1647 if (vt == VT::KEY)
1638 { 1648 {
1639 return false; 1649 return false;
1640 } 1650 }
1641 1651
1642 // try clonable userdata first 1652 // try clonable userdata first
1643 if( copyclone( U, L2, L2_cache_i, L, i, mode_, upName_)) 1653 if( copyclone())
1644 { 1654 {
1645 STACK_CHECK(L, 0); 1655 STACK_CHECK(L1, 0);
1646 STACK_CHECK(L2, 1); 1656 STACK_CHECK(L2, 1);
1647 return true; 1657 return true;
1648 } 1658 }
1649 1659
1650 STACK_CHECK(L, 0); 1660 STACK_CHECK(L1, 0);
1651 STACK_CHECK(L2, 0); 1661 STACK_CHECK(L2, 0);
1652 1662
1653 // Allow only deep userdata entities to be copied across 1663 // Allow only deep userdata entities to be copied across
1654 DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n")); 1664 DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n"));
1655 if (copydeep(U, L2, L2_cache_i, L, i, mode_, upName_)) 1665 if (copydeep())
1656 { 1666 {
1657 STACK_CHECK(L, 0); 1667 STACK_CHECK(L1, 0);
1658 STACK_CHECK(L2, 1); 1668 STACK_CHECK(L2, 1);
1659 return true; 1669 return true;
1660 } 1670 }
1661 1671
1662 STACK_CHECK(L, 0); 1672 STACK_CHECK(L1, 0);
1663 STACK_CHECK(L2, 0); 1673 STACK_CHECK(L2, 0);
1664 1674
1665 // Not a deep or clonable full userdata 1675 // Not a deep or clonable full userdata
1666 if (U->demoteFullUserdata) // attempt demotion to light userdata 1676 if (U->demoteFullUserdata) // attempt demotion to light userdata
1667 { 1677 {
1668 void* lud = lua_touserdata(L, i); 1678 void* const lud{ lua_touserdata(L1, L1_i) };
1669 lua_pushlightuserdata(L2, lud); 1679 lua_pushlightuserdata(L2, lud);
1670 } 1680 }
1671 else // raise an error 1681 else // raise an error
1672 { 1682 {
1673 luaL_error(L, "can't copy non-deep full userdata across lanes"); // doesn't return 1683 luaL_error(L1, "can't copy non-deep full userdata across lanes"); // doesn't return
1674 } 1684 }
1675 1685
1676 STACK_CHECK(L2, 1); 1686 STACK_CHECK(L2, 1);
1677 STACK_CHECK(L, 0); 1687 STACK_CHECK(L1, 0);
1678 return true; 1688 return true;
1679} 1689}
1680 1690
1681// ################################################################################################# 1691// #################################################################################################
1682 1692
1683[[nodiscard]] static bool inter_copy_function(Universe* U, Dest L2, int L2_cache_i, Source L, int source_i_, VT vt_, LookupMode mode_, char const* upName_) 1693[[nodiscard]] bool InterCopyContext::inter_copy_function() const
1684{ 1694{
1685 if (vt_ == VT::KEY) 1695 if (vt == VT::KEY)
1686 { 1696 {
1687 return false; 1697 return false;
1688 } 1698 }
1689 1699
1690 STACK_CHECK_START_REL(L, 0); // L (source) // L2 (destination) 1700 STACK_CHECK_START_REL(L1, 0); // L1 (source) // L2 (destination)
1691 STACK_CHECK_START_REL(L2, 0); 1701 STACK_CHECK_START_REL(L2, 0);
1692 DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", upName_)); 1702 DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
1693 1703
1694 if (lua_tocfunction(L, source_i_) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper 1704 if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper
1695 { 1705 {
1696 // clone the full userdata again 1706 // clone the full userdata again
1697 1707
1698 // let's see if we already restored this userdata 1708 // let's see if we already restored this userdata
1699 lua_getupvalue(L, source_i_, 2); // ... u 1709 lua_getupvalue(L1, L1_i, 2); // ... u
1700 void* source = lua_touserdata(L, -1); 1710 void* source = lua_touserdata(L1, -1);
1701 lua_pushlightuserdata(L2, source); // ... source 1711 lua_pushlightuserdata(L2, source); // ... source
1702 lua_rawget(L2, L2_cache_i); // ... u? 1712 lua_rawget(L2, L2_cache_i); // ... u?
1703 if (!lua_isnil(L2, -1)) 1713 if (!lua_isnil(L2, -1))
1704 { 1714 {
1705 lua_pop(L, 1); // ... 1715 lua_pop(L1, 1); // ...
1706 STACK_CHECK(L, 0); 1716 STACK_CHECK(L1, 0);
1707 STACK_CHECK(L2, 1); 1717 STACK_CHECK(L2, 1);
1708 return true; 1718 return true;
1709 } 1719 }
1710 lua_pop(L2, 1); // ... 1720 lua_pop(L2, 1); // ...
1711 1721
1712 // this function has 2 upvalues: the fqn of its metatable, and the userdata itself 1722 // this function has 2 upvalues: the fqn of its metatable, and the userdata itself
1713 std::ignore = lookup_table(L2, L, source_i_, mode_, upName_); // ... mt 1723 bool const found{ lookup_table(L2, L1, L1_i, mode, name) }; // ... mt?
1714 // originally 'source_i_' slot was the proxy closure, but from now on it indexes the actual userdata we extracted from it 1724 if (!found)
1715 source_i_ = lua_gettop(L); 1725 {
1716 source = lua_touserdata(L, -1); 1726 STACK_CHECK(L2, 0);
1727 return false;
1728 }
1729 // 'L1_i' slot was the proxy closure, but from now on we operate onthe actual userdata we extracted from it
1730 SourceIndex const source_i{ lua_gettop(L1) };
1731 source = lua_touserdata(L1, -1);
1717 void* clone{ nullptr }; 1732 void* clone{ nullptr };
1718 // get the number of bytes to allocate for the clone 1733 // get the number of bytes to allocate for the clone
1719 size_t const userdata_size{ lua_rawlen(L, -1) }; 1734 size_t const userdata_size{ lua_rawlen(L1, -1) };
1720 { 1735 {
1721 // extract uservalues (don't transfer them yet) 1736 // extract uservalues (don't transfer them yet)
1722 int uvi = 0; 1737 int uvi = 0;
1723 while (lua_getiuservalue(L, source_i_, ++uvi) != LUA_TNONE) {} // ... u uv 1738 while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // ... u uv
1724 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now 1739 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
1725 lua_pop(L, 1); // ... u [uv]* 1740 lua_pop(L1, 1); // ... u [uv]*
1726 --uvi; 1741 --uvi;
1727 STACK_CHECK(L, uvi + 1); 1742 STACK_CHECK(L1, uvi + 1);
1728 // create the clone userdata with the required number of uservalue slots 1743 // create the clone userdata with the required number of uservalue slots
1729 clone = lua_newuserdatauv(L2, userdata_size, uvi); // ... mt u 1744 clone = lua_newuserdatauv(L2, userdata_size, uvi); // ... mt u
1730 // add it in the cache 1745 // add it in the cache
@@ -1735,20 +1750,22 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1735 lua_pushvalue(L2, -2); // ... mt u mt 1750 lua_pushvalue(L2, -2); // ... mt u mt
1736 lua_setmetatable(L2, -2); // ... mt u 1751 lua_setmetatable(L2, -2); // ... mt u
1737 // transfer and assign uservalues 1752 // transfer and assign uservalues
1753 InterCopyContext c{ *this };
1738 while (uvi > 0) 1754 while (uvi > 0)
1739 { 1755 {
1740 if (!inter_copy_one(U, L2, L2_cache_i, L, lua_absindex(L, -1), vt_, mode_, upName_)) // ... mt u uv 1756 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
1757 if (!c.inter_copy_one()) // ... mt u uv
1741 { 1758 {
1742 luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1)); // doesn't return 1759 luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); // doesn't return
1743 } 1760 }
1744 lua_pop(L, 1); // ... u [uv]* 1761 lua_pop(L1, 1); // ... u [uv]*
1745 // this pops the value from the stack 1762 // this pops the value from the stack
1746 lua_setiuservalue(L2, -2, uvi); // ... mt u 1763 lua_setiuservalue(L2, -2, uvi); // ... mt u
1747 -- uvi; 1764 -- uvi;
1748 } 1765 }
1749 // when we are done, all uservalues are popped from the stack, we can pop the source as well 1766 // when we are done, all uservalues are popped from the stack, we can pop the source as well
1750 lua_pop(L, 1); // ... 1767 lua_pop(L1, 1); // ...
1751 STACK_CHECK(L, 0); 1768 STACK_CHECK(L1, 0);
1752 STACK_CHECK(L2, 2); // ... mt u 1769 STACK_CHECK(L2, 2); // ... mt u
1753 } 1770 }
1754 // perform the custom cloning part 1771 // perform the custom cloning part
@@ -1764,36 +1781,36 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1764 } 1781 }
1765 else // regular function 1782 else // regular function
1766 { 1783 {
1767 DEBUGSPEW_CODE(fprintf( stderr, "FUNCTION %s\n", upName_)); 1784 DEBUGSPEW_CODE(fprintf( stderr, "FUNCTION %s\n", name));
1768 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1785 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1769 copy_cached_func(U, L2, L2_cache_i, L, source_i_, mode_, upName_); // ... f 1786 copy_cached_func(); // ... f
1770 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); 1787 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1771 } 1788 }
1772 STACK_CHECK(L2, 1); 1789 STACK_CHECK(L2, 1);
1773 STACK_CHECK(L, 0); 1790 STACK_CHECK(L1, 0);
1774 return true; 1791 return true;
1775} 1792}
1776 1793
1777// ################################################################################################# 1794// #################################################################################################
1778 1795
1779[[nodiscard]] static bool inter_copy_table(Universe* U, Dest L2, int L2_cache_i, Source L, int i, VT vt_, LookupMode mode_, char const* upName_) 1796[[nodiscard]] bool InterCopyContext::inter_copy_table() const
1780{ 1797{
1781 if (vt_ == VT::KEY) 1798 if (vt == VT::KEY)
1782 { 1799 {
1783 return false; 1800 return false;
1784 } 1801 }
1785 1802
1786 STACK_CHECK_START_REL(L, 0); 1803 STACK_CHECK_START_REL(L1, 0);
1787 STACK_CHECK_START_REL(L2, 0); 1804 STACK_CHECK_START_REL(L2, 0);
1788 DEBUGSPEW_CODE(fprintf(stderr, "TABLE %s\n", upName_)); 1805 DEBUGSPEW_CODE(fprintf(stderr, "TABLE %s\n", name));
1789 1806
1790 /* 1807 /*
1791 * First, let's try to see if this table is special (aka is it some table that we registered in our lookup databases during module registration?) 1808 * First, let's try to see if this table is special (aka is it some table that we registered in our lookup databases during module registration?)
1792 * Note that this table CAN be a module table, but we just didn't register it, in which case we'll send it through the table cloning mechanism 1809 * Note that this table CAN be a module table, but we just didn't register it, in which case we'll send it through the table cloning mechanism
1793 */ 1810 */
1794 if (lookup_table(L2, L, i, mode_, upName_)) 1811 if (lookup_table(L2, L1, L1_i, mode, name))
1795 { 1812 {
1796 ASSERT_L(lua_istable(L2, -1) || (lua_tocfunction(L2, -1) == table_lookup_sentinel)); // from lookup data. can also be table_lookup_sentinel if this is a table we know 1813 _ASSERT_L(L1, lua_istable(L2, -1) || (lua_tocfunction(L2, -1) == table_lookup_sentinel)); // from lookup data. can also be table_lookup_sentinel if this is a table we know
1797 return true; 1814 return true;
1798 } 1815 }
1799 1816
@@ -1806,40 +1823,40 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1806 * Note: Even metatables need to go through this test; to detect 1823 * Note: Even metatables need to go through this test; to detect
1807 * loops such as those in required module tables (getmetatable(lanes).lanes == lanes) 1824 * loops such as those in required module tables (getmetatable(lanes).lanes == lanes)
1808 */ 1825 */
1809 if (push_cached_table(L2, L2_cache_i, L, i)) 1826 if (push_cached_table(L2, L2_cache_i, L1, L1_i))
1810 { 1827 {
1811 ASSERT_L(lua_istable(L2, -1)); // from cache 1828 _ASSERT_L(L1, lua_istable(L2, -1)); // from cache
1812 return true; 1829 return true;
1813 } 1830 }
1814 ASSERT_L(lua_istable(L2, -1)); 1831 _ASSERT_L(L1, lua_istable(L2, -1));
1815 1832
1816 STACK_GROW(L, 2); 1833 STACK_GROW(L1, 2);
1817 STACK_GROW(L2, 2); 1834 STACK_GROW(L2, 2);
1818 1835
1819 lua_pushnil(L); // start iteration 1836 lua_pushnil(L1); // start iteration
1820 while (lua_next(L, i)) 1837 while (lua_next(L1, L1_i))
1821 { 1838 {
1822 // need a function to prevent overflowing the stack with verboseErrors-induced alloca() 1839 // need a function to prevent overflowing the stack with verboseErrors-induced alloca()
1823 inter_copy_keyvaluepair(U, L2, L2_cache_i, L, vt_, mode_, upName_); 1840 inter_copy_keyvaluepair();
1824 lua_pop(L, 1); // pop value (next round) 1841 lua_pop(L1, 1); // pop value (next round)
1825 } 1842 }
1826 STACK_CHECK(L, 0); 1843 STACK_CHECK(L1, 0);
1827 STACK_CHECK(L2, 1); 1844 STACK_CHECK(L2, 1);
1828 1845
1829 // Metatables are expected to be immutable, and copied only once. 1846 // Metatables are expected to be immutable, and copied only once.
1830 if (push_cached_metatable(U, L2, L2_cache_i, L, i, mode_, upName_)) // ... t mt? 1847 if (push_cached_metatable()) // ... t mt?
1831 { 1848 {
1832 lua_setmetatable(L2, -2); // ... t 1849 lua_setmetatable(L2, -2); // ... t
1833 } 1850 }
1834 STACK_CHECK(L2, 1); 1851 STACK_CHECK(L2, 1);
1835 STACK_CHECK(L, 0); 1852 STACK_CHECK(L1, 0);
1836 return true; 1853 return true;
1837} 1854}
1838 1855
1839// ################################################################################################# 1856// #################################################################################################
1840 1857
1841/* 1858/*
1842* Copies a value from 'L' state (at index 'i') to 'L2' state. Does not remove 1859* Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove
1843* the original value. 1860* the original value.
1844* 1861*
1845* NOTE: Both the states must be solely in the current OS thread's possession. 1862* NOTE: Both the states must be solely in the current OS thread's possession.
@@ -1848,92 +1865,92 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1848* 1865*
1849* Returns true if value was pushed, false if its type is non-supported. 1866* Returns true if value was pushed, false if its type is non-supported.
1850*/ 1867*/
1851[[nodiscard]] bool inter_copy_one(Universe* U, Dest L2, int L2_cache_i, Source L, int i, VT vt_, LookupMode mode_, char const* upName_) 1868[[nodiscard]] bool InterCopyContext::inter_copy_one() const
1852{ 1869{
1853 bool ret{ true };
1854 LuaType val_type{ lua_type_as_enum(L, i) };
1855 static constexpr int pod_mask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING); 1870 static constexpr int pod_mask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING);
1856 STACK_GROW( L2, 1); 1871 STACK_GROW(L2, 1);
1857 STACK_CHECK_START_REL(L, 0); // L // L2 1872 STACK_CHECK_START_REL(L1, 0); // L1 // L2
1858 STACK_CHECK_START_REL(L2, 0); // L // L2 1873 STACK_CHECK_START_REL(L2, 0); // L1 // L2
1859 1874
1860 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END)); 1875 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END));
1861 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1876 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
1862 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[val_type], vt_names[static_cast<int>(vt_)])); 1877 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[val_type], vt_names[static_cast<int>(vt)]));
1863 1878
1864 // Non-POD can be skipped if its metatable contains { __lanesignore = true } 1879 // Non-POD can be skipped if its metatable contains { __lanesignore = true }
1880 LuaType val_type{ lua_type_as_enum(L1, L1_i) };
1865 if( ((1 << static_cast<int>(val_type)) & pod_mask) == 0) 1881 if( ((1 << static_cast<int>(val_type)) & pod_mask) == 0)
1866 { 1882 {
1867 if( lua_getmetatable( L, i)) // ... mt 1883 if (lua_getmetatable(L1, L1_i)) // ... mt
1868 { 1884 {
1869 lua_getfield( L, -1, "__lanesignore"); // ... mt ignore? 1885 lua_getfield(L1, -1, "__lanesignore"); // ... mt ignore?
1870 if( lua_isboolean( L, -1) && lua_toboolean( L, -1)) 1886 if( lua_isboolean(L1, -1) && lua_toboolean(L1, -1))
1871 { 1887 {
1872 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END)); 1888 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END));
1873 val_type = LuaType::NIL; 1889 val_type = LuaType::NIL;
1874 } 1890 }
1875 lua_pop( L, 2); // ... 1891 lua_pop(L1, 2); // ...
1876 } 1892 }
1877 } 1893 }
1878 STACK_CHECK( L, 0); 1894 STACK_CHECK(L1, 0);
1879 1895
1880 /* Lets push nil to L2 if the object should be ignored */ 1896 /* Lets push nil to L2 if the object should be ignored */
1881 switch( val_type) 1897 bool ret{ true };
1898 switch (val_type)
1882 { 1899 {
1883 /* Basic types allowed both as values, and as table keys */ 1900 /* Basic types allowed both as values, and as table keys */
1884 1901
1885 case LuaType::BOOLEAN: 1902 case LuaType::BOOLEAN:
1886 { 1903 {
1887 int const v{ lua_toboolean(L, i) }; 1904 int const v{ lua_toboolean(L1, L1_i) };
1888 DEBUGSPEW_CODE( fprintf( stderr, "%s\n", v ? "true" : "false")); 1905 DEBUGSPEW_CODE( fprintf(stderr, "%s\n", v ? "true" : "false"));
1889 lua_pushboolean( L2, v); 1906 lua_pushboolean(L2, v);
1890 } 1907 }
1891 break; 1908 break;
1892 1909
1893 case LuaType::NUMBER: 1910 case LuaType::NUMBER:
1894 /* LNUM patch support (keeping integer accuracy) */ 1911 /* LNUM patch support (keeping integer accuracy) */
1895#if defined LUA_LNUM || LUA_VERSION_NUM >= 503 1912#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
1896 if( lua_isinteger( L, i)) 1913 if( lua_isinteger(L1, L1_i))
1897 { 1914 {
1898 lua_Integer v = lua_tointeger( L, i); 1915 lua_Integer const v{ lua_tointeger(L1, L1_i) };
1899 DEBUGSPEW_CODE( fprintf( stderr, LUA_INTEGER_FMT "\n", v)); 1916 DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v));
1900 lua_pushinteger( L2, v); 1917 lua_pushinteger(L2, v);
1901 break; 1918 break;
1902 } 1919 }
1903 else 1920 else
1904#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 1921#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
1905 { 1922 {
1906 lua_Number v = lua_tonumber( L, i); 1923 lua_Number const v{ lua_tonumber(L1, L1_i) };
1907 DEBUGSPEW_CODE( fprintf( stderr, LUA_NUMBER_FMT "\n", v)); 1924 DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", v));
1908 lua_pushnumber( L2, v); 1925 lua_pushnumber(L2, v);
1909 } 1926 }
1910 break; 1927 break;
1911 1928
1912 case LuaType::STRING: 1929 case LuaType::STRING:
1913 { 1930 {
1914 size_t len; 1931 size_t len;
1915 char const* s = lua_tolstring( L, i, &len); 1932 char const* const s{ lua_tolstring(L1, L1_i, &len) };
1916 DEBUGSPEW_CODE( fprintf( stderr, "'%s'\n", s)); 1933 DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", s));
1917 lua_pushlstring( L2, s, len); 1934 lua_pushlstring(L2, s, len);
1918 } 1935 }
1919 break; 1936 break;
1920 1937
1921 case LuaType::LIGHTUSERDATA: 1938 case LuaType::LIGHTUSERDATA:
1922 { 1939 {
1923 void* p = lua_touserdata( L, i); 1940 void* const p{ lua_touserdata(L1, L1_i) };
1924 DEBUGSPEW_CODE( fprintf( stderr, "%p\n", p)); 1941 DEBUGSPEW_CODE(fprintf(stderr, "%p\n", p));
1925 lua_pushlightuserdata( L2, p); 1942 lua_pushlightuserdata(L2, p);
1926 } 1943 }
1927 break; 1944 break;
1928 1945
1929 /* The following types are not allowed as table keys */ 1946 /* The following types are not allowed as table keys */
1930 1947
1931 case LuaType::USERDATA: 1948 case LuaType::USERDATA:
1932 ret = inter_copy_userdata(U, L2, L2_cache_i, L, i, vt_, mode_, upName_); 1949 ret = inter_copy_userdata();
1933 break; 1950 break;
1934 1951
1935 case LuaType::NIL: 1952 case LuaType::NIL:
1936 if (vt_ == VT::KEY) 1953 if (vt == VT::KEY)
1937 { 1954 {
1938 ret = false; 1955 ret = false;
1939 break; 1956 break;
@@ -1942,11 +1959,11 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1942 break; 1959 break;
1943 1960
1944 case LuaType::FUNCTION: 1961 case LuaType::FUNCTION:
1945 ret = inter_copy_function(U, L2, L2_cache_i, L, i, vt_, mode_, upName_); 1962 ret = inter_copy_function();
1946 break; 1963 break;
1947 1964
1948 case LuaType::TABLE: 1965 case LuaType::TABLE:
1949 ret = inter_copy_table(U, L2, L2_cache_i, L, i, vt_, mode_, upName_); 1966 ret = inter_copy_table();
1950 break; 1967 break;
1951 1968
1952 /* The following types cannot be copied */ 1969 /* The following types cannot be copied */
@@ -1959,8 +1976,8 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1959 1976
1960 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed)); 1977 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_sub(1, std::memory_order_relaxed));
1961 1978
1962 STACK_CHECK( L2, ret ? 1 : 0); 1979 STACK_CHECK(L2, ret ? 1 : 0);
1963 STACK_CHECK( L, 0); 1980 STACK_CHECK(L1, 0);
1964 return ret; 1981 return ret;
1965} 1982}
1966 1983
@@ -1976,9 +1993,6 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1976[[nodiscard]] InterCopyResult luaG_inter_copy(Universe* U, Source L, Dest L2, int n, LookupMode mode_) 1993[[nodiscard]] InterCopyResult luaG_inter_copy(Universe* U, Source L, Dest L2, int n, LookupMode mode_)
1977{ 1994{
1978 int const top_L{ lua_gettop(L) }; // ... {}n 1995 int const top_L{ lua_gettop(L) }; // ... {}n
1979 int const top_L2{ lua_gettop(L2) }; // ...
1980 char tmpBuf[16];
1981 char const* pBuf{ U->verboseErrors ? tmpBuf : "?" };
1982 1996
1983 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "luaG_inter_copy()\n" INDENT_END)); 1997 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "luaG_inter_copy()\n" INDENT_END));
1984 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed)); 1998 DEBUGSPEW_CODE(U->debugspew_indent_depth.fetch_add(1, std::memory_order_relaxed));
@@ -1999,17 +2013,22 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1999 * function entries, avoiding the same entries to be passed on as multiple 2013 * function entries, avoiding the same entries to be passed on as multiple
2000 * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner! 2014 * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
2001 */ 2015 */
2016 int const top_L2{ lua_gettop(L2) }; // ...
2002 lua_newtable(L2); // ... cache 2017 lua_newtable(L2); // ... cache
2003 2018
2004 STACK_CHECK_START_REL(L, 0); 2019 char tmpBuf[16];
2020 char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" };
2021 InterCopyContext c{ U, L2, L, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode_, pBuf };
2005 bool copyok{ true }; 2022 bool copyok{ true };
2023 STACK_CHECK_START_REL(L, 0);
2006 for (int i = top_L - n + 1, j = 1; i <= top_L; ++i, ++j) 2024 for (int i = top_L - n + 1, j = 1; i <= top_L; ++i, ++j)
2007 { 2025 {
2008 if (U->verboseErrors) 2026 if (U->verboseErrors)
2009 { 2027 {
2010 sprintf(tmpBuf, "arg_%d", j); 2028 sprintf(tmpBuf, "arg_%d", j);
2011 } 2029 }
2012 copyok = inter_copy_one(U, L2, top_L2 + 1, L, i, VT::NORMAL, mode_, pBuf); // ... cache {}n 2030 c.L1_i = SourceIndex{ i };
2031 copyok = c.inter_copy_one(); // ... cache {}n
2013 if (!copyok) 2032 if (!copyok)
2014 { 2033 {
2015 break; 2034 break;
diff --git a/src/tools.h b/src/tools.h
index dce7378..e14ac65 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -31,7 +31,36 @@ enum class InterCopyResult
31 Error 31 Error
32}; 32};
33 33
34[[nodiscard]] bool inter_copy_one(Universe* U, Dest L2, int L2_cache_i, Source L, int i, VT vt_, LookupMode mode_, char const* upName_); 34// ################################################################################################
35
36using CacheIndex = Unique<int>;
37using SourceIndex = Unique<int>;
38struct InterCopyContext
39{
40
41 Universe* const U;
42 Dest const L2;
43 Source const L1;
44 CacheIndex const L2_cache_i;
45 SourceIndex L1_i; // that one can change when we reuse the context
46 VT vt; // that one can change when we reuse the context
47 LookupMode const mode;
48 char const* name; // that one can change when we reuse the context
49
50 [[nodiscard]] bool inter_copy_one() const;
51
52 private:
53
54 [[nodiscard]] bool inter_copy_userdata() const;
55 [[nodiscard]] bool inter_copy_function() const;
56 [[nodiscard]] bool inter_copy_table() const;
57 [[nodiscard]] bool copyclone() const;
58 [[nodiscard]] bool copydeep() const;
59 [[nodiscard]] bool push_cached_metatable() const;
60 void copy_func() const;
61 void copy_cached_func() const;
62 void inter_copy_keyvaluepair() const;
63};
35 64
36// ################################################################################################ 65// ################################################################################################
37 66
diff --git a/tests/rupval.lua b/tests/rupval.lua
index 7b5a270..1079168 100644
--- a/tests/rupval.lua
+++ b/tests/rupval.lua
@@ -2,24 +2,29 @@ lanes = require "lanes".configure{with_timers=false}
2 2
3-- make sure we can copy functions with interdependant upvalues 3-- make sure we can copy functions with interdependant upvalues
4 4
5local x = 33
6local y = 44
7local z = 55
8
5local b 9local b
6local a = function( n) 10local a = function( n)
7 print( "a", n) 11 print( "a", n)
8 return n <= 0 and n or b( n-1) 12 return n <= 0 and x or b( n-1)
9end 13end
10 14
11local c 15local c
12b = function( n) 16b = function( n)
13 print( "b", n) 17 print( "b", n)
14 return n <= 0 and n or c( n-1) 18 return n <= 0 and y or c( n-1)
15end 19end
16 20
17c = function( n) 21c = function( n)
18 print( "c", n) 22 print( "c", n)
19 return n <= 0 and n or a( n-1) 23 return n <= 0 and z or a( n-1)
20end 24end
21 25
22local g = lanes.gen( "*", a) 26local g = lanes.gen( "base", a)
23 27
24local l = g(10) 28local l = g(10)
25l:join() 29local r = l:join()
30print(r)