aboutsummaryrefslogtreecommitdiff
path: root/src/intercopycontext.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/intercopycontext.cpp98
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]]
58static int func_lookup_sentinel(lua_State* L_) 58static 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]]
67static int table_lookup_sentinel(lua_State* L_) 67static 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]]
76static int userdata_clone_sentinel(lua_State* L_) 76static 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]]
85static 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]]
85std::string_view InterCopyContext::findLookupName() const 94std::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]]
694bool 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]]
682bool InterCopyContext::tryCopyClonable() const 746bool 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