diff options
Diffstat (limited to 'src/tools.cpp')
-rw-r--r-- | src/tools.cpp | 1309 |
1 files changed, 4 insertions, 1305 deletions
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 | ||
10 | Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com> | 11 | Copyright (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 | ||
13 | Permission is hereby granted, free of charge, to any person obtaining a copy | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy |
14 | of this software and associated documentation files (the "Software"), to deal | 15 | of 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 | ||
46 | extern "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? | ||
70 | void 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 | ||
141 | enum class FuncSubType | 66 | [[nodiscard]] FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) |
142 | { | ||
143 | Bytecode, | ||
144 | Native, | ||
145 | FastJIT | ||
146 | }; | ||
147 | |||
148 | FuncSubType 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 | ||
436 | static 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. | ||
833 | void 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() | ||
903 | static 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 | }; | ||
916 | static 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 | ||
943 | void 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'. | ||
1080 | void 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 | |||
1175 | void 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 | } | ||