aboutsummaryrefslogtreecommitdiff
path: root/src/intercopycontext.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/intercopycontext.cpp1296
1 files changed, 1296 insertions, 0 deletions
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}