aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-05-13 15:03:12 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-05-13 18:15:46 +0200
commitc46869699aa3ae477516fba0043c2cfd8cda822a (patch)
tree2a63c9302f530a4da5ee3121eb2a545c39e0fe20 /src
parent13f7f505375f7c1afd3a7e479a64cc147501b01d (diff)
downloadlanes-c46869699aa3ae477516fba0043c2cfd8cda822a.tar.gz
lanes-c46869699aa3ae477516fba0043c2cfd8cda822a.tar.bz2
lanes-c46869699aa3ae477516fba0043c2cfd8cda822a.zip
Move InterCopyContext implementation in a separate file
Diffstat (limited to 'src')
-rw-r--r--src/Makefile18
-rw-r--r--src/cancel.cpp2
-rw-r--r--src/compat.cpp7
-rw-r--r--src/deep.cpp56
-rw-r--r--src/deep.h10
-rw-r--r--src/intercopycontext.cpp1296
-rw-r--r--src/intercopycontext.h77
-rw-r--r--src/keeper.cpp5
-rw-r--r--src/keeper.h1
-rw-r--r--src/lanes.cpp81
-rw-r--r--src/linda.cpp4
-rw-r--r--src/lindafactory.cpp1
-rw-r--r--src/macros_and_utils.h2
-rw-r--r--src/state.cpp1
-rw-r--r--src/tools.cpp1309
-rw-r--r--src/tools.h78
-rw-r--r--src/universe.cpp3
-rw-r--r--src/universe.h1
18 files changed, 1488 insertions, 1464 deletions
diff --git a/src/Makefile b/src/Makefile
index 06bbcd0..bbe7b23 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -7,7 +7,7 @@
7 7
8MODULE=lanes 8MODULE=lanes
9 9
10SRC=lanes.c cancel.cpp compat.cpp threading.cpp tools.cpp state.cpp linda.cpp lindafactory.cpp deep.cpp keeper.cpp universe.cpp 10SRC=lanes.c cancel.cpp compat.cpp threading.cpp tools.cpp state.cpp linda.cpp lindafactory.cpp deep.cpp keeper.cpp universe.cpp intercopycontext.cpp
11 11
12OBJ=$(SRC:.c=.o) 12OBJ=$(SRC:.c=.o)
13 13
@@ -124,17 +124,17 @@ all: $(MODULE)/core.$(_SO)
124# Note: Don't put $(LUA_LIBS) ahead of $^; MSYS will not like that (I think) 124# Note: Don't put $(LUA_LIBS) ahead of $^; MSYS will not like that (I think)
125# 125#
126$(MODULE_DIR)/core.$(_SO): $(OBJ) 126$(MODULE_DIR)/core.$(_SO): $(OBJ)
127 mkdir -p $(MODULE_DIR) 127 mkdir -p $(MODULE_DIR)
128 $(CC) $(LIBFLAG) $^ $(LIBS) $(LUA_LIBS) -o $@ 128 $(CC) $(LIBFLAG) $^ $(LIBS) $(LUA_LIBS) -o $@
129 129
130clean: 130clean:
131 -rm -rf $(MODULE)/core.$(_SO) *.o *.map 131 -rm -rf $(MODULE)/core.$(_SO) *.o *.map
132 132
133#--- 133#---
134# NSLU2 "slug" Linux ARM 134# NSLU2 "slug" Linux ARM
135# 135#
136nslu2: 136nslu2:
137 $(MAKE) all CFLAGS="$(CFLAGS) -I/opt/include -L/opt/lib -D_GNU_SOURCE -lpthread" 137 $(MAKE) all CFLAGS="$(CFLAGS) -I/opt/include -L/opt/lib -D_GNU_SOURCE -lpthread"
138 138
139#--- 139#---
140# Cross compiling to Win32 (MinGW on OS X Intel) 140# Cross compiling to Win32 (MinGW on OS X Intel)
@@ -150,15 +150,15 @@ MINGW_GCC=mingw32-gcc
150 # i686-pc-mingw32-gcc 150 # i686-pc-mingw32-gcc
151 151
152win32: $(WIN32_LUA51)/include/lua.h 152win32: $(WIN32_LUA51)/include/lua.h
153 $(MAKE) build CC=$(MINGW_GCC) \ 153 $(MAKE) build CC=$(MINGW_GCC) \
154 LUA_FLAGS=-I$(WIN32_LUA51)/include \ 154 LUA_FLAGS=-I$(WIN32_LUA51)/include \
155 LUA_LIBS="-L$(WIN32_LUA51) -llua51" \ 155 LUA_LIBS="-L$(WIN32_LUA51) -llua51" \
156 _SO=dll \ 156 _SO=dll \
157 SO_FLAGS=-shared 157 SO_FLAGS=-shared
158 158
159$(WIN32_LUA51)/include/lua.h: 159$(WIN32_LUA51)/include/lua.h:
160 @echo "Usage: make win32 WIN32_LUA51=<path of extracted LuaBinaries dll8 and dev packages>" 160 @echo "Usage: make win32 WIN32_LUA51=<path of extracted LuaBinaries dll8 and dev packages>"
161 @echo " [MINGW_GCC=...mingw32-gcc]" 161 @echo " [MINGW_GCC=...mingw32-gcc]"
162 @false 162 @false
163 163
164.PROXY: all clean nslu2 win32 164.PROXY: all clean nslu2 win32
diff --git a/src/cancel.cpp b/src/cancel.cpp
index fe1623b..8356169 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -36,8 +36,6 @@ THE SOFTWARE.
36#include "cancel.h" 36#include "cancel.h"
37 37
38#include "lanes_private.h" 38#include "lanes_private.h"
39#include "threading.h"
40#include "tools.h"
41 39
42// ################################################################################################# 40// #################################################################################################
43// ################################################################################################# 41// #################################################################################################
diff --git a/src/compat.cpp b/src/compat.cpp
index 336f716..4e8025e 100644
--- a/src/compat.cpp
+++ b/src/compat.cpp
@@ -1,9 +1,12 @@
1#include "compat.h"
2
3#include "macros_and_utils.h"
4
5
1// ################################################################################################# 6// #################################################################################################
2// ###################################### Lua 5.1 / 5.2 / 5.3 ###################################### 7// ###################################### Lua 5.1 / 5.2 / 5.3 ######################################
3// ################################################################################################# 8// #################################################################################################
4 9
5#include "compat.h"
6#include "macros_and_utils.h"
7 10
8// ################################################################################################# 11// #################################################################################################
9 12
diff --git a/src/deep.cpp b/src/deep.cpp
index 6570e55..e0c2a39 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -34,10 +34,7 @@ THE SOFTWARE.
34 34
35#include "deep.h" 35#include "deep.h"
36 36
37#include "compat.h"
38#include "tools.h" 37#include "tools.h"
39#include "uniquekey.h"
40#include "universe.h"
41 38
42#include <bit> 39#include <bit>
43#include <cassert> 40#include <cassert>
@@ -105,7 +102,7 @@ static void LookupDeep(lua_State* L_)
105// ################################################################################################# 102// #################################################################################################
106 103
107// Return the registered factory for 'index' (deep userdata proxy), or nullptr if 'index' is not a deep userdata proxy. 104// Return the registered factory for 'index' (deep userdata proxy), or nullptr if 'index' is not a deep userdata proxy.
108[[nodiscard]] static inline DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_) 105[[nodiscard]] DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_)
109{ 106{
110 // when looking inside a keeper, we are 100% sure the object is a deep userdata 107 // when looking inside a keeper, we are 100% sure the object is a deep userdata
111 if (mode_ == LookupMode::FromKeeper) { 108 if (mode_ == LookupMode::FromKeeper) {
@@ -386,54 +383,3 @@ DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const
386 DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L_, index_) }; 383 DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L_, index_) };
387 return *proxy; 384 return *proxy;
388} 385}
389
390// #################################################################################################
391
392// Copy deep userdata between two separate Lua states (from L1 to L2)
393// Returns false if not a deep userdata, else true (unless an error occured)
394[[nodiscard]] bool InterCopyContext::tryCopyDeep() const
395{
396 DeepFactory* const factory{ LookupFactory(L1, L1_i, mode) };
397 if (factory == nullptr) {
398 return false; // not a deep userdata
399 }
400
401 STACK_CHECK_START_REL(L1, 0);
402 STACK_CHECK_START_REL(L2, 0);
403
404 // extract all uservalues of the source. unfortunately, the only way to know their count is to iterate until we fail
405 int nuv = 0;
406 while (lua_getiuservalue(L1, L1_i, nuv + 1) != LUA_TNONE) { // L1: ... u [uv]* nil
407 ++nuv;
408 }
409 // last call returned TNONE and pushed nil, that we don't need
410 lua_pop(L1, 1); // L1: ... u [uv]*
411 STACK_CHECK(L1, nuv);
412
413 DeepPrelude* const u{ *lua_tofulluserdata<DeepPrelude*>(L1, L1_i) };
414 char const* errmsg{ DeepFactory::PushDeepProxy(L2, u, nuv, mode) }; // L1: ... u [uv]* L2: u
415 if (errmsg != nullptr) {
416 raise_luaL_error(getErrL(), errmsg);
417 }
418
419 // transfer all uservalues of the source in the destination
420 {
421 InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name };
422 int const clone_i{ lua_gettop(L2) };
423 while (nuv) {
424 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
425 if (!c.inter_copy_one()) { // L1: ... u [uv]* L2: u uv
426 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
427 }
428 lua_pop(L1, 1); // L1: ... u [uv]*
429 // this pops the value from the stack
430 lua_setiuservalue(L2, clone_i, nuv); // L2: u
431 --nuv;
432 }
433 }
434
435 STACK_CHECK(L2, 1);
436 STACK_CHECK(L1, 0);
437
438 return true;
439} \ No newline at end of file
diff --git a/src/deep.h b/src/deep.h
index 784952c..41df86f 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -20,15 +20,9 @@ extern "C"
20#include <atomic> 20#include <atomic>
21 21
22// forwards 22// forwards
23enum class LookupMode;
23class Universe; 24class Universe;
24 25
25enum class LookupMode
26{
27 LaneBody, // send the lane body directly from the source to the destination lane. keep this one first so that it's the value we get when we default-construct
28 ToKeeper, // send a function from a lane to a keeper state
29 FromKeeper // send a function from a keeper state to a lane
30};
31
32// ################################################################################################# 26// #################################################################################################
33 27
34// xxh64 of string "kDeepVersion_1" generated at https://www.pelock.com/products/hash-calculator 28// xxh64 of string "kDeepVersion_1" generated at https://www.pelock.com/products/hash-calculator
@@ -80,3 +74,5 @@ class DeepFactory
80 static void DeleteDeepObject(lua_State* L_, DeepPrelude* o_); 74 static void DeleteDeepObject(lua_State* L_, DeepPrelude* o_);
81 [[nodiscard]] static char const* PushDeepProxy(DestState L_, DeepPrelude* o_, int nuv_, LookupMode mode_); 75 [[nodiscard]] static char const* PushDeepProxy(DestState L_, DeepPrelude* o_, int nuv_, LookupMode mode_);
82}; 76};
77
78[[nodiscard]] DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_);
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp
new file mode 100644
index 0000000..07fcd77
--- /dev/null
+++ b/src/intercopycontext.cpp
@@ -0,0 +1,1296 @@
1/*
2===============================================================================
3
4Copyright (C) 2024 benoit Germain <bnt.germain@gmail.com>
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in
14all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22THE SOFTWARE.
23
24===============================================================================
25*/
26
27#include "intercopycontext.h"
28
29#include "deep.h"
30#include "universe.h"
31
32// #################################################################################################
33
34// Lua 5.4.3 style of dumping (see lstrlib.c)
35// we have to do it that way because we can't unbalance the stack between buffer operations
36// namely, this means we can't push a function on top of the stack *after* we initialize the buffer!
37// luckily, this also works with earlier Lua versions
38[[nodiscard]] static int buf_writer(lua_State* L_, void const* b_, size_t size_, void* ud_)
39{
40 luaL_Buffer* const B{ static_cast<luaL_Buffer*>(ud_) };
41 if (!B->L) {
42 luaL_buffinit(L_, B);
43 }
44 luaL_addlstring(B, static_cast<char const*>(b_), size_);
45 return 0;
46}
47
48// #################################################################################################
49
50// function sentinel used to transfer native functions from/to keeper states
51[[nodiscard]] static int func_lookup_sentinel(lua_State* L_)
52{
53 raise_luaL_error(L_, "function lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
54}
55
56// #################################################################################################
57
58// function sentinel used to transfer native table from/to keeper states
59[[nodiscard]] static int table_lookup_sentinel(lua_State* L_)
60{
61 raise_luaL_error(L_, "table lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
62}
63
64// #################################################################################################
65
66// function sentinel used to transfer cloned full userdata from/to keeper states
67[[nodiscard]] static int userdata_clone_sentinel(lua_State* L_)
68{
69 raise_luaL_error(L_, "userdata clone sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
70}
71
72// #################################################################################################
73
74// retrieve the name of a function/table in the lookup database
75[[nodiscard]] static char const* find_lookup_name(lua_State* L_, int i_, LookupMode mode_, char const* upName_, size_t* len_)
76{
77 LUA_ASSERT(L_, lua_isfunction(L_, i_) || lua_istable(L_, i_)); // L_: ... v ...
78 STACK_CHECK_START_REL(L_, 0);
79 STACK_GROW(L_, 3); // up to 3 slots are necessary on error
80 if (mode_ == LookupMode::FromKeeper) {
81 lua_CFunction f = lua_tocfunction(L_, i_); // should *always* be one of the function sentinels
82 if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) {
83 lua_getupvalue(L_, i_, 1); // L_: ... v ... "f.q.n"
84 } else {
85 // if this is not a sentinel, this is some user-created table we wanted to lookup
86 LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i_));
87 // push anything that will convert to nullptr string
88 lua_pushnil(L_); // L_: ... v ... nil
89 }
90 } else {
91 // fetch the name from the source state's lookup table
92 kLookupRegKey.pushValue(L_); // L_: ... v ... {}
93 STACK_CHECK(L_, 1);
94 LUA_ASSERT(L_, lua_istable(L_, -1));
95 lua_pushvalue(L_, i_); // L_: ... v ... {} v
96 lua_rawget(L_, -2); // L_: ... v ... {} "f.q.n"
97 }
98 char const* fqn{ lua_tolstring(L_, -1, len_) };
99 DEBUGSPEW_CODE(Universe* const U = universe_get(L_));
100 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END(U), fqn));
101 // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database
102 lua_pop(L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // L_: ... v ...
103 STACK_CHECK(L_, 0);
104 if (nullptr == fqn && !lua_istable(L_, i_)) { // raise an error if we try to send an unknown function (but not for tables)
105 *len_ = 0; // just in case
106 // try to discover the name of the function we want to send
107 lua_getglobal(L_, "decoda_name"); // L_: ... v ... decoda_name
108 char const* from{ lua_tostring(L_, -1) };
109 lua_pushcfunction(L_, luaG_nameof); // L_: ... v ... decoda_name luaG_nameof
110 lua_pushvalue(L_, i_); // L_: ... v ... decoda_name luaG_nameof t
111 lua_call(L_, 1, 2); // L_: ... v ... decoda_name "type" "name"|nil
112 char const* typewhat{ (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : luaL_typename(L_, -2) };
113 // second return value can be nil if the table was not found
114 // probable reason: the function was removed from the source Lua state before Lanes was required.
115 char const *what, *gotchaA, *gotchaB;
116 if (lua_isnil(L_, -1)) {
117 gotchaA = " referenced by";
118 gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)";
119 what = upName_;
120 } else {
121 gotchaA = "";
122 gotchaB = "";
123 what = (lua_type(L_, -1) == LUA_TSTRING) ? lua_tostring(L_, -1) : luaL_typename(L_, -1);
124 }
125 raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB);
126 }
127 STACK_CHECK(L_, 0);
128 return fqn;
129}
130
131// #################################################################################################
132
133/*---=== Inter-state copying ===---*/
134
135// xxh64 of string "kMtIdRegKey" generated at https://www.pelock.com/products/hash-calculator
136static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull };
137
138// get a unique ID for metatable at [i].
139[[nodiscard]] static lua_Integer get_mt_id(Universe* U_, lua_State* L_, int idx_)
140{
141 idx_ = lua_absindex(L_, idx_);
142
143 STACK_GROW(L_, 3);
144
145 STACK_CHECK_START_REL(L_, 0);
146 std::ignore = kMtIdRegKey.getSubTable(L_, 0, 0); // L_: ... _R[kMtIdRegKey]
147 lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
148 lua_rawget(L_, -2); // L_: ... _R[kMtIdRegKey] mtk?
149
150 lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil
151 lua_pop(L_, 1); // L_: ... _R[kMtIdRegKey]
152 STACK_CHECK(L_, 1);
153
154 if (id == 0) {
155 id = U_->nextMetatableId.fetch_add(1, std::memory_order_relaxed);
156
157 // Create two-way references: id_uint <-> table
158 lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
159 lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] {mt} id
160 lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
161
162 lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] id
163 lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] id {mt}
164 lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
165 }
166 lua_pop(L_, 1); // L_: ...
167 STACK_CHECK(L_, 0);
168
169 return id;
170}
171
172// #################################################################################################
173
174// Copy a function over, which has not been found in the cache.
175// L2 has the cache key for this function at the top of the stack
176void InterCopyContext::copy_func() const
177{
178 LUA_ASSERT(L1, L2_cache_i != 0); // L2: ... {cache} ... p
179 STACK_GROW(L1, 2);
180 STACK_CHECK_START_REL(L1, 0);
181
182 // 'lua_dump()' needs the function at top of stack
183 // if already on top of the stack, no need to push again
184 bool const needToPush{ L1_i != lua_gettop(L1) };
185 if (needToPush) {
186 lua_pushvalue(L1, L1_i); // L1: ... f
187 }
188
189 //
190 // "value returned is the error code returned by the last call
191 // to the writer" (and we only return 0)
192 // not sure this could ever fail but for memory shortage reasons
193 // last parameter is Lua 5.4-specific (no stripping)
194 luaL_Buffer B;
195 B.L = nullptr;
196 if (lua504_dump(L1, buf_writer, &B, 0) != 0) {
197 raise_luaL_error(getErrL(), "internal error: function dump failed.");
198 }
199
200 // pushes dumped string on 'L1'
201 luaL_pushresult(&B); // L1: ... f b
202
203 // if not pushed, no need to pop
204 if (needToPush) {
205 lua_remove(L1, -2); // L1: ... b
206 }
207
208 // transfer the bytecode, then the upvalues, to create a similar closure
209 {
210 char const* fname = nullptr;
211#define LOG_FUNC_INFO 0
212#if LOG_FUNC_INFO
213 // "To get information about a function you push it onto the
214 // stack and start the what string with the character '>'."
215 //
216 {
217 lua_Debug ar;
218 lua_pushvalue(L1, L1_i); // L1: ... b f
219 // fills 'fname' 'namewhat' and 'linedefined', pops function
220 lua_getinfo(L1, ">nS", &ar); // L1: ... b
221 fname = ar.namewhat;
222 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "FNAME: %s @ %d" INDENT_END(U), ar.short_src, ar.linedefined)); // just gives nullptr
223 }
224#endif // LOG_FUNC_INFO
225 {
226 size_t sz;
227 char const* s = lua_tolstring(L1, -1, &sz); // L1: ... b
228 LUA_ASSERT(L1, s && sz);
229 STACK_GROW(L2, 2);
230 // Note: Line numbers seem to be taken precisely from the
231 // original function. 'fname' is not used since the chunk
232 // is precompiled (it seems...).
233 //
234 // TBD: Can we get the function's original name through, as well?
235 //
236 if (luaL_loadbuffer(L2, s, sz, fname) != 0) { // L2: ... {cache} ... p function
237 // chunk is precompiled so only LUA_ERRMEM can happen
238 // "Otherwise, it pushes an error message"
239 //
240 STACK_GROW(L1, 1);
241 raise_luaL_error(getErrL(), "%s: %s", fname, lua_tostring(L2, -1));
242 }
243 // remove the dumped string
244 lua_pop(L1, 1); // ...
245 // now set the cache as soon as we can.
246 // this is necessary if one of the function's upvalues references it indirectly
247 // we need to find it in the cache even if it isn't fully transfered yet
248 lua_insert(L2, -2); // L2: ... {cache} ... function p
249 lua_pushvalue(L2, -2); // L2: ... {cache} ... function p function
250 // cache[p] = function
251 lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function
252 }
253 STACK_CHECK(L1, 0);
254
255 /* push over any upvalues; references to this function will come from
256 * cache so we don't end up in eternal loop.
257 * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy!
258 * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state!
259 */
260 int n{ 0 };
261 {
262 InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
263#if LUA_VERSION_NUM >= 502
264 // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default)
265 // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state...
266 // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
267 lua_pushglobaltable(L1); // L1: ... _G
268#endif // LUA_VERSION_NUM
269 for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n]
270 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, c.name));
271#if LUA_VERSION_NUM >= 502
272 if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table?
273 DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n"));
274 lua_pushglobaltable(L2); // L2: ... {cache} ... function <upvalues>
275 } else
276#endif // LUA_VERSION_NUM
277 {
278 DEBUGSPEW_CODE(fprintf(stderr, "copying value\n"));
279 c.L1_i = SourceIndex{ lua_gettop(L1) };
280 if (!c.inter_copy_one()) { // L2: ... {cache} ... function <upvalues>
281 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
282 }
283 }
284 lua_pop(L1, 1); // L1: ... _G
285 }
286#if LUA_VERSION_NUM >= 502
287 lua_pop(L1, 1); // L1: ...
288#endif // LUA_VERSION_NUM
289 }
290 // L2: ... {cache} ... function + 'n' upvalues (>=0)
291
292 STACK_CHECK(L1, 0);
293
294 // Set upvalues (originally set to 'nil' by 'lua_load')
295 for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) {
296 char const* rc{ lua_setupvalue(L2, func_index, n) }; // L2: ... {cache} ... function
297 //
298 // "assigns the value at the top of the stack to the upvalue and returns its name.
299 // It also pops the value from the stack."
300
301 LUA_ASSERT(L1, rc); // not having enough slots?
302 }
303 // once all upvalues have been set we are left
304 // with the function at the top of the stack // L2: ... {cache} ... function
305 }
306 STACK_CHECK(L1, 0);
307}
308
309// #################################################################################################
310
311// Push a looked-up native/LuaJIT function.
312void InterCopyContext::lookup_native_func() const
313{
314 // get the name of the function we want to send
315 size_t len;
316 char const* const fqn{ find_lookup_name(L1, L1_i, mode, name, &len) };
317 // push the equivalent function in the destination's stack, retrieved from the lookup table
318 STACK_CHECK_START_REL(L2, 0);
319 STACK_GROW(L2, 3); // up to 3 slots are necessary on error
320 switch (mode) {
321 default: // shouldn't happen, in theory...
322 raise_luaL_error(getErrL(), "internal error: unknown lookup mode");
323 break;
324
325 case LookupMode::ToKeeper:
326 // push a sentinel closure that holds the lookup name as upvalue
327 lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: "f.q.n"
328 lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f
329 break;
330
331 case LookupMode::LaneBody:
332 case LookupMode::FromKeeper:
333 kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {}
334 STACK_CHECK(L2, 1);
335 LUA_ASSERT(L1, lua_istable(L2, -1));
336 lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: {} "f.q.n"
337 lua_rawget(L2, -2); // L1: ... f ... L2: {} f
338 // nil means we don't know how to transfer stuff: user should do something
339 // anything other than function or table should not happen!
340 if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) {
341 lua_getglobal(L1, "decoda_name"); // L1: ... f ... decoda_name
342 char const* const from{ lua_tostring(L1, -1) };
343 lua_pop(L1, 1); // L1: ... f ...
344 lua_getglobal(L2, "decoda_name"); // L1: ... f ... L2: {} f decoda_name
345 char const* const to{ lua_tostring(L2, -1) };
346 lua_pop(L2, 1); // L2: {} f
347 // 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
348 raise_luaL_error(
349 getErrL(),
350 "%s%s: function '%s' not found in %s destination transfer database.",
351 lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN ",
352 from ? from : "main",
353 fqn,
354 to ? to : "main");
355 return;
356 }
357 lua_remove(L2, -2); // L2: f
358 break;
359
360 /* keep it in case I need it someday, who knows...
361 case LookupMode::RawFunctions:
362 {
363 int n;
364 char const* upname;
365 lua_CFunction f = lua_tocfunction( L, i);
366 // copy upvalues
367 for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) {
368 luaG_inter_move( U, L, L2, 1, mode_); // L2: [up[,up ...]]
369 }
370 lua_pushcclosure( L2, f, n); // L2:
371 }
372 break;
373 */
374 }
375 STACK_CHECK(L2, 1);
376}
377
378// #################################################################################################
379
380// Check if we've already copied the same function from 'L1', and reuse the old copy.
381// Always pushes a function to 'L2'.
382void InterCopyContext::copy_cached_func() const
383{
384 FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) };
385 if (funcSubType == FuncSubType::Bytecode) {
386 void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i));
387 // TBD: Merge this and same code for tables
388 LUA_ASSERT(L1, L2_cache_i != 0);
389
390 STACK_GROW(L2, 2);
391
392 // L2_cache[id_str]= function
393 //
394 STACK_CHECK_START_REL(L2, 0);
395
396 // We don't need to use the from state ('L1') in ID since the life span
397 // is only for the duration of a copy (both states are locked).
398
399 // push a light userdata uniquely representing the function
400 lua_pushlightuserdata(L2, aspointer); // L2: ... {cache} ... p
401
402 // fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
403
404 lua_pushvalue(L2, -1); // L2: ... {cache} ... p p
405 lua_rawget(L2, L2_cache_i); // L2: ... {cache} ... p function|nil|true
406
407 if (lua_isnil(L2, -1)) { // function is unknown
408 lua_pop(L2, 1); // L2: ... {cache} ... p
409
410 // Set to 'true' for the duration of creation; need to find self-references
411 // via upvalues
412 //
413 // pushes a copy of the func, stores a reference in the cache
414 copy_func(); // L2: ... {cache} ... function
415 } else { // found function in the cache
416 lua_remove(L2, -2); // L2: ... {cache} ... function
417 }
418 STACK_CHECK(L2, 1);
419 LUA_ASSERT(L1, lua_isfunction(L2, -1));
420 } else { // function is native/LuaJIT: no need to cache
421 lookup_native_func(); // L2: ... {cache} ... function
422 // if the function was in fact a lookup sentinel, we can either get a function or a table here
423 LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1));
424 }
425}
426
427// #################################################################################################
428
429// Push a looked-up table, or nothing if we found nothing
430[[nodiscard]] bool InterCopyContext::lookup_table() const
431{
432 // get the name of the table we want to send
433 size_t len;
434 char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len);
435 if (nullptr == fqn) { // name not found, it is some user-created table
436 return false;
437 }
438 // push the equivalent table in the destination's stack, retrieved from the lookup table
439 STACK_CHECK_START_REL(L2, 0);
440 STACK_GROW(L2, 3); // up to 3 slots are necessary on error
441 switch (mode) {
442 default: // shouldn't happen, in theory...
443 raise_luaL_error(getErrL(), "internal error: unknown lookup mode");
444 break;
445
446 case LookupMode::ToKeeper:
447 // push a sentinel closure that holds the lookup name as upvalue
448 lua_pushlstring(L2, fqn, len); // L1: ... t ... L2: "f.q.n"
449 lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f
450 break;
451
452 case LookupMode::LaneBody:
453 case LookupMode::FromKeeper:
454 kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {}
455 STACK_CHECK(L2, 1);
456 LUA_ASSERT(L1, lua_istable(L2, -1));
457 lua_pushlstring(L2, fqn, len); // L2: {} "f.q.n"
458 lua_rawget(L2, -2); // L2: {} t
459 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead)
460 // but not when we extract something out of a keeper, as there is nothing to clone!
461 if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody) {
462 lua_pop(L2, 2); // L1: ... t ... L2:
463 STACK_CHECK(L2, 0);
464 return false;
465 } else if (!lua_istable(L2, -1)) { // this can happen if someone decides to replace same already registered item (for a example a standard lib function) with a table
466 lua_getglobal(L1, "decoda_name"); // L1: ... t ... decoda_name
467 char const* from{ lua_tostring(L1, -1) };
468 lua_pop(L1, 1); // L1: ... t ...
469 lua_getglobal(L2, "decoda_name"); // L1: ... t ... L2: {} t decoda_name
470 char const* to{ lua_tostring(L2, -1) };
471 lua_pop(L2, 1); // L1: ... t ... L2: {} t
472 raise_luaL_error(
473 getErrL(),
474 "%s: source table '%s' found as %s in %s destination transfer database.",
475 from ? from : "main",
476 fqn,
477 lua_typename(L2, lua_type_as_enum(L2, -1)),
478 to ? to : "main");
479 }
480 lua_remove(L2, -2); // L1: ... t ... L2: t
481 break;
482 }
483 STACK_CHECK(L2, 1);
484 return true;
485}
486
487// #################################################################################################
488
489void InterCopyContext::inter_copy_keyvaluepair() const
490{
491 SourceIndex const val_i{ lua_gettop(L1) };
492 SourceIndex const key_i{ val_i - 1 };
493
494 // For the key, only basic key types are copied over. others ignored
495 InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name };
496 if (!c.inter_copy_one()) {
497 return;
498 // we could raise an error instead of ignoring the table entry, like so:
499 // raise_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));
500 // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer?
501 }
502
503 char* valPath{ nullptr };
504 if (U->verboseErrors) {
505 // for debug purposes, let's try to build a useful name
506 if (lua_type(L1, key_i) == LUA_TSTRING) {
507 char const* key{ lua_tostring(L1, key_i) };
508 size_t const keyRawLen = lua_rawlen(L1, key_i);
509 size_t const bufLen = strlen(name) + keyRawLen + 2;
510 valPath = (char*) alloca(bufLen);
511 sprintf(valPath, "%s.%*s", name, (int) keyRawLen, key);
512 key = nullptr;
513 }
514#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
515 else if (lua_isinteger(L1, key_i)) {
516 lua_Integer const key{ lua_tointeger(L1, key_i) };
517 valPath = (char*) alloca(strlen(name) + 32 + 3);
518 sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key);
519 }
520#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
521 else if (lua_type(L1, key_i) == LUA_TNUMBER) {
522 lua_Number const key{ lua_tonumber(L1, key_i) };
523 valPath = (char*) alloca(strlen(name) + 32 + 3);
524 sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key);
525 } else if (lua_type(L1, key_i) == LUA_TLIGHTUSERDATA) {
526 void* const key{ lua_touserdata(L1, key_i) };
527 valPath = (char*) alloca(strlen(name) + 16 + 5);
528 sprintf(valPath, "%s[U:%p]", name, key);
529 } else if (lua_type(L1, key_i) == LUA_TBOOLEAN) {
530 int const key{ lua_toboolean(L1, key_i) };
531 valPath = (char*) alloca(strlen(name) + 8);
532 sprintf(valPath, "%s[%s]", name, key ? "true" : "false");
533 }
534 }
535 c.L1_i = SourceIndex{ val_i };
536 // Contents of metatables are copied with cache checking. important to detect loops.
537 c.vt = VT::NORMAL;
538 c.name = valPath ? valPath : name;
539 if (c.inter_copy_one()) {
540 LUA_ASSERT(L1, lua_istable(L2, -3));
541 lua_rawset(L2, -3); // add to table (pops key & val)
542 } else {
543 raise_luaL_error(getErrL(), "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i));
544 }
545}
546
547// #################################################################################################
548
549[[nodiscard]] bool InterCopyContext::push_cached_metatable() const
550{
551 STACK_CHECK_START_REL(L1, 0);
552 if (!lua_getmetatable(L1, L1_i)) { // L1: ... mt
553 STACK_CHECK(L1, 0);
554 return false;
555 }
556 STACK_CHECK(L1, 1);
557
558 lua_Integer const mt_id{ get_mt_id(U, L1, -1) }; // Unique id for the metatable
559
560 STACK_CHECK_START_REL(L2, 0);
561 STACK_GROW(L2, 4);
562 // do we already know this metatable?
563 std::ignore = kMtIdRegKey.getSubTable(L2, 0, 0); // L2: _R[kMtIdRegKey]
564 lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] id
565 lua_rawget(L2, -2); // L2: _R[kMtIdRegKey] mt|nil
566 STACK_CHECK(L2, 2);
567
568 if (lua_isnil(L2, -1)) { // L2 did not know the metatable
569 lua_pop(L2, 1); // L2: _R[kMtIdRegKey]
570 InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name };
571 if (!c.inter_copy_one()) { // L2: _R[kMtIdRegKey] mt?
572 raise_luaL_error(getErrL(), "Error copying a metatable");
573 }
574
575 STACK_CHECK(L2, 2); // L2: _R[kMtIdRegKey] mt
576 // mt_id -> metatable
577 lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt id
578 lua_pushvalue(L2, -2); // L2: _R[kMtIdRegKey] mt id mt
579 lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
580
581 // metatable -> mt_id
582 lua_pushvalue(L2, -1); // L2: _R[kMtIdRegKey] mt mt
583 lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt mt id
584 lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
585 STACK_CHECK(L2, 2);
586 }
587 lua_remove(L2, -2); // L2: mt
588
589 lua_pop(L1, 1); // L1: ...
590 STACK_CHECK(L2, 1);
591 STACK_CHECK(L1, 0);
592 return true;
593}
594
595// #################################################################################################
596
597// Check if we've already copied the same table from 'L1', and reuse the old copy. This allows table upvalues shared by multiple
598// local functions to point to the same table, also in the target.
599// Always pushes a table to 'L2'.
600// Returns true if the table was cached (no need to fill it!); false if it's a virgin.
601[[nodiscard]] bool InterCopyContext::push_cached_table() const
602{
603 void const* p{ lua_topointer(L1, L1_i) };
604
605 LUA_ASSERT(L1, L2_cache_i != 0);
606 STACK_GROW(L2, 3);
607 STACK_CHECK_START_REL(L2, 0);
608
609 // We don't need to use the from state ('L1') in ID since the life span
610 // is only for the duration of a copy (both states are locked).
611 // push a light userdata uniquely representing the table
612 lua_pushlightuserdata(L2, const_cast<void*>(p)); // L1: ... t ... L2: ... p
613
614 // fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1));
615
616 lua_rawget(L2, L2_cache_i); // L1: ... t ... L2: ... {cached|nil}
617 bool const not_found_in_cache{ lua_isnil(L2, -1) };
618 if (not_found_in_cache) {
619 // create a new entry in the cache
620 lua_pop(L2, 1); // L1: ... t ... L2: ...
621 lua_newtable(L2); // L1: ... t ... L2: ... {}
622 lua_pushlightuserdata(L2, const_cast<void*>(p)); // L1: ... t ... L2: ... {} p
623 lua_pushvalue(L2, -2); // L1: ... t ... L2: ... {} p {}
624 lua_rawset(L2, L2_cache_i); // L1: ... t ... L2: ... {}
625 }
626 STACK_CHECK(L2, 1);
627 LUA_ASSERT(L1, lua_istable(L2, -1));
628 return !not_found_in_cache;
629}
630
631// #################################################################################################
632
633[[nodiscard]] bool InterCopyContext::tryCopyClonable() const
634{
635 SourceIndex const L1i{ lua_absindex(L1, L1_i) };
636 void* const source{ lua_touserdata(L1, L1i) };
637
638 STACK_CHECK_START_REL(L1, 0);
639 STACK_CHECK_START_REL(L2, 0);
640
641 // Check if the source was already cloned during this copy
642 lua_pushlightuserdata(L2, source); // L2: ... source
643 lua_rawget(L2, L2_cache_i); // L2: ... clone?
644 if (!lua_isnil(L2, -1)) {
645 STACK_CHECK(L2, 1);
646 return true;
647 } else {
648 lua_pop(L2, 1); // L2: ...
649 }
650 STACK_CHECK(L2, 0);
651
652 // no metatable? -> not clonable
653 if (!lua_getmetatable(L1, L1i)) { // L1: ... mt?
654 STACK_CHECK(L1, 0);
655 return false;
656 }
657
658 // no __lanesclone? -> not clonable
659 lua_getfield(L1, -1, "__lanesclone"); // L1: ... mt __lanesclone?
660 if (lua_isnil(L1, -1)) {
661 lua_pop(L1, 2); // L1: ...
662 STACK_CHECK(L1, 0);
663 return false;
664 }
665
666 // we need to copy over the uservalues of the userdata as well
667 {
668 int const mt{ lua_absindex(L1, -2) }; // L1: ... mt __lanesclone
669 size_t const userdata_size{ lua_rawlen(L1, L1i) };
670 // extract all the uservalues, but don't transfer them yet
671 int uvi = 0;
672 while (lua_getiuservalue(L1, L1i, ++uvi) != LUA_TNONE) {} // L1: ... mt __lanesclone [uv]+ nil
673 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
674 lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]+
675 --uvi;
676 // create the clone userdata with the required number of uservalue slots
677 void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // L2: ... u
678 // copy the metatable in the target state, and give it to the clone we put there
679 InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name };
680 if (c.inter_copy_one()) { // L2: ... u mt|sentinel
681 if (LookupMode::ToKeeper == mode) { // L2: ... u sentinel
682 LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel);
683 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
684 lua_getupvalue(L2, -1, 1); // L2: ... u sentinel fqn
685 lua_remove(L2, -2); // L2: ... u fqn
686 lua_insert(L2, -2); // L2: ... fqn u
687 lua_pushcclosure(L2, userdata_clone_sentinel, 2); // L2: ... userdata_clone_sentinel
688 } else { // from keeper or direct // L2: ... u mt
689 LUA_ASSERT(L1, lua_istable(L2, -1));
690 lua_setmetatable(L2, -2); // L2: ... u
691 }
692 STACK_CHECK(L2, 1);
693 } else {
694 raise_luaL_error(getErrL(), "Error copying a metatable");
695 }
696 // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel
697 lua_pushlightuserdata(L2, source); // L2: ... u source
698 lua_pushvalue(L2, -2); // L2: ... u source u
699 lua_rawset(L2, L2_cache_i); // L2: ... u
700 // make sure we have the userdata now
701 if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel
702 lua_getupvalue(L2, -1, 2); // L2: ... userdata_clone_sentinel u
703 }
704 // assign uservalues
705 while (uvi > 0) {
706 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
707 if (!c.inter_copy_one()) { // L2: ... u uv
708 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
709 }
710 lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]*
711 // this pops the value from the stack
712 lua_setiuservalue(L2, -2, uvi); // L2: ... u
713 --uvi;
714 }
715 // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination
716 if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel u
717 lua_pop(L2, 1); // L2: ... userdata_clone_sentinel
718 }
719 STACK_CHECK(L2, 1);
720 STACK_CHECK(L1, 2);
721 // call cloning function in source state to perform the actual memory cloning
722 lua_pushlightuserdata(L1, clone); // L1: ... mt __lanesclone clone
723 lua_pushlightuserdata(L1, source); // L1: ... mt __lanesclone clone source
724 lua_pushinteger(L1, static_cast<lua_Integer>(userdata_size)); // L1: ... mt __lanesclone clone source size
725 lua_call(L1, 3, 0); // L1: ... mt
726 STACK_CHECK(L1, 1);
727 }
728
729 STACK_CHECK(L2, 1);
730 lua_pop(L1, 1); // L1: ...
731 STACK_CHECK(L1, 0);
732 return true;
733}
734
735// #################################################################################################
736
737// Copy deep userdata between two separate Lua states (from L1 to L2)
738// Returns false if not a deep userdata, else true (unless an error occured)
739[[nodiscard]] bool InterCopyContext::tryCopyDeep() const
740{
741 DeepFactory* const factory{ LookupFactory(L1, L1_i, mode) };
742 if (factory == nullptr) {
743 return false; // not a deep userdata
744 }
745
746 STACK_CHECK_START_REL(L1, 0);
747 STACK_CHECK_START_REL(L2, 0);
748
749 // extract all uservalues of the source. unfortunately, the only way to know their count is to iterate until we fail
750 int nuv = 0;
751 while (lua_getiuservalue(L1, L1_i, nuv + 1) != LUA_TNONE) { // L1: ... u [uv]* nil
752 ++nuv;
753 }
754 // last call returned TNONE and pushed nil, that we don't need
755 lua_pop(L1, 1); // L1: ... u [uv]*
756 STACK_CHECK(L1, nuv);
757
758 DeepPrelude* const u{ *lua_tofulluserdata<DeepPrelude*>(L1, L1_i) };
759 char const* errmsg{ DeepFactory::PushDeepProxy(L2, u, nuv, mode) }; // L1: ... u [uv]* L2: u
760 if (errmsg != nullptr) {
761 raise_luaL_error(getErrL(), errmsg);
762 }
763
764 // transfer all uservalues of the source in the destination
765 {
766 InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, name };
767 int const clone_i{ lua_gettop(L2) };
768 while (nuv) {
769 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
770 if (!c.inter_copy_one()) { // L1: ... u [uv]* L2: u uv
771 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
772 }
773 lua_pop(L1, 1); // L1: ... u [uv]*
774 // this pops the value from the stack
775 lua_setiuservalue(L2, clone_i, nuv); // L2: u
776 --nuv;
777 }
778 }
779
780 STACK_CHECK(L2, 1);
781 STACK_CHECK(L1, 0);
782
783 return true;
784}
785
786// #################################################################################################
787
788[[nodiscard]] bool InterCopyContext::inter_copy_boolean() const
789{
790 int const v{ lua_toboolean(L1, L1_i) };
791 DEBUGSPEW_CODE(fprintf(stderr, "%s\n", v ? "true" : "false"));
792 lua_pushboolean(L2, v);
793 return true;
794}
795
796// #################################################################################################
797
798[[nodiscard]] bool InterCopyContext::inter_copy_function() const
799{
800 if (vt == VT::KEY) {
801 return false;
802 }
803
804 STACK_CHECK_START_REL(L1, 0);
805 STACK_CHECK_START_REL(L2, 0);
806 DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
807
808 if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) { // we are actually copying a clonable full userdata from a keeper
809 // clone the full userdata again
810
811 // let's see if we already restored this userdata
812 lua_getupvalue(L1, L1_i, 2); // L1: ... u
813 void* source = lua_touserdata(L1, -1);
814 lua_pushlightuserdata(L2, source); // L2: ... source
815 lua_rawget(L2, L2_cache_i); // L2: ... u?
816 if (!lua_isnil(L2, -1)) {
817 lua_pop(L1, 1); // L1: ...
818 STACK_CHECK(L1, 0);
819 STACK_CHECK(L2, 1);
820 return true;
821 }
822 lua_pop(L2, 1); // L2: ...
823
824 // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself
825 bool const found{ lookup_table() }; // L2: ... mt?
826 if (!found) {
827 STACK_CHECK(L2, 0);
828 return false;
829 }
830 // 'L1_i' slot was the proxy closure, but from now on we operate onthe actual userdata we extracted from it
831 SourceIndex const source_i{ lua_gettop(L1) };
832 source = lua_touserdata(L1, -1);
833 void* clone{ nullptr };
834 // get the number of bytes to allocate for the clone
835 size_t const userdata_size{ lua_rawlen(L1, -1) };
836 {
837 // extract uservalues (don't transfer them yet)
838 int uvi = 0;
839 while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // L1: ... u uv
840 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
841 lua_pop(L1, 1); // L1: ... u [uv]*
842 --uvi;
843 STACK_CHECK(L1, uvi + 1);
844 // create the clone userdata with the required number of uservalue slots
845 clone = lua_newuserdatauv(L2, userdata_size, uvi); // L2: ... mt u
846 // add it in the cache
847 lua_pushlightuserdata(L2, source); // L2: ... mt u source
848 lua_pushvalue(L2, -2); // L2: ... mt u source u
849 lua_rawset(L2, L2_cache_i); // L2: ... mt u
850 // set metatable
851 lua_pushvalue(L2, -2); // L2: ... mt u mt
852 lua_setmetatable(L2, -2); // L2: ... mt u
853 // transfer and assign uservalues
854 InterCopyContext c{ *this };
855 while (uvi > 0) {
856 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
857 if (!c.inter_copy_one()) { // L2: ... mt u uv
858 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
859 }
860 lua_pop(L1, 1); // L1: ... u [uv]*
861 // this pops the value from the stack
862 lua_setiuservalue(L2, -2, uvi); // L2: ... mt u
863 --uvi;
864 }
865 // when we are done, all uservalues are popped from the stack, we can pop the source as well
866 lua_pop(L1, 1); // L1: ...
867 STACK_CHECK(L1, 0);
868 STACK_CHECK(L2, 2); // L2: ... mt u
869 }
870 // perform the custom cloning part
871 lua_insert(L2, -2); // L2: ... u mt
872 // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with
873 lua_getfield(L2, -1, "__lanesclone"); // L2: ... u mt __lanesclone
874 lua_remove(L2, -2); // L2: ... u __lanesclone
875 lua_pushlightuserdata(L2, clone); // L2: ... u __lanesclone clone
876 lua_pushlightuserdata(L2, source); // L2: ... u __lanesclone clone source
877 lua_pushinteger(L2, userdata_size); // L2: ... u __lanesclone clone source size
878 // clone:__lanesclone(dest, source, size)
879 lua_call(L2, 3, 0); // L2: ... u
880 } else { // regular function
881 DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
882 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
883 copy_cached_func(); // L2: ... f
884 }
885 STACK_CHECK(L2, 1);
886 STACK_CHECK(L1, 0);
887 return true;
888}
889
890// #################################################################################################
891
892[[nodiscard]] bool InterCopyContext::inter_copy_lightuserdata() const
893{
894 void* const p{ lua_touserdata(L1, L1_i) };
895 DEBUGSPEW_CODE(fprintf(stderr, "%p\n", p));
896 lua_pushlightuserdata(L2, p);
897 return true;
898}
899
900// #################################################################################################
901
902[[nodiscard]] bool InterCopyContext::inter_copy_nil() const
903{
904 if (vt == VT::KEY) {
905 return false;
906 }
907 lua_pushnil(L2);
908 return true;
909}
910
911// #################################################################################################
912
913[[nodiscard]] bool InterCopyContext::inter_copy_number() const
914{
915 // LNUM patch support (keeping integer accuracy)
916#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
917 if (lua_isinteger(L1, L1_i)) {
918 lua_Integer const v{ lua_tointeger(L1, L1_i) };
919 DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v));
920 lua_pushinteger(L2, v);
921 } else
922#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
923 {
924 lua_Number const v{ lua_tonumber(L1, L1_i) };
925 DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", v));
926 lua_pushnumber(L2, v);
927 }
928 return true;
929}
930
931// #################################################################################################
932
933[[nodiscard]] bool InterCopyContext::inter_copy_string() const
934{
935 size_t len;
936 char const* const s{ lua_tolstring(L1, L1_i, &len) };
937 DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", s));
938 lua_pushlstring(L2, s, len);
939 return true;
940}
941
942// #################################################################################################
943
944[[nodiscard]] bool InterCopyContext::inter_copy_table() const
945{
946 if (vt == VT::KEY) {
947 return false;
948 }
949
950 STACK_CHECK_START_REL(L1, 0);
951 STACK_CHECK_START_REL(L2, 0);
952 DEBUGSPEW_CODE(fprintf(stderr, "TABLE %s\n", name));
953
954 /*
955 * 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?)
956 * 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
957 */
958 if (lookup_table()) {
959 LUA_ASSERT(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
960 return true;
961 }
962
963 /* Check if we've already copied the same table from 'L1' (during this transmission), and
964 * reuse the old copy. This allows table upvalues shared by multiple
965 * local functions to point to the same table, also in the target.
966 * Also, this takes care of cyclic tables and multiple references
967 * to the same subtable.
968 *
969 * Note: Even metatables need to go through this test; to detect
970 * loops such as those in required module tables (getmetatable(lanes).lanes == lanes)
971 */
972 if (push_cached_table()) {
973 LUA_ASSERT(L1, lua_istable(L2, -1)); // from cache
974 return true;
975 }
976 LUA_ASSERT(L1, lua_istable(L2, -1));
977
978 STACK_GROW(L1, 2);
979 STACK_GROW(L2, 2);
980
981 lua_pushnil(L1); // start iteration
982 while (lua_next(L1, L1_i)) {
983 // need a function to prevent overflowing the stack with verboseErrors-induced alloca()
984 inter_copy_keyvaluepair();
985 lua_pop(L1, 1); // pop value (next round)
986 }
987 STACK_CHECK(L1, 0);
988 STACK_CHECK(L2, 1);
989
990 // Metatables are expected to be immutable, and copied only once.
991 if (push_cached_metatable()) { // L2: ... t mt?
992 lua_setmetatable(L2, -2); // L2: ... t
993 }
994 STACK_CHECK(L2, 1);
995 STACK_CHECK(L1, 0);
996 return true;
997}
998
999// #################################################################################################
1000
1001[[nodiscard]] bool InterCopyContext::inter_copy_userdata() const
1002{
1003 STACK_CHECK_START_REL(L1, 0);
1004 STACK_CHECK_START_REL(L2, 0);
1005 if (vt == VT::KEY) {
1006 return false;
1007 }
1008
1009 // try clonable userdata first
1010 if (tryCopyClonable()) {
1011 STACK_CHECK(L1, 0);
1012 STACK_CHECK(L2, 1);
1013 return true;
1014 }
1015
1016 STACK_CHECK(L1, 0);
1017 STACK_CHECK(L2, 0);
1018
1019 // Allow only deep userdata entities to be copied across
1020 DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n"));
1021 if (tryCopyDeep()) {
1022 STACK_CHECK(L1, 0);
1023 STACK_CHECK(L2, 1);
1024 return true;
1025 }
1026
1027 STACK_CHECK(L1, 0);
1028 STACK_CHECK(L2, 0);
1029
1030 // Not a deep or clonable full userdata
1031 if (U->demoteFullUserdata) { // attempt demotion to light userdata
1032 void* const lud{ lua_touserdata(L1, L1_i) };
1033 lua_pushlightuserdata(L2, lud);
1034 } else { // raise an error
1035 raise_luaL_error(getErrL(), "can't copy non-deep full userdata across lanes");
1036 }
1037
1038 STACK_CHECK(L2, 1);
1039 STACK_CHECK(L1, 0);
1040 return true;
1041}
1042
1043// #################################################################################################
1044
1045#if USE_DEBUG_SPEW()
1046static char const* lua_type_names[] = {
1047 "LUA_TNIL"
1048 , "LUA_TBOOLEAN"
1049 , "LUA_TLIGHTUSERDATA"
1050 , "LUA_TNUMBER"
1051 , "LUA_TSTRING"
1052 , "LUA_TTABLE"
1053 , "LUA_TFUNCTION"
1054 , "LUA_TUSERDATA"
1055 , "LUA_TTHREAD"
1056 , "<LUA_NUMTAGS>" // not really a type
1057 , "LUA_TJITCDATA" // LuaJIT specific
1058};
1059static char const* vt_names[] = {
1060 "VT::NORMAL"
1061 , "VT::KEY"
1062 , "VT::METATABLE"
1063};
1064#endif // USE_DEBUG_SPEW()
1065
1066/*
1067 * Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove
1068 * the original value.
1069 *
1070 * NOTE: Both the states must be solely in the current OS thread's possession.
1071 *
1072 * 'i' is an absolute index (no -1, ...)
1073 *
1074 * Returns true if value was pushed, false if its type is non-supported.
1075 */
1076[[nodiscard]] bool InterCopyContext::inter_copy_one() const
1077{
1078 static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING);
1079 STACK_GROW(L2, 1);
1080 STACK_CHECK_START_REL(L1, 0);
1081 STACK_CHECK_START_REL(L2, 0);
1082
1083 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END(U)));
1084 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1085
1086 LuaType val_type{ lua_type_as_enum(L1, L1_i) };
1087 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END(U), lua_type_names[static_cast<int>(val_type)], vt_names[static_cast<int>(vt)]));
1088
1089 // Non-POD can be skipped if its metatable contains { __lanesignore = true }
1090 if (((1 << static_cast<int>(val_type)) & kPODmask) == 0) {
1091 if (lua_getmetatable(L1, L1_i)) { // L1: ... mt
1092 lua_getfield(L1, -1, "__lanesignore"); // L1: ... mt ignore?
1093 if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) {
1094 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END(U)));
1095 val_type = LuaType::NIL;
1096 }
1097 lua_pop(L1, 2); // L1: ...
1098 }
1099 }
1100 STACK_CHECK(L1, 0);
1101
1102 // Lets push nil to L2 if the object should be ignored
1103 bool ret{ true };
1104 switch (val_type) {
1105 // Basic types allowed both as values, and as table keys
1106 case LuaType::BOOLEAN:
1107 ret = inter_copy_boolean();
1108 break;
1109 case LuaType::NUMBER:
1110 ret = inter_copy_number();
1111 break;
1112 case LuaType::STRING:
1113 ret = inter_copy_string();
1114 break;
1115 case LuaType::LIGHTUSERDATA:
1116 ret = inter_copy_lightuserdata();
1117 break;
1118
1119 // The following types are not allowed as table keys
1120 case LuaType::USERDATA:
1121 ret = inter_copy_userdata();
1122 break;
1123 case LuaType::NIL:
1124 ret = inter_copy_nil();
1125 break;
1126 case LuaType::FUNCTION:
1127 ret = inter_copy_function();
1128 break;
1129 case LuaType::TABLE:
1130 ret = inter_copy_table();
1131 break;
1132
1133 // The following types cannot be copied
1134 case LuaType::CDATA:
1135 [[fallthrough]];
1136 case LuaType::THREAD:
1137 ret = false;
1138 break;
1139 }
1140
1141 STACK_CHECK(L2, ret ? 1 : 0);
1142 STACK_CHECK(L1, 0);
1143 return ret;
1144}
1145
1146// #################################################################################################
1147
1148// transfers stuff from L1->_G["package"] to L2->_G["package"]
1149// returns InterCopyResult::Success if everything is fine
1150// returns InterCopyResult::Error if pushed an error message in L1
1151// else raise an error in L1
1152[[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const
1153{
1154 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END(U)));
1155
1156 class OnExit
1157 {
1158 private:
1159 lua_State* const L2;
1160 int const top_L2;
1161 DEBUGSPEW_CODE(DebugSpewIndentScope m_scope);
1162
1163 public:
1164 OnExit(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L2_)
1165 : L2{ L2_ }
1166 , top_L2{ lua_gettop(L2) } DEBUGSPEW_COMMA_PARAM(m_scope{ U_ })
1167 {
1168 }
1169
1170 ~OnExit()
1171 {
1172 lua_settop(L2, top_L2);
1173 }
1174 } onExit{ DEBUGSPEW_PARAM_COMMA(U) L2 };
1175
1176 STACK_CHECK_START_REL(L1, 0);
1177 if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) {
1178 lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i));
1179 STACK_CHECK(L1, 1);
1180 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
1181 if (mode == LookupMode::LaneBody) {
1182 raise_lua_error(getErrL()); // that's ok, getErrL() is L1 in that case
1183 }
1184 return InterCopyResult::Error;
1185 }
1186 if (luaG_getmodule(L2, LUA_LOADLIBNAME) == LuaType::NIL) { // package library not loaded: do nothing
1187 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END(U)));
1188 STACK_CHECK(L1, 0);
1189 return InterCopyResult::Success;
1190 }
1191
1192 InterCopyResult result{ InterCopyResult::Success };
1193 // package.loaders is renamed package.searchers in Lua 5.2
1194 // but don't copy it anyway, as the function names change depending on the slot index!
1195 // users should provide an on_state_create function to setup custom loaders instead
1196 // don't copy package.preload in keeper states (they don't know how to translate functions)
1197 char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr };
1198 for (char const* const entry : entries) {
1199 if (!entry) {
1200 continue;
1201 }
1202 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END(U), entry));
1203 lua_getfield(L1, L1_i, entry);
1204 if (lua_isnil(L1, -1)) {
1205 lua_pop(L1, 1);
1206 } else {
1207 {
1208 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1209 result = inter_move(1); // moves the entry to L2
1210 STACK_CHECK(L1, 0);
1211 }
1212 if (result == InterCopyResult::Success) {
1213 lua_setfield(L2, -2, entry); // set package[entry]
1214 } else {
1215 lua_pushfstring(L1, "failed to copy package entry %s", entry);
1216 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
1217 if (mode == LookupMode::LaneBody) {
1218 raise_lua_error(getErrL());
1219 }
1220 lua_pop(L1, 1);
1221 break;
1222 }
1223 }
1224 }
1225 STACK_CHECK(L1, 0);
1226 return result;
1227}
1228
1229// #################################################################################################
1230
1231// Akin to 'lua_xmove' but copies values between _any_ Lua states.
1232// NOTE: Both the states must be solely in the current OS thread's possession.
1233[[nodiscard]] InterCopyResult InterCopyContext::inter_copy(int n_) const
1234{
1235 LUA_ASSERT(L1, vt == VT::NORMAL);
1236
1237 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END(U)));
1238 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1239
1240 int const top_L1{ lua_gettop(L1) };
1241 if (n_ > top_L1) {
1242 // requesting to copy more than is available?
1243 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END(U)));
1244 return InterCopyResult::NotEnoughValues;
1245 }
1246
1247 STACK_CHECK_START_REL(L2, 0);
1248 STACK_GROW(L2, n_ + 1);
1249
1250 /*
1251 * Make a cache table for the duration of this copy. Collects tables and
1252 * function entries, avoiding the same entries to be passed on as multiple
1253 * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
1254 */
1255 int const top_L2{ lua_gettop(L2) }; // L2: ...
1256 lua_newtable(L2); // L2: ... cache
1257
1258 char tmpBuf[16];
1259 char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" };
1260 InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf };
1261 bool copyok{ true };
1262 STACK_CHECK_START_REL(L1, 0);
1263 for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) {
1264 if (U->verboseErrors) {
1265 sprintf(tmpBuf, "arg_%d", j);
1266 }
1267 c.L1_i = SourceIndex{ i };
1268 copyok = c.inter_copy_one(); // L2: ... cache {}n
1269 if (!copyok) {
1270 break;
1271 }
1272 }
1273 STACK_CHECK(L1, 0);
1274
1275 if (copyok) {
1276 STACK_CHECK(L2, n_ + 1);
1277 // Remove the cache table. Persistent caching would cause i.e. multiple
1278 // messages passed in the same table to use the same table also in receiving end.
1279 lua_remove(L2, top_L2 + 1);
1280 return InterCopyResult::Success;
1281 }
1282
1283 // error -> pop everything from the target state stack
1284 lua_settop(L2, top_L2);
1285 STACK_CHECK(L2, 0);
1286 return InterCopyResult::Error;
1287}
1288
1289// #################################################################################################
1290
1291[[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const
1292{
1293 InterCopyResult const ret{ inter_copy(n_) };
1294 lua_pop(L1, n_);
1295 return ret;
1296}
diff --git a/src/intercopycontext.h b/src/intercopycontext.h
new file mode 100644
index 0000000..28e1ead
--- /dev/null
+++ b/src/intercopycontext.h
@@ -0,0 +1,77 @@
1#pragma once
2
3#include "tools.h"
4
5// forwards
6class Universe;
7
8// #################################################################################################
9
10enum class VT
11{
12 NORMAL, // keep this one first so that it's the value we get when we default-construct
13 KEY,
14 METATABLE
15};
16
17enum class InterCopyResult
18{
19 Success,
20 NotEnoughValues,
21 Error
22};
23
24// #################################################################################################
25
26using CacheIndex = Unique<int>;
27using SourceIndex = Unique<int>;
28class InterCopyContext
29{
30 public:
31 Universe* const U;
32 DestState const L2;
33 SourceState const L1;
34 CacheIndex const L2_cache_i;
35 SourceIndex L1_i; // that one can change when we reuse the context
36 VT vt; // that one can change when we reuse the context
37 LookupMode const mode;
38 char const* name; // that one can change when we reuse the context
39
40 private:
41 // when mode == LookupMode::FromKeeper, L1 is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error
42 // whon mode != LookupMode::FromKeeper, L1 is not a keeper state, therefore L1 is the state where we want to raise the error
43 lua_State* getErrL() const { return (mode == LookupMode::FromKeeper) ? L2 : L1; }
44
45 // for use in copy_cached_func
46 void copy_func() const;
47 void lookup_native_func() const;
48
49 // for use in inter_copy_function
50 void copy_cached_func() const;
51 [[nodiscard]] bool lookup_table() const;
52
53 // for use in inter_copy_table
54 void inter_copy_keyvaluepair() const;
55 [[nodiscard]] bool push_cached_metatable() const;
56 [[nodiscard]] bool push_cached_table() const;
57
58 // for use in inter_copy_userdata
59 [[nodiscard]] bool tryCopyClonable() const;
60 [[nodiscard]] bool tryCopyDeep() const;
61
62 // copying a single Lua stack item
63 [[nodiscard]] bool inter_copy_boolean() const;
64 [[nodiscard]] bool inter_copy_function() const;
65 [[nodiscard]] bool inter_copy_lightuserdata() const;
66 [[nodiscard]] bool inter_copy_nil() const;
67 [[nodiscard]] bool inter_copy_number() const;
68 [[nodiscard]] bool inter_copy_string() const;
69 [[nodiscard]] bool inter_copy_table() const;
70 [[nodiscard]] bool inter_copy_userdata() const;
71
72 public:
73 [[nodiscard]] bool inter_copy_one() const;
74 [[nodiscard]] InterCopyResult inter_copy_package() const;
75 [[nodiscard]] InterCopyResult inter_copy(int n_) const;
76 [[nodiscard]] InterCopyResult inter_move(int n_) const;
77};
diff --git a/src/keeper.cpp b/src/keeper.cpp
index bb510f4..7367d0c 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -39,12 +39,9 @@
39 */ 39 */
40#include "keeper.h" 40#include "keeper.h"
41 41
42#include "compat.h" 42#include "intercopycontext.h"
43#include "linda.h" 43#include "linda.h"
44#include "state.h" 44#include "state.h"
45#include "tools.h"
46#include "uniquekey.h"
47#include "universe.h"
48 45
49#include <algorithm> 46#include <algorithm>
50#include <cassert> 47#include <cassert>
diff --git a/src/keeper.h b/src/keeper.h
index 37642fd..8f30720 100644
--- a/src/keeper.h
+++ b/src/keeper.h
@@ -9,7 +9,6 @@ extern "C"
9} 9}
10#endif // __cplusplus 10#endif // __cplusplus
11 11
12#include "threading.h"
13#include "uniquekey.h" 12#include "uniquekey.h"
14 13
15#include <optional> 14#include <optional>
diff --git a/src/lanes.cpp b/src/lanes.cpp
index d211b6a..ee40ffa 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -81,7 +81,8 @@ THE SOFTWARE.
81 81
82#include "lanes.h" 82#include "lanes.h"
83 83
84#include "compat.h" 84#include "deep.h"
85#include "intercopycontext.h"
85#include "keeper.h" 86#include "keeper.h"
86#include "lanes_private.h" 87#include "lanes_private.h"
87#include "state.h" 88#include "state.h"
@@ -1522,6 +1523,84 @@ LUAG_FUNC(wakeup_conv)
1522} 1523}
1523 1524
1524// ################################################################################################# 1525// #################################################################################################
1526// ################################### custom allocator support ####################################
1527// #################################################################################################
1528
1529// same as PUC-Lua l_alloc
1530extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
1531{
1532 if (nsize_ == 0) {
1533 free(ptr_);
1534 return nullptr;
1535 } else {
1536 return realloc(ptr_, nsize_);
1537 }
1538}
1539
1540// #################################################################################################
1541
1542[[nodiscard]] static int luaG_provide_protected_allocator(lua_State* L_)
1543{
1544 Universe* const U{ universe_get(L_) };
1545 // push a new full userdata on the stack, giving access to the universe's protected allocator
1546 [[maybe_unused]] AllocatorDefinition* const def{ new (L_) AllocatorDefinition{ U->protectedAllocator.makeDefinition() } };
1547 return 1;
1548}
1549
1550// #################################################################################################
1551
1552// called once at the creation of the universe (therefore L is the master Lua state everything originates from)
1553// Do I need to disable this when compiling for LuaJIT to prevent issues?
1554static void initialize_allocator_function(Universe* U_, lua_State* L_)
1555{
1556 STACK_CHECK_START_REL(L_, 1); // L_: settings
1557 lua_getfield(L_, -1, "allocator"); // L_: settings allocator|nil|"protected"
1558 if (!lua_isnil(L_, -1)) {
1559 // store C function pointer in an internal variable
1560 U_->provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator
1561 if (U_->provideAllocator != nullptr) {
1562 // make sure the function doesn't have upvalues
1563 char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval?
1564 if (upname != nullptr) { // should be "" for C functions with upvalues if any
1565 raise_luaL_error(L_, "config.allocator() shouldn't have upvalues");
1566 }
1567 // remove this C function from the config table so that it doesn't cause problems
1568 // when we transfer the config table in newly created Lua states
1569 lua_pushnil(L_); // L_: settings allocator nil
1570 lua_setfield(L_, -3, "allocator"); // L_: settings allocator
1571 } else if (lua_type(L_, -1) == LUA_TSTRING) { // should be "protected"
1572 LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0);
1573 // set the original allocator to call from inside protection by the mutex
1574 U_->protectedAllocator.initFrom(L_);
1575 U_->protectedAllocator.installIn(L_);
1576 // before a state is created, this function will be called to obtain the allocator
1577 U_->provideAllocator = luaG_provide_protected_allocator;
1578 }
1579 } else {
1580 // just grab whatever allocator was provided to lua_newstate
1581 U_->protectedAllocator.initFrom(L_);
1582 }
1583 lua_pop(L_, 1); // L_: settings
1584 STACK_CHECK(L_, 1);
1585
1586 lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator"
1587 {
1588 char const* allocator = lua_tostring(L_, -1);
1589 if (strcmp(allocator, "libc") == 0) {
1590 U_->internalAllocator = AllocatorDefinition{ libc_lua_Alloc, nullptr };
1591 } else if (U_->provideAllocator == luaG_provide_protected_allocator) {
1592 // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
1593 U_->internalAllocator = U_->protectedAllocator.makeDefinition();
1594 } else {
1595 // no protection required, just use whatever we have as-is.
1596 U_->internalAllocator = U_->protectedAllocator;
1597 }
1598 }
1599 lua_pop(L_, 1); // L_: settings
1600 STACK_CHECK(L_, 1);
1601}
1602
1603// #################################################################################################
1525// ######################################## Module linkage ######################################### 1604// ######################################## Module linkage #########################################
1526// ################################################################################################# 1605// #################################################################################################
1527 1606
diff --git a/src/linda.cpp b/src/linda.cpp
index 40ef6c7..07911b3 100644
--- a/src/linda.cpp
+++ b/src/linda.cpp
@@ -32,13 +32,9 @@ THE SOFTWARE.
32 32
33#include "linda.h" 33#include "linda.h"
34 34
35#include "compat.h"
36#include "keeper.h"
37#include "lanes_private.h" 35#include "lanes_private.h"
38#include "lindafactory.h" 36#include "lindafactory.h"
39#include "threading.h"
40#include "tools.h" 37#include "tools.h"
41#include "universe.h"
42 38
43#include <functional> 39#include <functional>
44 40
diff --git a/src/lindafactory.cpp b/src/lindafactory.cpp
index 917d949..0ec5a0a 100644
--- a/src/lindafactory.cpp
+++ b/src/lindafactory.cpp
@@ -32,7 +32,6 @@ THE SOFTWARE.
32 32
33#include "lindafactory.h" 33#include "lindafactory.h"
34 34
35#include "lanes_private.h"
36#include "linda.h" 35#include "linda.h"
37 36
38// must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it) 37// must be a #define instead of a constexpr to work with lua_pushliteral (until I templatize it)
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index a1f6cba..b8f04b6 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -61,7 +61,7 @@ template <typename... ARGS>
61 61
62// ################################################################################################# 62// #################################################################################################
63 63
64#define USE_DEBUG_SPEW() 0 64#define USE_DEBUG_SPEW() 1
65#if USE_DEBUG_SPEW() 65#if USE_DEBUG_SPEW()
66#define INDENT_BEGIN "%.*s " 66#define INDENT_BEGIN "%.*s "
67#define INDENT_END(U_) , (U_ ? U_->debugspewIndentDepth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent 67#define INDENT_END(U_) , (U_ ? U_->debugspewIndentDepth.load(std::memory_order_relaxed) : 0), DebugSpewIndentScope::debugspew_indent
diff --git a/src/state.cpp b/src/state.cpp
index f894978..7252885 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -33,6 +33,7 @@ THE SOFTWARE.
33 33
34#include "state.h" 34#include "state.h"
35 35
36#include "intercopycontext.h"
36#include "lanes.h" 37#include "lanes.h"
37#include "lanes_private.h" 38#include "lanes_private.h"
38#include "tools.h" 39#include "tools.h"
diff --git a/src/tools.cpp b/src/tools.cpp
index 73efda9..2623da6 100644
--- a/src/tools.cpp
+++ b/src/tools.cpp
@@ -1,5 +1,6 @@
1/* 1/*
2 * TOOLS.C Copyright (c) 2002-10, Asko Kauppi 2 * TOOLS.CPP Copyright (c) 2002-10, Asko Kauppi
3 * Copyright (C) 2010-24, Benoit Germain
3 * 4 *
4 * Lua tools to support Lanes. 5 * Lua tools to support Lanes.
5 */ 6 */
@@ -8,7 +9,7 @@
8=============================================================================== 9===============================================================================
9 10
10Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com> 11Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com>
11 2011-17 benoit Germain <bnt.germain@gmail.com> 12 2011-24 benoit Germain <bnt.germain@gmail.com>
12 13
13Permission is hereby granted, free of charge, to any person obtaining a copy 14Permission is hereby granted, free of charge, to any person obtaining a copy
14of this software and associated documentation files (the "Software"), to deal 15of this software and associated documentation files (the "Software"), to deal
@@ -42,82 +43,6 @@ static constexpr RegistryUniqueKey kLookupCacheRegKey{ 0x9BF75F84E54B691Bull };
42 43
43// ################################################################################################# 44// #################################################################################################
44 45
45// same as PUC-Lua l_alloc
46extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
47{
48 if (nsize_ == 0) {
49 free(ptr_);
50 return nullptr;
51 } else {
52 return realloc(ptr_, nsize_);
53 }
54}
55
56// #################################################################################################
57
58[[nodiscard]] static int luaG_provide_protected_allocator(lua_State* L_)
59{
60 Universe* const U{ universe_get(L_) };
61 // push a new full userdata on the stack, giving access to the universe's protected allocator
62 [[maybe_unused]] AllocatorDefinition* const def{ new (L_) AllocatorDefinition{ U->protectedAllocator.makeDefinition() } };
63 return 1;
64}
65
66// #################################################################################################
67
68// called once at the creation of the universe (therefore L is the master Lua state everything originates from)
69// Do I need to disable this when compiling for LuaJIT to prevent issues?
70void initialize_allocator_function(Universe* U_, lua_State* L_)
71{
72 STACK_CHECK_START_REL(L_, 1); // L_: settings
73 lua_getfield(L_, -1, "allocator"); // L_: settings allocator|nil|"protected"
74 if (!lua_isnil(L_, -1)) {
75 // store C function pointer in an internal variable
76 U_->provideAllocator = lua_tocfunction(L_, -1); // L_: settings allocator
77 if (U_->provideAllocator != nullptr) {
78 // make sure the function doesn't have upvalues
79 char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval?
80 if (upname != nullptr) { // should be "" for C functions with upvalues if any
81 raise_luaL_error(L_, "config.allocator() shouldn't have upvalues");
82 }
83 // remove this C function from the config table so that it doesn't cause problems
84 // when we transfer the config table in newly created Lua states
85 lua_pushnil(L_); // L_: settings allocator nil
86 lua_setfield(L_, -3, "allocator"); // L_: settings allocator
87 } else if (lua_type(L_, -1) == LUA_TSTRING) { // should be "protected"
88 LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0);
89 // set the original allocator to call from inside protection by the mutex
90 U_->protectedAllocator.initFrom(L_);
91 U_->protectedAllocator.installIn(L_);
92 // before a state is created, this function will be called to obtain the allocator
93 U_->provideAllocator = luaG_provide_protected_allocator;
94 }
95 } else {
96 // just grab whatever allocator was provided to lua_newstate
97 U_->protectedAllocator.initFrom(L_);
98 }
99 lua_pop(L_, 1); // L_: settings
100 STACK_CHECK(L_, 1);
101
102 lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator"
103 {
104 char const* allocator = lua_tostring(L_, -1);
105 if (strcmp(allocator, "libc") == 0) {
106 U_->internalAllocator = AllocatorDefinition{ libc_lua_Alloc, nullptr };
107 } else if (U_->provideAllocator == luaG_provide_protected_allocator) {
108 // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case.
109 U_->internalAllocator = U_->protectedAllocator.makeDefinition();
110 } else {
111 // no protection required, just use whatever we have as-is.
112 U_->internalAllocator = U_->protectedAllocator;
113 }
114 }
115 lua_pop(L_, 1); // L_: settings
116 STACK_CHECK(L_, 1);
117}
118
119// #################################################################################################
120
121[[nodiscard]] static int dummy_writer([[maybe_unused]] lua_State* L_, [[maybe_unused]] void const* p_, [[maybe_unused]] size_t sz_, [[maybe_unused]] void* ud_) 46[[nodiscard]] static int dummy_writer([[maybe_unused]] lua_State* L_, [[maybe_unused]] void const* p_, [[maybe_unused]] size_t sz_, [[maybe_unused]] void* ud_)
122{ 47{
123 return 666; 48 return 666;
@@ -138,14 +63,7 @@ void initialize_allocator_function(Universe* U_, lua_State* L_)
138 * +-----------------+----------+------------+----------+ 63 * +-----------------+----------+------------+----------+
139 */ 64 */
140 65
141enum class FuncSubType 66[[nodiscard]] FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i)
142{
143 Bytecode,
144 Native,
145 FastJIT
146};
147
148FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i)
149{ 67{
150 if (lua_tocfunction(L_, _i)) { // nullptr for LuaJIT-fast && bytecode functions 68 if (lua_tocfunction(L_, _i)) { // nullptr for LuaJIT-fast && bytecode functions
151 return FuncSubType::Native; 69 return FuncSubType::Native;
@@ -430,227 +348,6 @@ void populate_func_lookup_table(lua_State* L_, int i_, char const* name_)
430 348
431// ################################################################################################# 349// #################################################################################################
432 350
433/*---=== Inter-state copying ===---*/
434
435// xxh64 of string "kMtIdRegKey" generated at https://www.pelock.com/products/hash-calculator
436static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull };
437
438// get a unique ID for metatable at [i].
439[[nodiscard]] static lua_Integer get_mt_id(Universe* U_, lua_State* L_, int idx_)
440{
441 idx_ = lua_absindex(L_, idx_);
442
443 STACK_GROW(L_, 3);
444
445 STACK_CHECK_START_REL(L_, 0);
446 std::ignore = kMtIdRegKey.getSubTable(L_, 0, 0); // L_: ... _R[kMtIdRegKey]
447 lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
448 lua_rawget(L_, -2); // L_: ... _R[kMtIdRegKey] mtk?
449
450 lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil
451 lua_pop(L_, 1); // L_: ... _R[kMtIdRegKey]
452 STACK_CHECK(L_, 1);
453
454 if (id == 0) {
455 id = U_->nextMetatableId.fetch_add(1, std::memory_order_relaxed);
456
457 // Create two-way references: id_uint <-> table
458 lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] {mt}
459 lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] {mt} id
460 lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
461
462 lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] id
463 lua_pushvalue(L_, idx_); // L_: ... _R[kMtIdRegKey] id {mt}
464 lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey]
465 }
466 lua_pop(L_, 1); // L_: ...
467 STACK_CHECK(L_, 0);
468
469 return id;
470}
471
472// #################################################################################################
473
474// function sentinel used to transfer native functions from/to keeper states
475[[nodiscard]] static int func_lookup_sentinel(lua_State* L_)
476{
477 raise_luaL_error(L_, "function lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
478}
479
480// #################################################################################################
481
482// function sentinel used to transfer native table from/to keeper states
483[[nodiscard]] static int table_lookup_sentinel(lua_State* L_)
484{
485 raise_luaL_error(L_, "table lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
486}
487
488// #################################################################################################
489
490// function sentinel used to transfer cloned full userdata from/to keeper states
491[[nodiscard]] static int userdata_clone_sentinel(lua_State* L_)
492{
493 raise_luaL_error(L_, "userdata clone sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1)));
494}
495
496// #################################################################################################
497
498// retrieve the name of a function/table in the lookup database
499[[nodiscard]] static char const* find_lookup_name(lua_State* L_, int i_, LookupMode mode_, char const* upName_, size_t* len_)
500{
501 LUA_ASSERT(L_, lua_isfunction(L_, i_) || lua_istable(L_, i_)); // L_: ... v ...
502 STACK_CHECK_START_REL(L_, 0);
503 STACK_GROW(L_, 3); // up to 3 slots are necessary on error
504 if (mode_ == LookupMode::FromKeeper) {
505 lua_CFunction f = lua_tocfunction(L_, i_); // should *always* be one of the function sentinels
506 if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) {
507 lua_getupvalue(L_, i_, 1); // L_: ... v ... "f.q.n"
508 } else {
509 // if this is not a sentinel, this is some user-created table we wanted to lookup
510 LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i_));
511 // push anything that will convert to nullptr string
512 lua_pushnil(L_); // L_: ... v ... nil
513 }
514 } else {
515 // fetch the name from the source state's lookup table
516 kLookupRegKey.pushValue(L_); // L_: ... v ... {}
517 STACK_CHECK(L_, 1);
518 LUA_ASSERT(L_, lua_istable(L_, -1));
519 lua_pushvalue(L_, i_); // L_: ... v ... {} v
520 lua_rawget(L_, -2); // L_: ... v ... {} "f.q.n"
521 }
522 char const* fqn{ lua_tolstring(L_, -1, len_) };
523 DEBUGSPEW_CODE(Universe* const U = universe_get(L_));
524 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END(U), fqn));
525 // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database
526 lua_pop(L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // L_: ... v ...
527 STACK_CHECK(L_, 0);
528 if (nullptr == fqn && !lua_istable(L_, i_)) { // raise an error if we try to send an unknown function (but not for tables)
529 *len_ = 0; // just in case
530 // try to discover the name of the function we want to send
531 lua_getglobal(L_, "decoda_name"); // L_: ... v ... decoda_name
532 char const* from{ lua_tostring(L_, -1) };
533 lua_pushcfunction(L_, luaG_nameof); // L_: ... v ... decoda_name luaG_nameof
534 lua_pushvalue(L_, i_); // L_: ... v ... decoda_name luaG_nameof t
535 lua_call(L_, 1, 2); // L_: ... v ... decoda_name "type" "name"|nil
536 char const* typewhat{ (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : luaL_typename(L_, -2) };
537 // second return value can be nil if the table was not found
538 // probable reason: the function was removed from the source Lua state before Lanes was required.
539 char const *what, *gotchaA, *gotchaB;
540 if (lua_isnil(L_, -1)) {
541 gotchaA = " referenced by";
542 gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)";
543 what = upName_;
544 } else {
545 gotchaA = "";
546 gotchaB = "";
547 what = (lua_type(L_, -1) == LUA_TSTRING) ? lua_tostring(L_, -1) : luaL_typename(L_, -1);
548 }
549 raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB);
550 }
551 STACK_CHECK(L_, 0);
552 return fqn;
553}
554
555// #################################################################################################
556
557// Push a looked-up table, or nothing if we found nothing
558[[nodiscard]] bool InterCopyContext::lookup_table() const
559{
560 // get the name of the table we want to send
561 size_t len;
562 char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len);
563 if (nullptr == fqn) { // name not found, it is some user-created table
564 return false;
565 }
566 // push the equivalent table in the destination's stack, retrieved from the lookup table
567 STACK_CHECK_START_REL(L2, 0);
568 STACK_GROW(L2, 3); // up to 3 slots are necessary on error
569 switch (mode) {
570 default: // shouldn't happen, in theory...
571 raise_luaL_error(getErrL(), "internal error: unknown lookup mode");
572 break;
573
574 case LookupMode::ToKeeper:
575 // push a sentinel closure that holds the lookup name as upvalue
576 lua_pushlstring(L2, fqn, len); // L1: ... t ... L2: "f.q.n"
577 lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f
578 break;
579
580 case LookupMode::LaneBody:
581 case LookupMode::FromKeeper:
582 kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {}
583 STACK_CHECK(L2, 1);
584 LUA_ASSERT(L1, lua_istable(L2, -1));
585 lua_pushlstring(L2, fqn, len); // L2: {} "f.q.n"
586 lua_rawget(L2, -2); // L2: {} t
587 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead)
588 // but not when we extract something out of a keeper, as there is nothing to clone!
589 if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody) {
590 lua_pop(L2, 2); // L1: ... t ... L2:
591 STACK_CHECK(L2, 0);
592 return false;
593 } else if (!lua_istable(L2, -1)) { // this can happen if someone decides to replace same already registered item (for a example a standard lib function) with a table
594 lua_getglobal(L1, "decoda_name"); // L1: ... t ... decoda_name
595 char const* from{ lua_tostring(L1, -1) };
596 lua_pop(L1, 1); // L1: ... t ...
597 lua_getglobal(L2, "decoda_name"); // L1: ... t ... L2: {} t decoda_name
598 char const* to{ lua_tostring(L2, -1) };
599 lua_pop(L2, 1); // L1: ... t ... L2: {} t
600 raise_luaL_error(
601 getErrL(),
602 "%s: source table '%s' found as %s in %s destination transfer database.",
603 from ? from : "main",
604 fqn,
605 lua_typename(L2, lua_type_as_enum(L2, -1)),
606 to ? to : "main"
607 );
608 }
609 lua_remove(L2, -2); // L1: ... t ... L2: t
610 break;
611 }
612 STACK_CHECK(L2, 1);
613 return true;
614}
615
616// #################################################################################################
617
618// Check if we've already copied the same table from 'L1', and reuse the old copy. This allows table upvalues shared by multiple
619// local functions to point to the same table, also in the target.
620// Always pushes a table to 'L2'.
621// Returns true if the table was cached (no need to fill it!); false if it's a virgin.
622[[nodiscard]] bool InterCopyContext::push_cached_table() const
623{
624 void const* p{ lua_topointer(L1, L1_i) };
625
626 LUA_ASSERT(L1, L2_cache_i != 0);
627 STACK_GROW(L2, 3);
628 STACK_CHECK_START_REL(L2, 0);
629
630 // We don't need to use the from state ('L1') in ID since the life span
631 // is only for the duration of a copy (both states are locked).
632 // push a light userdata uniquely representing the table
633 lua_pushlightuserdata(L2, const_cast<void*>(p)); // L1: ... t ... L2: ... p
634
635 // fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1));
636
637 lua_rawget(L2, L2_cache_i); // L1: ... t ... L2: ... {cached|nil}
638 bool const not_found_in_cache{ lua_isnil(L2, -1) };
639 if (not_found_in_cache) {
640 // create a new entry in the cache
641 lua_pop(L2, 1); // L1: ... t ... L2: ...
642 lua_newtable(L2); // L1: ... t ... L2: ... {}
643 lua_pushlightuserdata(L2, const_cast<void*>(p)); // L1: ... t ... L2: ... {} p
644 lua_pushvalue(L2, -2); // L1: ... t ... L2: ... {} p {}
645 lua_rawset(L2, L2_cache_i); // L1: ... t ... L2: ... {}
646 }
647 STACK_CHECK(L2, 1);
648 LUA_ASSERT(L1, lua_istable(L2, -1));
649 return !not_found_in_cache;
650}
651
652// #################################################################################################
653
654// Return some name helping to identify an object 351// Return some name helping to identify an object
655[[nodiscard]] static int DiscoverObjectNameRecur(lua_State* L_, int shortest_, int depth_) 352[[nodiscard]] static int DiscoverObjectNameRecur(lua_State* L_, int shortest_, int depth_)
656{ 353{
@@ -826,1001 +523,3 @@ int luaG_nameof(lua_State* L_)
826 lua_replace(L_, -3); // L_: "type" "result" 523 lua_replace(L_, -3); // L_: "type" "result"
827 return 2; 524 return 2;
828} 525}
829
830// #################################################################################################
831
832// Push a looked-up native/LuaJIT function.
833void InterCopyContext::lookup_native_func() const
834{
835 // get the name of the function we want to send
836 size_t len;
837 char const* const fqn{ find_lookup_name(L1, L1_i, mode, name, &len) };
838 // push the equivalent function in the destination's stack, retrieved from the lookup table
839 STACK_CHECK_START_REL(L2, 0);
840 STACK_GROW(L2, 3); // up to 3 slots are necessary on error
841 switch (mode) {
842 default: // shouldn't happen, in theory...
843 raise_luaL_error(getErrL(), "internal error: unknown lookup mode");
844 break;
845
846 case LookupMode::ToKeeper:
847 // push a sentinel closure that holds the lookup name as upvalue
848 lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: "f.q.n"
849 lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f
850 break;
851
852 case LookupMode::LaneBody:
853 case LookupMode::FromKeeper:
854 kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {}
855 STACK_CHECK(L2, 1);
856 LUA_ASSERT(L1, lua_istable(L2, -1));
857 lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: {} "f.q.n"
858 lua_rawget(L2, -2); // L1: ... f ... L2: {} f
859 // nil means we don't know how to transfer stuff: user should do something
860 // anything other than function or table should not happen!
861 if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) {
862 lua_getglobal(L1, "decoda_name"); // L1: ... f ... decoda_name
863 char const* const from{ lua_tostring(L1, -1) };
864 lua_pop(L1, 1); // L1: ... f ...
865 lua_getglobal(L2, "decoda_name"); // L1: ... f ... L2: {} f decoda_name
866 char const* const to{ lua_tostring(L2, -1) };
867 lua_pop(L2, 1); // L2: {} f
868 // 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
869 raise_luaL_error(
870 getErrL()
871 , "%s%s: function '%s' not found in %s destination transfer database."
872 , lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN "
873 , from ? from : "main"
874 , fqn
875 , to ? to : "main"
876 );
877 return;
878 }
879 lua_remove(L2, -2); // L2: f
880 break;
881
882 /* keep it in case I need it someday, who knows...
883 case LookupMode::RawFunctions:
884 {
885 int n;
886 char const* upname;
887 lua_CFunction f = lua_tocfunction( L, i);
888 // copy upvalues
889 for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) {
890 luaG_inter_move( U, L, L2, 1, mode_); // L2: [up[,up ...]]
891 }
892 lua_pushcclosure( L2, f, n); // L2:
893 }
894 break;
895 */
896 }
897 STACK_CHECK(L2, 1);
898}
899
900// #################################################################################################
901
902#if USE_DEBUG_SPEW()
903static char const* lua_type_names[] = {
904 "LUA_TNIL"
905 , "LUA_TBOOLEAN"
906 , "LUA_TLIGHTUSERDATA"
907 , "LUA_TNUMBER"
908 , "LUA_TSTRING"
909 , "LUA_TTABLE"
910 , "LUA_TFUNCTION"
911 , "LUA_TUSERDATA"
912 , "LUA_TTHREAD"
913 , "<LUA_NUMTAGS>" // not really a type
914 , "LUA_TJITCDATA" // LuaJIT specific
915};
916static char const* vt_names[] = {
917 "VT::NORMAL"
918 , "VT::KEY"
919 , "VT::METATABLE"
920};
921#endif // USE_DEBUG_SPEW()
922
923// #################################################################################################
924
925// Lua 5.4.3 style of dumping (see lstrlib.c)
926// we have to do it that way because we can't unbalance the stack between buffer operations
927// namely, this means we can't push a function on top of the stack *after* we initialize the buffer!
928// luckily, this also works with earlier Lua versions
929[[nodiscard]] static int buf_writer(lua_State* L_, void const* b_, size_t size_, void* ud_)
930{
931 luaL_Buffer* const B{ static_cast<luaL_Buffer*>(ud_) };
932 if (!B->L) {
933 luaL_buffinit(L_, B);
934 }
935 luaL_addlstring(B, static_cast<char const*>(b_), size_);
936 return 0;
937}
938
939// #################################################################################################
940
941// Copy a function over, which has not been found in the cache.
942// L2 has the cache key for this function at the top of the stack
943void InterCopyContext::copy_func() const
944{
945 LUA_ASSERT(L1, L2_cache_i != 0); // L2: ... {cache} ... p
946 STACK_GROW(L1, 2);
947 STACK_CHECK_START_REL(L1, 0);
948
949 // 'lua_dump()' needs the function at top of stack
950 // if already on top of the stack, no need to push again
951 bool const needToPush{ L1_i != lua_gettop(L1) };
952 if (needToPush) {
953 lua_pushvalue(L1, L1_i); // L1: ... f
954 }
955
956 //
957 // "value returned is the error code returned by the last call
958 // to the writer" (and we only return 0)
959 // not sure this could ever fail but for memory shortage reasons
960 // last parameter is Lua 5.4-specific (no stripping)
961 luaL_Buffer B;
962 B.L = nullptr;
963 if (lua504_dump(L1, buf_writer, &B, 0) != 0) {
964 raise_luaL_error(getErrL(), "internal error: function dump failed.");
965 }
966
967 // pushes dumped string on 'L1'
968 luaL_pushresult(&B); // L1: ... f b
969
970 // if not pushed, no need to pop
971 if (needToPush) {
972 lua_remove(L1, -2); // L1: ... b
973 }
974
975 // transfer the bytecode, then the upvalues, to create a similar closure
976 {
977 char const* fname = nullptr;
978#define LOG_FUNC_INFO 0
979#if LOG_FUNC_INFO
980 // "To get information about a function you push it onto the
981 // stack and start the what string with the character '>'."
982 //
983 {
984 lua_Debug ar;
985 lua_pushvalue(L1, L1_i); // L1: ... b f
986 // fills 'fname' 'namewhat' and 'linedefined', pops function
987 lua_getinfo(L1, ">nS", &ar); // L1: ... b
988 fname = ar.namewhat;
989 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "FNAME: %s @ %d" INDENT_END(U), ar.short_src, ar.linedefined)); // just gives nullptr
990 }
991#endif // LOG_FUNC_INFO
992 {
993 size_t sz;
994 char const* s = lua_tolstring(L1, -1, &sz); // L1: ... b
995 LUA_ASSERT(L1, s && sz);
996 STACK_GROW(L2, 2);
997 // Note: Line numbers seem to be taken precisely from the
998 // original function. 'fname' is not used since the chunk
999 // is precompiled (it seems...).
1000 //
1001 // TBD: Can we get the function's original name through, as well?
1002 //
1003 if (luaL_loadbuffer(L2, s, sz, fname) != 0) { // L2: ... {cache} ... p function
1004 // chunk is precompiled so only LUA_ERRMEM can happen
1005 // "Otherwise, it pushes an error message"
1006 //
1007 STACK_GROW(L1, 1);
1008 raise_luaL_error(getErrL(), "%s: %s", fname, lua_tostring(L2, -1));
1009 }
1010 // remove the dumped string
1011 lua_pop(L1, 1); // ...
1012 // now set the cache as soon as we can.
1013 // this is necessary if one of the function's upvalues references it indirectly
1014 // we need to find it in the cache even if it isn't fully transfered yet
1015 lua_insert(L2, -2); // L2: ... {cache} ... function p
1016 lua_pushvalue(L2, -2); // L2: ... {cache} ... function p function
1017 // cache[p] = function
1018 lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function
1019 }
1020 STACK_CHECK(L1, 0);
1021
1022 /* push over any upvalues; references to this function will come from
1023 * cache so we don't end up in eternal loop.
1024 * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy!
1025 * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state!
1026 */
1027 int n{ 0 };
1028 {
1029 InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} };
1030#if LUA_VERSION_NUM >= 502
1031 // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default)
1032 // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state...
1033 // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table
1034 lua_pushglobaltable(L1); // L1: ... _G
1035#endif // LUA_VERSION_NUM
1036 for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n]
1037 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, c.name));
1038#if LUA_VERSION_NUM >= 502
1039 if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table?
1040 DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n"));
1041 lua_pushglobaltable(L2); // L2: ... {cache} ... function <upvalues>
1042 } else
1043#endif // LUA_VERSION_NUM
1044 {
1045 DEBUGSPEW_CODE(fprintf(stderr, "copying value\n"));
1046 c.L1_i = SourceIndex{ lua_gettop(L1) };
1047 if (!c.inter_copy_one()) { // L2: ... {cache} ... function <upvalues>
1048 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
1049 }
1050 }
1051 lua_pop(L1, 1); // L1: ... _G
1052 }
1053#if LUA_VERSION_NUM >= 502
1054 lua_pop(L1, 1); // L1: ...
1055#endif // LUA_VERSION_NUM
1056 }
1057 // L2: ... {cache} ... function + 'n' upvalues (>=0)
1058
1059 STACK_CHECK(L1, 0);
1060
1061 // Set upvalues (originally set to 'nil' by 'lua_load')
1062 for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) {
1063 char const* rc{ lua_setupvalue(L2, func_index, n) }; // L2: ... {cache} ... function
1064 //
1065 // "assigns the value at the top of the stack to the upvalue and returns its name.
1066 // It also pops the value from the stack."
1067
1068 LUA_ASSERT(L1, rc); // not having enough slots?
1069 }
1070 // once all upvalues have been set we are left
1071 // with the function at the top of the stack // L2: ... {cache} ... function
1072 }
1073 STACK_CHECK(L1, 0);
1074}
1075
1076// #################################################################################################
1077
1078// Check if we've already copied the same function from 'L1', and reuse the old copy.
1079// Always pushes a function to 'L2'.
1080void InterCopyContext::copy_cached_func() const
1081{
1082 FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) };
1083 if (funcSubType == FuncSubType::Bytecode) {
1084 void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i));
1085 // TBD: Merge this and same code for tables
1086 LUA_ASSERT(L1, L2_cache_i != 0);
1087
1088 STACK_GROW(L2, 2);
1089
1090 // L2_cache[id_str]= function
1091 //
1092 STACK_CHECK_START_REL(L2, 0);
1093
1094 // We don't need to use the from state ('L1') in ID since the life span
1095 // is only for the duration of a copy (both states are locked).
1096
1097 // push a light userdata uniquely representing the function
1098 lua_pushlightuserdata(L2, aspointer); // L2: ... {cache} ... p
1099
1100 // fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1));
1101
1102 lua_pushvalue(L2, -1); // L2: ... {cache} ... p p
1103 lua_rawget(L2, L2_cache_i); // L2: ... {cache} ... p function|nil|true
1104
1105 if (lua_isnil(L2, -1)) { // function is unknown
1106 lua_pop(L2, 1); // L2: ... {cache} ... p
1107
1108 // Set to 'true' for the duration of creation; need to find self-references
1109 // via upvalues
1110 //
1111 // pushes a copy of the func, stores a reference in the cache
1112 copy_func(); // L2: ... {cache} ... function
1113 } else { // found function in the cache
1114 lua_remove(L2, -2); // L2: ... {cache} ... function
1115 }
1116 STACK_CHECK(L2, 1);
1117 LUA_ASSERT(L1, lua_isfunction(L2, -1));
1118 } else { // function is native/LuaJIT: no need to cache
1119 lookup_native_func(); // L2: ... {cache} ... function
1120 // if the function was in fact a lookup sentinel, we can either get a function or a table here
1121 LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1));
1122 }
1123}
1124
1125// #################################################################################################
1126
1127[[nodiscard]] bool InterCopyContext::push_cached_metatable() const
1128{
1129 STACK_CHECK_START_REL(L1, 0);
1130 if (!lua_getmetatable(L1, L1_i)) { // L1: ... mt
1131 STACK_CHECK(L1, 0);
1132 return false;
1133 }
1134 STACK_CHECK(L1, 1);
1135
1136 lua_Integer const mt_id{ get_mt_id(U, L1, -1) }; // Unique id for the metatable
1137
1138 STACK_CHECK_START_REL(L2, 0);
1139 STACK_GROW(L2, 4);
1140 // do we already know this metatable?
1141 std::ignore = kMtIdRegKey.getSubTable(L2, 0, 0); // L2: _R[kMtIdRegKey]
1142 lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] id
1143 lua_rawget(L2, -2); // L2: _R[kMtIdRegKey] mt|nil
1144 STACK_CHECK(L2, 2);
1145
1146 if (lua_isnil(L2, -1)) { // L2 did not know the metatable
1147 lua_pop(L2, 1); // L2: _R[kMtIdRegKey]
1148 InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name };
1149 if (!c.inter_copy_one()) { // L2: _R[kMtIdRegKey] mt?
1150 raise_luaL_error(getErrL(), "Error copying a metatable");
1151 }
1152
1153 STACK_CHECK(L2, 2); // L2: _R[kMtIdRegKey] mt
1154 // mt_id -> metatable
1155 lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt id
1156 lua_pushvalue(L2, -2); // L2: _R[kMtIdRegKey] mt id mt
1157 lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
1158
1159 // metatable -> mt_id
1160 lua_pushvalue(L2, -1); // L2: _R[kMtIdRegKey] mt mt
1161 lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt mt id
1162 lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt
1163 STACK_CHECK(L2, 2);
1164 }
1165 lua_remove(L2, -2); // L2: mt
1166
1167 lua_pop(L1, 1); // L1: ...
1168 STACK_CHECK(L2, 1);
1169 STACK_CHECK(L1, 0);
1170 return true;
1171}
1172
1173// #################################################################################################
1174
1175void InterCopyContext::inter_copy_keyvaluepair() const
1176{
1177 SourceIndex const val_i{ lua_gettop(L1) };
1178 SourceIndex const key_i{ val_i - 1 };
1179
1180 // For the key, only basic key types are copied over. others ignored
1181 InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name };
1182 if (!c.inter_copy_one()) {
1183 return;
1184 // we could raise an error instead of ignoring the table entry, like so:
1185 // raise_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));
1186 // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer?
1187 }
1188
1189 char* valPath{ nullptr };
1190 if (U->verboseErrors) {
1191 // for debug purposes, let's try to build a useful name
1192 if (lua_type(L1, key_i) == LUA_TSTRING) {
1193 char const* key{ lua_tostring(L1, key_i) };
1194 size_t const keyRawLen = lua_rawlen(L1, key_i);
1195 size_t const bufLen = strlen(name) + keyRawLen + 2;
1196 valPath = (char*) alloca(bufLen);
1197 sprintf(valPath, "%s.%*s", name, (int) keyRawLen, key);
1198 key = nullptr;
1199 }
1200#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
1201 else if (lua_isinteger(L1, key_i)) {
1202 lua_Integer const key{ lua_tointeger(L1, key_i) };
1203 valPath = (char*) alloca(strlen(name) + 32 + 3);
1204 sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key);
1205 }
1206#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
1207 else if (lua_type(L1, key_i) == LUA_TNUMBER) {
1208 lua_Number const key{ lua_tonumber(L1, key_i) };
1209 valPath = (char*) alloca(strlen(name) + 32 + 3);
1210 sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key);
1211 } else if (lua_type(L1, key_i) == LUA_TLIGHTUSERDATA) {
1212 void* const key{ lua_touserdata(L1, key_i) };
1213 valPath = (char*) alloca(strlen(name) + 16 + 5);
1214 sprintf(valPath, "%s[U:%p]", name, key);
1215 } else if (lua_type(L1, key_i) == LUA_TBOOLEAN) {
1216 int const key{ lua_toboolean(L1, key_i) };
1217 valPath = (char*) alloca(strlen(name) + 8);
1218 sprintf(valPath, "%s[%s]", name, key ? "true" : "false");
1219 }
1220 }
1221 c.L1_i = SourceIndex{ val_i };
1222 // Contents of metatables are copied with cache checking. important to detect loops.
1223 c.vt = VT::NORMAL;
1224 c.name = valPath ? valPath : name;
1225 if (c.inter_copy_one()) {
1226 LUA_ASSERT(L1, lua_istable(L2, -3));
1227 lua_rawset(L2, -3); // add to table (pops key & val)
1228 } else {
1229 raise_luaL_error(getErrL(), "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i));
1230 }
1231}
1232
1233// #################################################################################################
1234
1235[[nodiscard]] bool InterCopyContext::tryCopyClonable() const
1236{
1237 SourceIndex const L1i{ lua_absindex(L1, L1_i) };
1238 void* const source{ lua_touserdata(L1, L1i) };
1239
1240 STACK_CHECK_START_REL(L1, 0);
1241 STACK_CHECK_START_REL(L2, 0);
1242
1243 // Check if the source was already cloned during this copy
1244 lua_pushlightuserdata(L2, source); // L2: ... source
1245 lua_rawget(L2, L2_cache_i); // L2: ... clone?
1246 if (!lua_isnil(L2, -1)) {
1247 STACK_CHECK(L2, 1);
1248 return true;
1249 } else {
1250 lua_pop(L2, 1); // L2: ...
1251 }
1252 STACK_CHECK(L2, 0);
1253
1254 // no metatable? -> not clonable
1255 if (!lua_getmetatable(L1, L1i)) { // L1: ... mt?
1256 STACK_CHECK(L1, 0);
1257 return false;
1258 }
1259
1260 // no __lanesclone? -> not clonable
1261 lua_getfield(L1, -1, "__lanesclone"); // L1: ... mt __lanesclone?
1262 if (lua_isnil(L1, -1)) {
1263 lua_pop(L1, 2); // L1: ...
1264 STACK_CHECK(L1, 0);
1265 return false;
1266 }
1267
1268 // we need to copy over the uservalues of the userdata as well
1269 {
1270 int const mt{ lua_absindex(L1, -2) }; // L1: ... mt __lanesclone
1271 size_t const userdata_size{ lua_rawlen(L1, L1i) };
1272 // extract all the uservalues, but don't transfer them yet
1273 int uvi = 0;
1274 while (lua_getiuservalue(L1, L1i, ++uvi) != LUA_TNONE) {} // L1: ... mt __lanesclone [uv]+ nil
1275 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
1276 lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]+
1277 --uvi;
1278 // create the clone userdata with the required number of uservalue slots
1279 void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // L2: ... u
1280 // copy the metatable in the target state, and give it to the clone we put there
1281 InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name };
1282 if (c.inter_copy_one()) { // L2: ... u mt|sentinel
1283 if (LookupMode::ToKeeper == mode) { // L2: ... u sentinel
1284 LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel);
1285 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
1286 lua_getupvalue(L2, -1, 1); // L2: ... u sentinel fqn
1287 lua_remove(L2, -2); // L2: ... u fqn
1288 lua_insert(L2, -2); // L2: ... fqn u
1289 lua_pushcclosure(L2, userdata_clone_sentinel, 2); // L2: ... userdata_clone_sentinel
1290 } else { // from keeper or direct // L2: ... u mt
1291 LUA_ASSERT(L1, lua_istable(L2, -1));
1292 lua_setmetatable(L2, -2); // L2: ... u
1293 }
1294 STACK_CHECK(L2, 1);
1295 } else {
1296 raise_luaL_error(getErrL(), "Error copying a metatable");
1297 }
1298 // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel
1299 lua_pushlightuserdata(L2, source); // L2: ... u source
1300 lua_pushvalue(L2, -2); // L2: ... u source u
1301 lua_rawset(L2, L2_cache_i); // L2: ... u
1302 // make sure we have the userdata now
1303 if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel
1304 lua_getupvalue(L2, -1, 2); // L2: ... userdata_clone_sentinel u
1305 }
1306 // assign uservalues
1307 while (uvi > 0) {
1308 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
1309 if (!c.inter_copy_one()) { // L2: ... u uv
1310 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
1311 }
1312 lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]*
1313 // this pops the value from the stack
1314 lua_setiuservalue(L2, -2, uvi); // L2: ... u
1315 --uvi;
1316 }
1317 // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination
1318 if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel u
1319 lua_pop(L2, 1); // L2: ... userdata_clone_sentinel
1320 }
1321 STACK_CHECK(L2, 1);
1322 STACK_CHECK(L1, 2);
1323 // call cloning function in source state to perform the actual memory cloning
1324 lua_pushlightuserdata(L1, clone); // L1: ... mt __lanesclone clone
1325 lua_pushlightuserdata(L1, source); // L1: ... mt __lanesclone clone source
1326 lua_pushinteger(L1, static_cast<lua_Integer>(userdata_size)); // L1: ... mt __lanesclone clone source size
1327 lua_call(L1, 3, 0); // L1: ... mt
1328 STACK_CHECK(L1, 1);
1329 }
1330
1331 STACK_CHECK(L2, 1);
1332 lua_pop(L1, 1); // L1: ...
1333 STACK_CHECK(L1, 0);
1334 return true;
1335}
1336
1337// #################################################################################################
1338
1339[[nodiscard]] bool InterCopyContext::inter_copy_userdata() const
1340{
1341 STACK_CHECK_START_REL(L1, 0);
1342 STACK_CHECK_START_REL(L2, 0);
1343 if (vt == VT::KEY) {
1344 return false;
1345 }
1346
1347 // try clonable userdata first
1348 if (tryCopyClonable()) {
1349 STACK_CHECK(L1, 0);
1350 STACK_CHECK(L2, 1);
1351 return true;
1352 }
1353
1354 STACK_CHECK(L1, 0);
1355 STACK_CHECK(L2, 0);
1356
1357 // Allow only deep userdata entities to be copied across
1358 DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n"));
1359 if (tryCopyDeep()) {
1360 STACK_CHECK(L1, 0);
1361 STACK_CHECK(L2, 1);
1362 return true;
1363 }
1364
1365 STACK_CHECK(L1, 0);
1366 STACK_CHECK(L2, 0);
1367
1368 // Not a deep or clonable full userdata
1369 if (U->demoteFullUserdata) { // attempt demotion to light userdata
1370 void* const lud{ lua_touserdata(L1, L1_i) };
1371 lua_pushlightuserdata(L2, lud);
1372 } else { // raise an error
1373 raise_luaL_error(getErrL(), "can't copy non-deep full userdata across lanes");
1374 }
1375
1376 STACK_CHECK(L2, 1);
1377 STACK_CHECK(L1, 0);
1378 return true;
1379}
1380
1381// #################################################################################################
1382
1383[[nodiscard]] bool InterCopyContext::inter_copy_function() const
1384{
1385 if (vt == VT::KEY) {
1386 return false;
1387 }
1388
1389 STACK_CHECK_START_REL(L1, 0);
1390 STACK_CHECK_START_REL(L2, 0);
1391 DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
1392
1393 if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) { // we are actually copying a clonable full userdata from a keeper
1394 // clone the full userdata again
1395
1396 // let's see if we already restored this userdata
1397 lua_getupvalue(L1, L1_i, 2); // L1: ... u
1398 void* source = lua_touserdata(L1, -1);
1399 lua_pushlightuserdata(L2, source); // L2: ... source
1400 lua_rawget(L2, L2_cache_i); // L2: ... u?
1401 if (!lua_isnil(L2, -1)) {
1402 lua_pop(L1, 1); // L1: ...
1403 STACK_CHECK(L1, 0);
1404 STACK_CHECK(L2, 1);
1405 return true;
1406 }
1407 lua_pop(L2, 1); // L2: ...
1408
1409 // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself
1410 bool const found{ lookup_table() }; // L2: ... mt?
1411 if (!found) {
1412 STACK_CHECK(L2, 0);
1413 return false;
1414 }
1415 // 'L1_i' slot was the proxy closure, but from now on we operate onthe actual userdata we extracted from it
1416 SourceIndex const source_i{ lua_gettop(L1) };
1417 source = lua_touserdata(L1, -1);
1418 void* clone{ nullptr };
1419 // get the number of bytes to allocate for the clone
1420 size_t const userdata_size{ lua_rawlen(L1, -1) };
1421 {
1422 // extract uservalues (don't transfer them yet)
1423 int uvi = 0;
1424 while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // L1: ... u uv
1425 // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now
1426 lua_pop(L1, 1); // L1: ... u [uv]*
1427 --uvi;
1428 STACK_CHECK(L1, uvi + 1);
1429 // create the clone userdata with the required number of uservalue slots
1430 clone = lua_newuserdatauv(L2, userdata_size, uvi); // L2: ... mt u
1431 // add it in the cache
1432 lua_pushlightuserdata(L2, source); // L2: ... mt u source
1433 lua_pushvalue(L2, -2); // L2: ... mt u source u
1434 lua_rawset(L2, L2_cache_i); // L2: ... mt u
1435 // set metatable
1436 lua_pushvalue(L2, -2); // L2: ... mt u mt
1437 lua_setmetatable(L2, -2); // L2: ... mt u
1438 // transfer and assign uservalues
1439 InterCopyContext c{ *this };
1440 while (uvi > 0) {
1441 c.L1_i = SourceIndex{ lua_absindex(L1, -1) };
1442 if (!c.inter_copy_one()) { // L2: ... mt u uv
1443 raise_luaL_error(getErrL(), "Cannot copy upvalue type '%s'", luaL_typename(L1, -1));
1444 }
1445 lua_pop(L1, 1); // L1: ... u [uv]*
1446 // this pops the value from the stack
1447 lua_setiuservalue(L2, -2, uvi); // L2: ... mt u
1448 --uvi;
1449 }
1450 // when we are done, all uservalues are popped from the stack, we can pop the source as well
1451 lua_pop(L1, 1); // L1: ...
1452 STACK_CHECK(L1, 0);
1453 STACK_CHECK(L2, 2); // L2: ... mt u
1454 }
1455 // perform the custom cloning part
1456 lua_insert(L2, -2); // L2: ... u mt
1457 // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with
1458 lua_getfield(L2, -1, "__lanesclone"); // L2: ... u mt __lanesclone
1459 lua_remove(L2, -2); // L2: ... u __lanesclone
1460 lua_pushlightuserdata(L2, clone); // L2: ... u __lanesclone clone
1461 lua_pushlightuserdata(L2, source); // L2: ... u __lanesclone clone source
1462 lua_pushinteger(L2, userdata_size); // L2: ... u __lanesclone clone source size
1463 // clone:__lanesclone(dest, source, size)
1464 lua_call(L2, 3, 0); // L2: ... u
1465 } else { // regular function
1466 DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name));
1467 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1468 copy_cached_func(); // L2: ... f
1469 }
1470 STACK_CHECK(L2, 1);
1471 STACK_CHECK(L1, 0);
1472 return true;
1473}
1474
1475// #################################################################################################
1476
1477[[nodiscard]] bool InterCopyContext::inter_copy_table() const
1478{
1479 if (vt == VT::KEY) {
1480 return false;
1481 }
1482
1483 STACK_CHECK_START_REL(L1, 0);
1484 STACK_CHECK_START_REL(L2, 0);
1485 DEBUGSPEW_CODE(fprintf(stderr, "TABLE %s\n", name));
1486
1487 /*
1488 * 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?)
1489 * 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
1490 */
1491 if (lookup_table()) {
1492 LUA_ASSERT(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
1493 return true;
1494 }
1495
1496 /* Check if we've already copied the same table from 'L1' (during this transmission), and
1497 * reuse the old copy. This allows table upvalues shared by multiple
1498 * local functions to point to the same table, also in the target.
1499 * Also, this takes care of cyclic tables and multiple references
1500 * to the same subtable.
1501 *
1502 * Note: Even metatables need to go through this test; to detect
1503 * loops such as those in required module tables (getmetatable(lanes).lanes == lanes)
1504 */
1505 if (push_cached_table()) {
1506 LUA_ASSERT(L1, lua_istable(L2, -1)); // from cache
1507 return true;
1508 }
1509 LUA_ASSERT(L1, lua_istable(L2, -1));
1510
1511 STACK_GROW(L1, 2);
1512 STACK_GROW(L2, 2);
1513
1514 lua_pushnil(L1); // start iteration
1515 while (lua_next(L1, L1_i)) {
1516 // need a function to prevent overflowing the stack with verboseErrors-induced alloca()
1517 inter_copy_keyvaluepair();
1518 lua_pop(L1, 1); // pop value (next round)
1519 }
1520 STACK_CHECK(L1, 0);
1521 STACK_CHECK(L2, 1);
1522
1523 // Metatables are expected to be immutable, and copied only once.
1524 if (push_cached_metatable()) { // L2: ... t mt?
1525 lua_setmetatable(L2, -2); // L2: ... t
1526 }
1527 STACK_CHECK(L2, 1);
1528 STACK_CHECK(L1, 0);
1529 return true;
1530}
1531
1532// #################################################################################################
1533
1534[[nodiscard]] bool InterCopyContext::inter_copy_boolean() const
1535{
1536 int const v{ lua_toboolean(L1, L1_i) };
1537 DEBUGSPEW_CODE(fprintf(stderr, "%s\n", v ? "true" : "false"));
1538 lua_pushboolean(L2, v);
1539 return true;
1540}
1541
1542// #################################################################################################
1543
1544[[nodiscard]] bool InterCopyContext::inter_copy_lightuserdata() const
1545{
1546 void* const p{ lua_touserdata(L1, L1_i) };
1547 DEBUGSPEW_CODE(fprintf(stderr, "%p\n", p));
1548 lua_pushlightuserdata(L2, p);
1549 return true;
1550}
1551
1552// #################################################################################################
1553
1554[[nodiscard]] bool InterCopyContext::inter_copy_nil() const
1555{
1556 if (vt == VT::KEY) {
1557 return false;
1558 }
1559 lua_pushnil(L2);
1560 return true;
1561}
1562
1563// #################################################################################################
1564
1565[[nodiscard]] bool InterCopyContext::inter_copy_number() const
1566{
1567 // LNUM patch support (keeping integer accuracy)
1568#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
1569 if (lua_isinteger(L1, L1_i)) {
1570 lua_Integer const v{ lua_tointeger(L1, L1_i) };
1571 DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v));
1572 lua_pushinteger(L2, v);
1573 } else
1574#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
1575 {
1576 lua_Number const v{ lua_tonumber(L1, L1_i) };
1577 DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", v));
1578 lua_pushnumber(L2, v);
1579 }
1580 return true;
1581}
1582
1583// #################################################################################################
1584
1585[[nodiscard]] bool InterCopyContext::inter_copy_string() const
1586{
1587 size_t len;
1588 char const* const s{ lua_tolstring(L1, L1_i, &len) };
1589 DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", s));
1590 lua_pushlstring(L2, s, len);
1591 return true;
1592}
1593
1594// #################################################################################################
1595
1596/*
1597 * Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove
1598 * the original value.
1599 *
1600 * NOTE: Both the states must be solely in the current OS thread's possession.
1601 *
1602 * 'i' is an absolute index (no -1, ...)
1603 *
1604 * Returns true if value was pushed, false if its type is non-supported.
1605 */
1606[[nodiscard]] bool InterCopyContext::inter_copy_one() const
1607{
1608 static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING);
1609 STACK_GROW(L2, 1);
1610 STACK_CHECK_START_REL(L1, 0);
1611 STACK_CHECK_START_REL(L2, 0);
1612
1613 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END(U)));
1614 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1615
1616 LuaType val_type{ lua_type_as_enum(L1, L1_i) };
1617 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END(U), lua_type_names[static_cast<int>(val_type)], vt_names[static_cast<int>(vt)]));
1618
1619 // Non-POD can be skipped if its metatable contains { __lanesignore = true }
1620 if (((1 << static_cast<int>(val_type)) & kPODmask) == 0) {
1621 if (lua_getmetatable(L1, L1_i)) { // L1: ... mt
1622 lua_getfield(L1, -1, "__lanesignore"); // L1: ... mt ignore?
1623 if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) {
1624 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END(U)));
1625 val_type = LuaType::NIL;
1626 }
1627 lua_pop(L1, 2); // L1: ...
1628 }
1629 }
1630 STACK_CHECK(L1, 0);
1631
1632 // Lets push nil to L2 if the object should be ignored
1633 bool ret{ true };
1634 switch (val_type) {
1635 // Basic types allowed both as values, and as table keys
1636 case LuaType::BOOLEAN:
1637 ret = inter_copy_boolean();
1638 break;
1639 case LuaType::NUMBER:
1640 ret = inter_copy_number();
1641 break;
1642 case LuaType::STRING:
1643 ret = inter_copy_string();
1644 break;
1645 case LuaType::LIGHTUSERDATA:
1646 ret = inter_copy_lightuserdata();
1647 break;
1648
1649 // The following types are not allowed as table keys
1650 case LuaType::USERDATA:
1651 ret = inter_copy_userdata();
1652 break;
1653 case LuaType::NIL:
1654 ret = inter_copy_nil();
1655 break;
1656 case LuaType::FUNCTION:
1657 ret = inter_copy_function();
1658 break;
1659 case LuaType::TABLE:
1660 ret = inter_copy_table();
1661 break;
1662
1663 // The following types cannot be copied
1664 case LuaType::CDATA:
1665 [[fallthrough]];
1666 case LuaType::THREAD:
1667 ret = false;
1668 break;
1669 }
1670
1671 STACK_CHECK(L2, ret ? 1 : 0);
1672 STACK_CHECK(L1, 0);
1673 return ret;
1674}
1675
1676// #################################################################################################
1677
1678// Akin to 'lua_xmove' but copies values between _any_ Lua states.
1679// NOTE: Both the states must be solely in the current OS thread's possession.
1680[[nodiscard]] InterCopyResult InterCopyContext::inter_copy(int n_) const
1681{
1682 LUA_ASSERT(L1, vt == VT::NORMAL);
1683
1684 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END(U)));
1685 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1686
1687 int const top_L1{ lua_gettop(L1) };
1688 if (n_ > top_L1) {
1689 // requesting to copy more than is available?
1690 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END(U)));
1691 return InterCopyResult::NotEnoughValues;
1692 }
1693
1694 STACK_CHECK_START_REL(L2, 0);
1695 STACK_GROW(L2, n_ + 1);
1696
1697 /*
1698 * Make a cache table for the duration of this copy. Collects tables and
1699 * function entries, avoiding the same entries to be passed on as multiple
1700 * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner!
1701 */
1702 int const top_L2{ lua_gettop(L2) }; // L2: ...
1703 lua_newtable(L2); // L2: ... cache
1704
1705 char tmpBuf[16];
1706 char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" };
1707 InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf };
1708 bool copyok{ true };
1709 STACK_CHECK_START_REL(L1, 0);
1710 for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) {
1711 if (U->verboseErrors) {
1712 sprintf(tmpBuf, "arg_%d", j);
1713 }
1714 c.L1_i = SourceIndex{ i };
1715 copyok = c.inter_copy_one(); // L2: ... cache {}n
1716 if (!copyok) {
1717 break;
1718 }
1719 }
1720 STACK_CHECK(L1, 0);
1721
1722 if (copyok) {
1723 STACK_CHECK(L2, n_ + 1);
1724 // Remove the cache table. Persistent caching would cause i.e. multiple
1725 // messages passed in the same table to use the same table also in receiving end.
1726 lua_remove(L2, top_L2 + 1);
1727 return InterCopyResult::Success;
1728 }
1729
1730 // error -> pop everything from the target state stack
1731 lua_settop(L2, top_L2);
1732 STACK_CHECK(L2, 0);
1733 return InterCopyResult::Error;
1734}
1735
1736// #################################################################################################
1737
1738[[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const
1739{
1740 InterCopyResult const ret{ inter_copy(n_) };
1741 lua_pop(L1, n_);
1742 return ret;
1743}
1744
1745// #################################################################################################
1746
1747// transfers stuff from L1->_G["package"] to L2->_G["package"]
1748// returns InterCopyResult::Success if everything is fine
1749// returns InterCopyResult::Error if pushed an error message in L1
1750// else raise an error in L1
1751[[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const
1752{
1753 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END(U)));
1754
1755 class OnExit
1756 {
1757 private:
1758 lua_State* const L2;
1759 int const top_L2;
1760 DEBUGSPEW_CODE(DebugSpewIndentScope m_scope);
1761
1762 public:
1763 OnExit(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L2_)
1764 : L2{ L2_ }
1765 , top_L2{ lua_gettop(L2) } DEBUGSPEW_COMMA_PARAM(m_scope{ U_ })
1766 {
1767 }
1768
1769 ~OnExit()
1770 {
1771 lua_settop(L2, top_L2);
1772 }
1773 } onExit{ DEBUGSPEW_PARAM_COMMA(U) L2 };
1774
1775 STACK_CHECK_START_REL(L1, 0);
1776 if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) {
1777 lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i));
1778 STACK_CHECK(L1, 1);
1779 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
1780 if (mode == LookupMode::LaneBody) {
1781 raise_lua_error(getErrL()); // that's ok, getErrL() is L1 in that case
1782 }
1783 return InterCopyResult::Error;
1784 }
1785 if (luaG_getmodule(L2, LUA_LOADLIBNAME) == LuaType::NIL) { // package library not loaded: do nothing
1786 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END(U)));
1787 STACK_CHECK(L1, 0);
1788 return InterCopyResult::Success;
1789 }
1790
1791 InterCopyResult result{ InterCopyResult::Success };
1792 // package.loaders is renamed package.searchers in Lua 5.2
1793 // but don't copy it anyway, as the function names change depending on the slot index!
1794 // users should provide an on_state_create function to setup custom loaders instead
1795 // don't copy package.preload in keeper states (they don't know how to translate functions)
1796 char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr };
1797 for (char const* const entry : entries) {
1798 if (!entry) {
1799 continue;
1800 }
1801 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END(U), entry));
1802 lua_getfield(L1, L1_i, entry);
1803 if (lua_isnil(L1, -1)) {
1804 lua_pop(L1, 1);
1805 } else {
1806 {
1807 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
1808 result = inter_move(1); // moves the entry to L2
1809 STACK_CHECK(L1, 0);
1810 }
1811 if (result == InterCopyResult::Success) {
1812 lua_setfield(L2, -2, entry); // set package[entry]
1813 } else {
1814 lua_pushfstring(L1, "failed to copy package entry %s", entry);
1815 // raise the error when copying from lane to lane, else just leave it on the stack to be raised later
1816 if (mode == LookupMode::LaneBody) {
1817 raise_lua_error(getErrL());
1818 }
1819 lua_pop(L1, 1);
1820 break;
1821 }
1822 }
1823 }
1824 STACK_CHECK(L1, 0);
1825 return result;
1826}
diff --git a/src/tools.h b/src/tools.h
index 53d3a99..be76fd9 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -1,88 +1,30 @@
1#pragma once 1#pragma once
2 2
3#include "deep.h" 3#include "uniquekey.h"
4#include "macros_and_utils.h"
5 4
6// forwards
7class Universe; 5class Universe;
8 6
9// ################################################################################################# 7enum class LookupMode
10
11enum class VT
12{ 8{
13 NORMAL, // keep this one first so that it's the value we get when we default-construct 9 LaneBody, // send the lane body directly from the source to the destination lane. keep this one first so that it's the value we get when we default-construct
14 KEY, 10 ToKeeper, // send a function from a lane to a keeper state
15 METATABLE 11 FromKeeper // send a function from a keeper state to a lane
16}; 12};
17 13
18enum class InterCopyResult 14enum class FuncSubType
19{ 15{
20 Success, 16 Bytecode,
21 NotEnoughValues, 17 Native,
22 Error 18 FastJIT
23}; 19};
24 20
25// ################################################################################################# 21[[nodiscard]] FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i);
26
27using CacheIndex = Unique<int>;
28using SourceIndex = Unique<int>;
29class InterCopyContext
30{
31 public:
32 Universe* const U;
33 DestState const L2;
34 SourceState const L1;
35 CacheIndex const L2_cache_i;
36 SourceIndex L1_i; // that one can change when we reuse the context
37 VT vt; // that one can change when we reuse the context
38 LookupMode const mode;
39 char const* name; // that one can change when we reuse the context
40
41 private:
42 // when mode == LookupMode::FromKeeper, L1 is a keeper state and L2 is not, therefore L2 is the state where we want to raise the error
43 // whon mode != LookupMode::FromKeeper, L1 is not a keeper state, therefore L1 is the state where we want to raise the error
44 lua_State* getErrL() const { return (mode == LookupMode::FromKeeper) ? L2 : L1; }
45
46 // for use in copy_cached_func
47 void copy_func() const;
48 void lookup_native_func() const;
49
50 // for use in inter_copy_function
51 void copy_cached_func() const;
52 [[nodiscard]] bool lookup_table() const;
53
54 // for use in inter_copy_table
55 void inter_copy_keyvaluepair() const;
56 [[nodiscard]] bool push_cached_metatable() const;
57 [[nodiscard]] bool push_cached_table() const;
58
59 // for use in inter_copy_userdata
60 [[nodiscard]] bool tryCopyClonable() const;
61 [[nodiscard]] bool tryCopyDeep() const;
62
63 // copying a single Lua stack item
64 [[nodiscard]] bool inter_copy_boolean() const;
65 [[nodiscard]] bool inter_copy_function() const;
66 [[nodiscard]] bool inter_copy_lightuserdata() const;
67 [[nodiscard]] bool inter_copy_nil() const;
68 [[nodiscard]] bool inter_copy_number() const;
69 [[nodiscard]] bool inter_copy_string() const;
70 [[nodiscard]] bool inter_copy_table() const;
71 [[nodiscard]] bool inter_copy_userdata() const;
72
73 public:
74 [[nodiscard]] bool inter_copy_one() const;
75 [[nodiscard]] InterCopyResult inter_copy_package() const;
76 [[nodiscard]] InterCopyResult inter_copy(int n_) const;
77 [[nodiscard]] InterCopyResult inter_move(int n_) const;
78};
79 22
80// ################################################################################################# 23// #################################################################################################
81 24
82[[nodiscard]] int luaG_nameof(lua_State* L_); 25[[nodiscard]] int luaG_nameof(lua_State* L_);
83 26
84void populate_func_lookup_table(lua_State* L_, int i_, char const* name_); 27void populate_func_lookup_table(lua_State* L_, int i_, char const* name_);
85void initialize_allocator_function(Universe* U_, lua_State* L_);
86 28
87// ################################################################################################# 29// #################################################################################################
88 30
diff --git a/src/universe.cpp b/src/universe.cpp
index becffdd..a5c28f0 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -30,12 +30,9 @@ THE SOFTWARE.
30 30
31#include "universe.h" 31#include "universe.h"
32 32
33#include "cancel.h"
34#include "compat.h"
35#include "deep.h" 33#include "deep.h"
36#include "keeper.h" 34#include "keeper.h"
37#include "lanes_private.h" 35#include "lanes_private.h"
38#include "tools.h"
39 36
40// ################################################################################################# 37// #################################################################################################
41 38
diff --git a/src/universe.h b/src/universe.h
index 58ddffc..3adba4d 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -9,7 +9,6 @@ extern "C"
9} 9}
10#endif // __cplusplus 10#endif // __cplusplus
11 11
12#include "compat.h"
13#include "macros_and_utils.h" 12#include "macros_and_utils.h"
14#include "uniquekey.h" 13#include "uniquekey.h"
15 14