diff options
Diffstat (limited to '')
-rw-r--r-- | src/intercopycontext.cpp | 98 |
1 files changed, 84 insertions, 14 deletions
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp index 568e4cb..1be4c53 100644 --- a/src/intercopycontext.cpp +++ b/src/intercopycontext.cpp | |||
@@ -55,7 +55,7 @@ static int buf_writer(lua_State* L_, void const* b_, size_t size_, void* ud_) | |||
55 | 55 | ||
56 | // function sentinel used to transfer native functions from/to keeper states | 56 | // function sentinel used to transfer native functions from/to keeper states |
57 | [[nodiscard]] | 57 | [[nodiscard]] |
58 | static int func_lookup_sentinel(lua_State* L_) | 58 | static int func_lookup_sentinel(lua_State* const L_) |
59 | { | 59 | { |
60 | raise_luaL_error(L_, "function lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1))); | 60 | raise_luaL_error(L_, "function lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1))); |
61 | } | 61 | } |
@@ -64,7 +64,7 @@ static int func_lookup_sentinel(lua_State* L_) | |||
64 | 64 | ||
65 | // function sentinel used to transfer native table from/to keeper states | 65 | // function sentinel used to transfer native table from/to keeper states |
66 | [[nodiscard]] | 66 | [[nodiscard]] |
67 | static int table_lookup_sentinel(lua_State* L_) | 67 | static int table_lookup_sentinel(lua_State* const L_) |
68 | { | 68 | { |
69 | raise_luaL_error(L_, "table lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1))); | 69 | raise_luaL_error(L_, "table lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1))); |
70 | } | 70 | } |
@@ -73,23 +73,32 @@ static int table_lookup_sentinel(lua_State* L_) | |||
73 | 73 | ||
74 | // function sentinel used to transfer cloned full userdata from/to keeper states | 74 | // function sentinel used to transfer cloned full userdata from/to keeper states |
75 | [[nodiscard]] | 75 | [[nodiscard]] |
76 | static int userdata_clone_sentinel(lua_State* L_) | 76 | static int userdata_clone_sentinel(lua_State* const L_) |
77 | { | 77 | { |
78 | raise_luaL_error(L_, "userdata clone sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1))); | 78 | raise_luaL_error(L_, "userdata clone sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1))); |
79 | } | 79 | } |
80 | 80 | ||
81 | // ################################################################################################# | 81 | // ################################################################################################# |
82 | 82 | ||
83 | // retrieve the name of a function/table in the lookup database | 83 | // function sentinel used to transfer native table from/to keeper states |
84 | [[nodiscard]] | ||
85 | static int userdata_lookup_sentinel(lua_State* const L_) | ||
86 | { | ||
87 | raise_luaL_error(L_, "userdata lookup sentinel for %s, should never be called", lua_tostring(L_, lua_upvalueindex(1))); | ||
88 | } | ||
89 | |||
90 | // ################################################################################################# | ||
91 | |||
92 | // retrieve the name of a function/table/userdata in the lookup database | ||
84 | [[nodiscard]] | 93 | [[nodiscard]] |
85 | std::string_view InterCopyContext::findLookupName() const | 94 | std::string_view InterCopyContext::findLookupName() const |
86 | { | 95 | { |
87 | LUA_ASSERT(L1, lua_isfunction(L1, L1_i) || lua_istable(L1, L1_i)); // L1: ... v ... | 96 | LUA_ASSERT(L1, lua_isfunction(L1, L1_i) || lua_istable(L1, L1_i) || luaG_type(L1, L1_i) == LuaType::USERDATA); |
88 | STACK_CHECK_START_REL(L1, 0); | 97 | STACK_CHECK_START_REL(L1, 0); // L1: ... v ... |
89 | STACK_GROW(L1, 3); // up to 3 slots are necessary on error | 98 | STACK_GROW(L1, 3); // up to 3 slots are necessary on error |
90 | if (mode == LookupMode::FromKeeper) { | 99 | if (mode == LookupMode::FromKeeper) { |
91 | lua_CFunction const _f{ lua_tocfunction(L1, L1_i) }; // should *always* be one of the function sentinels | 100 | lua_CFunction const _f{ lua_tocfunction(L1, L1_i) }; // should *always* be one of the sentinel functions |
92 | if (_f == func_lookup_sentinel || _f == table_lookup_sentinel || _f == userdata_clone_sentinel) { | 101 | if (_f == func_lookup_sentinel || _f == table_lookup_sentinel || _f == userdata_clone_sentinel || _f == userdata_lookup_sentinel) { |
93 | lua_getupvalue(L1, L1_i, 1); // L1: ... v ... "f.q.n" | 102 | lua_getupvalue(L1, L1_i, 1); // L1: ... v ... "f.q.n" |
94 | } else { | 103 | } else { |
95 | // if this is not a sentinel, this is some user-created table we wanted to lookup | 104 | // if this is not a sentinel, this is some user-created table we wanted to lookup |
@@ -110,8 +119,8 @@ std::string_view InterCopyContext::findLookupName() const | |||
110 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database | 119 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database |
111 | lua_pop(L1, (mode == LookupMode::FromKeeper) ? 1 : 2); // L1: ... v ... | 120 | lua_pop(L1, (mode == LookupMode::FromKeeper) ? 1 : 2); // L1: ... v ... |
112 | STACK_CHECK(L1, 0); | 121 | STACK_CHECK(L1, 0); |
113 | if (_fqn.empty() && !lua_istable(L1, L1_i)) { // raise an error if we try to send an unknown function (but not for tables) | 122 | if (_fqn.empty() && !lua_istable(L1, L1_i)) { // raise an error if we try to send an unknown function/userdata (but not for tables) |
114 | // try to discover the name of the function we want to send | 123 | // try to discover the name of the function/userdata we want to send |
115 | kLaneNameRegKey.pushValue(L1); // L1: ... v ... lane_name | 124 | kLaneNameRegKey.pushValue(L1); // L1: ... v ... lane_name |
116 | std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; | 125 | std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; |
117 | lua_pushcfunction(L1, LG_nameof); // L1: ... v ... lane_name LG_nameof | 126 | lua_pushcfunction(L1, LG_nameof); // L1: ... v ... lane_name LG_nameof |
@@ -331,7 +340,8 @@ void InterCopyContext::lookupNativeFunction() const | |||
331 | lua_rawget(L2, -2); // L1: ... f ... L2: {} f | 340 | lua_rawget(L2, -2); // L1: ... f ... L2: {} f |
332 | // nil means we don't know how to transfer stuff: user should do something | 341 | // nil means we don't know how to transfer stuff: user should do something |
333 | // anything other than function or table should not happen! | 342 | // anything other than function or table should not happen! |
334 | if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) { | 343 | LuaType const _objType{ luaG_type(L2, kIdxTop) }; |
344 | if (_objType != LuaType::FUNCTION && _objType != LuaType::TABLE && _objType != LuaType::USERDATA) { | ||
335 | kLaneNameRegKey.pushValue(L1); // L1: ... f ... lane_name | 345 | kLaneNameRegKey.pushValue(L1); // L1: ... f ... lane_name |
336 | std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; | 346 | std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; |
337 | lua_pop(L1, 1); // L1: ... f ... | 347 | lua_pop(L1, 1); // L1: ... f ... |
@@ -340,9 +350,10 @@ void InterCopyContext::lookupNativeFunction() const | |||
340 | lua_pop(L2, 1); // L2: {} f | 350 | lua_pop(L2, 1); // L2: {} f |
341 | raise_luaL_error( | 351 | raise_luaL_error( |
342 | getErrL(), | 352 | getErrL(), |
343 | "%s%s: function '%s' not found in %s destination transfer database.", | 353 | "%s%s: %s '%s' not found in %s destination transfer database.", |
344 | lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN ", | 354 | lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN ", |
345 | _from.empty() ? "main" : _from.data(), | 355 | _from.empty() ? "main" : _from.data(), |
356 | luaG_typename(L2, _objType), | ||
346 | _fqn.data(), | 357 | _fqn.data(), |
347 | _to.empty() ? "main" : _to.data()); | 358 | _to.empty() ? "main" : _to.data()); |
348 | return; | 359 | return; |
@@ -397,8 +408,8 @@ void InterCopyContext::copyCachedFunction() const | |||
397 | LUA_ASSERT(L1, lua_isfunction(L2, -1)); | 408 | LUA_ASSERT(L1, lua_isfunction(L2, -1)); |
398 | } else { // function is native/LuaJIT: no need to cache | 409 | } else { // function is native/LuaJIT: no need to cache |
399 | lookupNativeFunction(); // L2: ... {cache} ... function | 410 | lookupNativeFunction(); // L2: ... {cache} ... function |
400 | // if the function was in fact a lookup sentinel, we can either get a function or a table here | 411 | // if the function was in fact a lookup sentinel, we can either get a function, table or full userdata here |
401 | LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1)); | 412 | LUA_ASSERT(L1, lua_isfunction(L2, kIdxTop) || lua_istable(L2, kIdxTop) || luaG_type(L2, kIdxTop) == LuaType::USERDATA); |
402 | } | 413 | } |
403 | } | 414 | } |
404 | 415 | ||
@@ -678,6 +689,59 @@ bool InterCopyContext::pushCachedTable() const | |||
678 | 689 | ||
679 | // ################################################################################################# | 690 | // ################################################################################################# |
680 | 691 | ||
692 | // Push a looked-up full userdata. | ||
693 | [[nodiscard]] | ||
694 | bool InterCopyContext::lookupUserdata() const | ||
695 | { | ||
696 | // get the name of the userdata we want to send | ||
697 | std::string_view const _fqn{ findLookupName() }; | ||
698 | // push the equivalent userdata in the destination's stack, retrieved from the lookup table | ||
699 | STACK_CHECK_START_REL(L2, 0); | ||
700 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error | ||
701 | switch (mode) { | ||
702 | default: // shouldn't happen, in theory... | ||
703 | raise_luaL_error(getErrL(), "internal error: unknown lookup mode"); | ||
704 | break; | ||
705 | |||
706 | case LookupMode::ToKeeper: | ||
707 | // push a sentinel closure that holds the lookup name as upvalue | ||
708 | luaG_pushstring(L2, _fqn); // L1: ... f ... L2: "f.q.n" | ||
709 | lua_pushcclosure(L2, userdata_lookup_sentinel, 1); // L1: ... f ... L2: f | ||
710 | break; | ||
711 | |||
712 | case LookupMode::LaneBody: | ||
713 | case LookupMode::FromKeeper: | ||
714 | kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {} | ||
715 | STACK_CHECK(L2, 1); | ||
716 | LUA_ASSERT(L1, lua_istable(L2, -1)); | ||
717 | luaG_pushstring(L2, _fqn); // L1: ... f ... L2: {} "f.q.n" | ||
718 | lua_rawget(L2, -2); // L1: ... f ... L2: {} f | ||
719 | // nil means we don't know how to transfer stuff: user should do something | ||
720 | // anything other than function or table should not happen! | ||
721 | if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) { | ||
722 | kLaneNameRegKey.pushValue(L1); // L1: ... f ... lane_name | ||
723 | std::string_view const _from{ luaG_tostring(L1, kIdxTop) }; | ||
724 | lua_pop(L1, 1); // L1: ... f ... | ||
725 | kLaneNameRegKey.pushValue(L2); // L1: ... f ... L2: {} f lane_name | ||
726 | std::string_view const _to{ luaG_tostring(L2, kIdxTop) }; | ||
727 | lua_pop(L2, 1); // L2: {} f | ||
728 | raise_luaL_error( | ||
729 | getErrL(), | ||
730 | "%s%s: userdata '%s' not found in %s destination transfer database.", | ||
731 | lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN ", | ||
732 | _from.empty() ? "main" : _from.data(), | ||
733 | _fqn.data(), | ||
734 | _to.empty() ? "main" : _to.data()); | ||
735 | } | ||
736 | lua_remove(L2, -2); // L2: f | ||
737 | break; | ||
738 | } | ||
739 | STACK_CHECK(L2, 1); | ||
740 | return true; | ||
741 | } | ||
742 | |||
743 | // ################################################################################################# | ||
744 | |||
681 | [[nodiscard]] | 745 | [[nodiscard]] |
682 | bool InterCopyContext::tryCopyClonable() const | 746 | bool InterCopyContext::tryCopyClonable() const |
683 | { | 747 | { |
@@ -1099,6 +1163,12 @@ bool InterCopyContext::interCopyUserdata() const | |||
1099 | return true; | 1163 | return true; |
1100 | } | 1164 | } |
1101 | 1165 | ||
1166 | // Last, let's try to see if this userdata is special (aka is it some userdata that we registered in our lookup databases during module registration?) | ||
1167 | if (lookupUserdata()) { | ||
1168 | LUA_ASSERT(L1, luaG_type(L2, kIdxTop) == LuaType::USERDATA || (lua_tocfunction(L2, kIdxTop) == userdata_lookup_sentinel)); // from lookup data. can also be userdata_lookup_sentinel if this is a userdata we know | ||
1169 | return true; | ||
1170 | } | ||
1171 | |||
1102 | raise_luaL_error(getErrL(), "can't copy non-deep full userdata across lanes"); | 1172 | raise_luaL_error(getErrL(), "can't copy non-deep full userdata across lanes"); |
1103 | } | 1173 | } |
1104 | 1174 | ||