diff options
Diffstat (limited to 'src/tools.cpp')
-rw-r--r-- | src/tools.cpp | 1501 |
1 files changed, 661 insertions, 840 deletions
diff --git a/src/tools.cpp b/src/tools.cpp index 7b2d153..63bb323 100644 --- a/src/tools.cpp +++ b/src/tools.cpp | |||
@@ -2,7 +2,7 @@ | |||
2 | * TOOLS.C Copyright (c) 2002-10, Asko Kauppi | 2 | * TOOLS.C Copyright (c) 2002-10, Asko Kauppi |
3 | * | 3 | * |
4 | * Lua tools to support Lanes. | 4 | * Lua tools to support Lanes. |
5 | */ | 5 | */ |
6 | 6 | ||
7 | /* | 7 | /* |
8 | =============================================================================== | 8 | =============================================================================== |
@@ -42,33 +42,29 @@ static constexpr RegistryUniqueKey kLookupCacheRegKey{ 0x9BF75F84E54B691Bull }; | |||
42 | 42 | ||
43 | // ################################################################################################# | 43 | // ################################################################################################# |
44 | 44 | ||
45 | /* | 45 | // does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it |
46 | * Does what the original 'push_registry_subtable' function did, but adds an optional mode argument to it | ||
47 | */ | ||
48 | void push_registry_subtable_mode(lua_State* L_, RegistryUniqueKey key_, const char* mode_) | 46 | void push_registry_subtable_mode(lua_State* L_, RegistryUniqueKey key_, const char* mode_) |
49 | { | 47 | { |
50 | STACK_GROW(L_, 3); | 48 | STACK_GROW(L_, 3); |
51 | STACK_CHECK_START_REL(L_, 0); | 49 | STACK_CHECK_START_REL(L_, 0); |
52 | 50 | ||
53 | key_.pushValue(L_); // {}|nil | 51 | key_.pushValue(L_); // L_: {}|nil |
54 | STACK_CHECK(L_, 1); | 52 | STACK_CHECK(L_, 1); |
55 | 53 | ||
56 | if (lua_isnil(L_, -1)) | 54 | if (lua_isnil(L_, -1)) { |
57 | { | 55 | lua_pop(L_, 1); // L_: |
58 | lua_pop(L_, 1); // | 56 | lua_newtable(L_); // L_: {} |
59 | lua_newtable(L_); // {} | ||
60 | // _R[key_] = {} | 57 | // _R[key_] = {} |
61 | key_.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); // {} | 58 | key_.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); // L_: {} |
62 | STACK_CHECK(L_, 1); | 59 | STACK_CHECK(L_, 1); |
63 | 60 | ||
64 | // Set its metatable if requested | 61 | // Set its metatable if requested |
65 | if (mode_) | 62 | if (mode_) { |
66 | { | 63 | lua_newtable(L_); // L_: {} mt |
67 | lua_newtable(L_); // {} mt | 64 | lua_pushliteral(L_, "__mode"); // L_: {} mt "__mode" |
68 | lua_pushliteral(L_, "__mode"); // {} mt "__mode" | 65 | lua_pushstring(L_, mode_); // L_: {} mt "__mode" mode |
69 | lua_pushstring(L_, mode_); // {} mt "__mode" mode | 66 | lua_rawset(L_, -3); // L_: {} mt |
70 | lua_rawset(L_, -3); // {} mt | 67 | lua_setmetatable(L_, -2); // L_: {} |
71 | lua_setmetatable(L_, -2); // {} | ||
72 | } | 68 | } |
73 | } | 69 | } |
74 | STACK_CHECK(L_, 1); | 70 | STACK_CHECK(L_, 1); |
@@ -83,7 +79,7 @@ void push_registry_subtable_mode(lua_State* L_, RegistryUniqueKey key_, const ch | |||
83 | */ | 79 | */ |
84 | void push_registry_subtable(lua_State* L_, RegistryUniqueKey key_) | 80 | void push_registry_subtable(lua_State* L_, RegistryUniqueKey key_) |
85 | { | 81 | { |
86 | push_registry_subtable_mode(L_, key_, nullptr); | 82 | push_registry_subtable_mode(L_, key_, nullptr); |
87 | } | 83 | } |
88 | 84 | ||
89 | // ################################################################################################# | 85 | // ################################################################################################# |
@@ -91,13 +87,10 @@ void push_registry_subtable(lua_State* L_, RegistryUniqueKey key_) | |||
91 | // same as PUC-Lua l_alloc | 87 | // same as PUC-Lua l_alloc |
92 | extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_) | 88 | extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_) |
93 | { | 89 | { |
94 | if (nsize_ == 0) | 90 | if (nsize_ == 0) { |
95 | { | ||
96 | free(ptr_); | 91 | free(ptr_); |
97 | return nullptr; | 92 | return nullptr; |
98 | } | 93 | } else { |
99 | else | ||
100 | { | ||
101 | return realloc(ptr_, nsize_); | 94 | return realloc(ptr_, nsize_); |
102 | } | 95 | } |
103 | } | 96 | } |
@@ -116,64 +109,52 @@ extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, | |||
116 | 109 | ||
117 | // called once at the creation of the universe (therefore L is the master Lua state everything originates from) | 110 | // called once at the creation of the universe (therefore L is the master Lua state everything originates from) |
118 | // Do I need to disable this when compiling for LuaJIT to prevent issues? | 111 | // Do I need to disable this when compiling for LuaJIT to prevent issues? |
119 | void initialize_allocator_function(Universe* U, lua_State* L_) | 112 | void initialize_allocator_function(Universe* U_, lua_State* L_) |
120 | { | 113 | { |
121 | STACK_CHECK_START_REL(L_, 1); // settings | 114 | STACK_CHECK_START_REL(L_, 1); // L_: settings |
122 | lua_getfield(L_, -1, "allocator"); // settings allocator|nil|"protected" | 115 | lua_getfield(L_, -1, "allocator"); // L_: settings allocator|nil|"protected" |
123 | if (!lua_isnil(L_, -1)) | 116 | if (!lua_isnil(L_, -1)) { |
124 | { | ||
125 | // store C function pointer in an internal variable | 117 | // store C function pointer in an internal variable |
126 | U->provide_allocator = lua_tocfunction(L_, -1); // settings allocator | 118 | U_->provide_allocator = lua_tocfunction(L_, -1); // L_: settings allocator |
127 | if (U->provide_allocator != nullptr) | 119 | if (U_->provide_allocator != nullptr) { |
128 | { | ||
129 | // make sure the function doesn't have upvalues | 120 | // make sure the function doesn't have upvalues |
130 | char const* upname = lua_getupvalue(L_, -1, 1); // settings allocator upval? | 121 | char const* upname = lua_getupvalue(L_, -1, 1); // L_: settings allocator upval? |
131 | if (upname != nullptr) // should be "" for C functions with upvalues if any | 122 | if (upname != nullptr) { // should be "" for C functions with upvalues if any |
132 | { | ||
133 | raise_luaL_error(L_, "config.allocator() shouldn't have upvalues"); | 123 | raise_luaL_error(L_, "config.allocator() shouldn't have upvalues"); |
134 | } | 124 | } |
135 | // remove this C function from the config table so that it doesn't cause problems | 125 | // remove this C function from the config table so that it doesn't cause problems |
136 | // when we transfer the config table in newly created Lua states | 126 | // when we transfer the config table in newly created Lua states |
137 | lua_pushnil(L_); // settings allocator nil | 127 | lua_pushnil(L_); // L_: settings allocator nil |
138 | lua_setfield(L_, -3, "allocator"); // settings allocator | 128 | lua_setfield(L_, -3, "allocator"); // L_: settings allocator |
139 | } | 129 | } else if (lua_type(L_, -1) == LUA_TSTRING) { // should be "protected" |
140 | else if (lua_type(L_, -1) == LUA_TSTRING) // should be "protected" | ||
141 | { | ||
142 | LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0); | 130 | LUA_ASSERT(L_, strcmp(lua_tostring(L_, -1), "protected") == 0); |
143 | // set the original allocator to call from inside protection by the mutex | 131 | // set the original allocator to call from inside protection by the mutex |
144 | U->protected_allocator.initFrom(L_); | 132 | U_->protected_allocator.initFrom(L_); |
145 | U->protected_allocator.installIn(L_); | 133 | U_->protected_allocator.installIn(L_); |
146 | // before a state is created, this function will be called to obtain the allocator | 134 | // before a state is created, this function will be called to obtain the allocator |
147 | U->provide_allocator = luaG_provide_protected_allocator; | 135 | U_->provide_allocator = luaG_provide_protected_allocator; |
148 | } | 136 | } |
149 | } | 137 | } else { |
150 | else | ||
151 | { | ||
152 | // just grab whatever allocator was provided to lua_newstate | 138 | // just grab whatever allocator was provided to lua_newstate |
153 | U->protected_allocator.initFrom(L_); | 139 | U_->protected_allocator.initFrom(L_); |
154 | } | 140 | } |
155 | lua_pop(L_, 1); // settings | 141 | lua_pop(L_, 1); // L_: settings |
156 | STACK_CHECK(L_, 1); | 142 | STACK_CHECK(L_, 1); |
157 | 143 | ||
158 | lua_getfield(L_, -1, "internal_allocator"); // settings "libc"|"allocator" | 144 | lua_getfield(L_, -1, "internal_allocator"); // L_: settings "libc"|"allocator" |
159 | { | 145 | { |
160 | char const* allocator = lua_tostring(L_, -1); | 146 | char const* allocator = lua_tostring(L_, -1); |
161 | if (strcmp(allocator, "libc") == 0) | 147 | if (strcmp(allocator, "libc") == 0) { |
162 | { | 148 | U_->internal_allocator = AllocatorDefinition{ libc_lua_Alloc, nullptr }; |
163 | U->internal_allocator = AllocatorDefinition{ libc_lua_Alloc, nullptr }; | 149 | } else if (U_->provide_allocator == luaG_provide_protected_allocator) { |
164 | } | ||
165 | else if (U->provide_allocator == luaG_provide_protected_allocator) | ||
166 | { | ||
167 | // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case. | 150 | // user wants mutex protection on the state's allocator. Use protection for our own allocations too, just in case. |
168 | U->internal_allocator = U->protected_allocator.makeDefinition(); | 151 | U_->internal_allocator = U_->protected_allocator.makeDefinition(); |
169 | } | 152 | } else { |
170 | else | ||
171 | { | ||
172 | // no protection required, just use whatever we have as-is. | 153 | // no protection required, just use whatever we have as-is. |
173 | U->internal_allocator = U->protected_allocator; | 154 | U_->internal_allocator = U_->protected_allocator; |
174 | } | 155 | } |
175 | } | 156 | } |
176 | lua_pop(L_, 1); // settings | 157 | lua_pop(L_, 1); // L_: settings |
177 | STACK_CHECK(L_, 1); | 158 | STACK_CHECK(L_, 1); |
178 | } | 159 | } |
179 | 160 | ||
@@ -204,18 +185,16 @@ enum class FuncSubType | |||
204 | Bytecode, | 185 | Bytecode, |
205 | Native, | 186 | Native, |
206 | FastJIT | 187 | FastJIT |
207 | } ; | 188 | }; |
208 | 189 | ||
209 | FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | 190 | FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) |
210 | { | 191 | { |
211 | if (lua_tocfunction(L_, _i)) // nullptr for LuaJIT-fast && bytecode functions | 192 | if (lua_tocfunction(L_, _i)) { // nullptr for LuaJIT-fast && bytecode functions |
212 | { | ||
213 | return FuncSubType::Native; | 193 | return FuncSubType::Native; |
214 | } | 194 | } |
215 | { | 195 | { |
216 | int mustpush{ 0 }; | 196 | int mustpush{ 0 }; |
217 | if (lua_absindex(L_, _i) != lua_gettop(L_)) | 197 | if (lua_absindex(L_, _i) != lua_gettop(L_)) { |
218 | { | ||
219 | lua_pushvalue(L_, _i); | 198 | lua_pushvalue(L_, _i); |
220 | mustpush = 1; | 199 | mustpush = 1; |
221 | } | 200 | } |
@@ -224,8 +203,7 @@ FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | |||
224 | // all other cases mean this is either a C or LuaJIT-fast function | 203 | // all other cases mean this is either a C or LuaJIT-fast function |
225 | int const dumpres{ lua504_dump(L_, dummy_writer, nullptr, 0) }; | 204 | int const dumpres{ lua504_dump(L_, dummy_writer, nullptr, 0) }; |
226 | lua_pop(L_, mustpush); | 205 | lua_pop(L_, mustpush); |
227 | if (dumpres == 666) | 206 | if (dumpres == 666) { |
228 | { | ||
229 | return FuncSubType::Bytecode; | 207 | return FuncSubType::Bytecode; |
230 | } | 208 | } |
231 | } | 209 | } |
@@ -235,28 +213,26 @@ FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | |||
235 | // ################################################################################################# | 213 | // ################################################################################################# |
236 | 214 | ||
237 | // inspired from tconcat() in ltablib.c | 215 | // inspired from tconcat() in ltablib.c |
238 | [[nodiscard]] static char const* luaG_pushFQN(lua_State* L_, int t, int last, size_t* length) | 216 | [[nodiscard]] static char const* luaG_pushFQN(lua_State* L_, int t_, int last_, size_t* length_) |
239 | { | 217 | { |
240 | luaL_Buffer b; | 218 | luaL_Buffer b; |
241 | STACK_CHECK_START_REL(L_, 0); | 219 | STACK_CHECK_START_REL(L_, 0); |
242 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... | 220 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... |
243 | luaL_buffinit(L_, &b); // ... {} ... &b? | 221 | luaL_buffinit(L_, &b); // L_: ... {} ... &b? |
244 | int i = 1; | 222 | int i = 1; |
245 | for (; i < last; ++i) | 223 | for (; i < last_; ++i) { |
246 | { | 224 | lua_rawgeti(L_, t_, i); |
247 | lua_rawgeti( L_, t, i); | 225 | luaL_addvalue(&b); |
248 | luaL_addvalue( &b); | ||
249 | luaL_addlstring(&b, "/", 1); | 226 | luaL_addlstring(&b, "/", 1); |
250 | } | 227 | } |
251 | if (i == last) // add last value (if interval was not empty) | 228 | if (i == last_) { // add last value (if interval was not empty) |
252 | { | 229 | lua_rawgeti(L_, t_, i); |
253 | lua_rawgeti(L_, t, i); | ||
254 | luaL_addvalue(&b); | 230 | luaL_addvalue(&b); |
255 | } | 231 | } |
256 | // &b is popped at that point (-> replaced by the result) | 232 | // &b is popped at that point (-> replaced by the result) |
257 | luaL_pushresult(&b); // ... {} ... "<result>" | 233 | luaL_pushresult(&b); // L_: ... {} ... "<result>" |
258 | STACK_CHECK(L_, 1); | 234 | STACK_CHECK(L_, 1); |
259 | return lua_tolstring( L_, -1, length); | 235 | return lua_tolstring(L_, -1, length_); |
260 | } | 236 | } |
261 | 237 | ||
262 | // ################################################################################################# | 238 | // ################################################################################################# |
@@ -269,7 +245,7 @@ FuncSubType luaG_getfuncsubtype(lua_State* L_, int _i) | |||
269 | * if we already had an entry of type [o] = ..., replace the name if the new one is shorter | 245 | * if we already had an entry of type [o] = ..., replace the name if the new one is shorter |
270 | * pops the processed object from the stack | 246 | * pops the processed object from the stack |
271 | */ | 247 | */ |
272 | static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, int _ctx_base, int _depth) | 248 | static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_, int _ctx_base, int _depth) |
273 | { | 249 | { |
274 | // slot 1 in the stack contains the table that receives everything we found | 250 | // slot 1 in the stack contains the table that receives everything we found |
275 | int const dest{ _ctx_base }; | 251 | int const dest{ _ctx_base }; |
@@ -278,22 +254,22 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_ | |||
278 | 254 | ||
279 | size_t prevNameLength, newNameLength; | 255 | size_t prevNameLength, newNameLength; |
280 | char const* prevName; | 256 | char const* prevName; |
281 | DEBUGSPEW_CODE(char const *newName); | 257 | DEBUGSPEW_CODE(char const* newName); |
282 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END)); | 258 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "update_lookup_entry()\n" INDENT_END(U_))); |
283 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 259 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ }); |
284 | 260 | ||
285 | STACK_CHECK_START_REL(L_, 0); | 261 | STACK_CHECK_START_REL(L_, 0); |
286 | // first, raise an error if the function is already known | 262 | // first, raise an error if the function is already known |
287 | lua_pushvalue(L_, -1); // ... {bfc} k o o | 263 | lua_pushvalue(L_, -1); // L_: ... {bfc} k o o |
288 | lua_rawget(L_, dest); // ... {bfc} k o name? | 264 | lua_rawget(L_, dest); // L_: ... {bfc} k o name? |
289 | prevName = lua_tolstring( L_, -1, &prevNameLength); // nullptr if we got nil (first encounter of this object) | 265 | prevName = lua_tolstring(L_, -1, &prevNameLength); // nullptr if we got nil (first encounter of this object) |
290 | // push name in fqn stack (note that concatenation will crash if name is a not string or a number) | 266 | // push name in fqn stack (note that concatenation will crash if name is a not string or a number) |
291 | lua_pushvalue(L_, -3); // ... {bfc} k o name? k | 267 | lua_pushvalue(L_, -3); // L_: ... {bfc} k o name? k |
292 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TNUMBER || lua_type(L_, -1) == LUA_TSTRING); | 268 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TNUMBER || lua_type(L_, -1) == LUA_TSTRING); |
293 | ++_depth; | 269 | ++_depth; |
294 | lua_rawseti(L_, fqn, _depth); // ... {bfc} k o name? | 270 | lua_rawseti(L_, fqn, _depth); // L_: ... {bfc} k o name? |
295 | // generate name | 271 | // generate name |
296 | DEBUGSPEW_OR_NOT(newName, std::ignore) = luaG_pushFQN(L_, fqn, _depth, &newNameLength); // ... {bfc} k o name? "f.q.n" | 272 | DEBUGSPEW_OR_NOT(newName, std::ignore) = luaG_pushFQN(L_, fqn, _depth, &newNameLength); // L_: ... {bfc} k o name? "f.q.n" |
297 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order | 273 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order |
298 | // on different VMs even when the tables are populated the exact same way. | 274 | // on different VMs even when the tables are populated the exact same way. |
299 | // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), | 275 | // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), |
@@ -303,227 +279,200 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_ | |||
303 | // Also, nothing prevents any external module from exposing a given object under several names, so... | 279 | // Also, nothing prevents any external module from exposing a given object under several names, so... |
304 | // Therefore, when we encounter an object for which a name was previously registered, we need to select the names | 280 | // Therefore, when we encounter an object for which a name was previously registered, we need to select the names |
305 | // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded | 281 | // based on some sorting order so that we end up with the same name in all databases whatever order the table walk yielded |
306 | if (prevName != nullptr && (prevNameLength < newNameLength || lua_lessthan(L_, -2, -1))) | 282 | if (prevName != nullptr && (prevNameLength < newNameLength || lua_lessthan(L_, -2, -1))) { |
307 | { | 283 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END(U_), lua_typename(L_, lua_type(L_, -3)), newName, prevName)); |
308 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename(L_, lua_type(L_, -3)), newName, prevName)); | ||
309 | // the previous name is 'smaller' than the one we just generated: keep it! | 284 | // the previous name is 'smaller' than the one we just generated: keep it! |
310 | lua_pop(L_, 3); // ... {bfc} k | 285 | lua_pop(L_, 3); // L_: ... {bfc} k |
311 | } | 286 | } else { |
312 | else | ||
313 | { | ||
314 | // the name we generated is either the first one, or a better fit for our purposes | 287 | // the name we generated is either the first one, or a better fit for our purposes |
315 | if (prevName) | 288 | if (prevName) { |
316 | { | ||
317 | // clear the previous name for the database to avoid clutter | 289 | // clear the previous name for the database to avoid clutter |
318 | lua_insert(L_, -2); // ... {bfc} k o "f.q.n" prevName | 290 | lua_insert(L_, -2); // L_: ... {bfc} k o "f.q.n" prevName |
319 | // t[prevName] = nil | 291 | // t[prevName] = nil |
320 | lua_pushnil(L_); // ... {bfc} k o "f.q.n" prevName nil | 292 | lua_pushnil(L_); // L_: ... {bfc} k o "f.q.n" prevName nil |
321 | lua_rawset(L_, dest); // ... {bfc} k o "f.q.n" | 293 | lua_rawset(L_, dest); // L_: ... {bfc} k o "f.q.n" |
322 | } | 294 | } else { |
323 | else | 295 | lua_remove(L_, -2); // L_: ... {bfc} k o "f.q.n" |
324 | { | ||
325 | lua_remove(L_, -2); // ... {bfc} k o "f.q.n" | ||
326 | } | 296 | } |
327 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename(L_, lua_type(L_, -2)), newName)); | 297 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END(U_), lua_typename(L_, lua_type(L_, -2)), newName)); |
328 | // prepare the stack for database feed | 298 | // prepare the stack for database feed |
329 | lua_pushvalue(L_, -1); // ... {bfc} k o "f.q.n" "f.q.n" | 299 | lua_pushvalue(L_, -1); // L_: ... {bfc} k o "f.q.n" "f.q.n" |
330 | lua_pushvalue(L_, -3); // ... {bfc} k o "f.q.n" "f.q.n" o | 300 | lua_pushvalue(L_, -3); // L_: ... {bfc} k o "f.q.n" "f.q.n" o |
331 | LUA_ASSERT(L_, lua_rawequal(L_, -1, -4)); | 301 | LUA_ASSERT(L_, lua_rawequal(L_, -1, -4)); |
332 | LUA_ASSERT(L_, lua_rawequal(L_, -2, -3)); | 302 | LUA_ASSERT(L_, lua_rawequal(L_, -2, -3)); |
333 | // t["f.q.n"] = o | 303 | // t["f.q.n"] = o |
334 | lua_rawset(L_, dest); // ... {bfc} k o "f.q.n" | 304 | lua_rawset(L_, dest); // L_: ... {bfc} k o "f.q.n" |
335 | // t[o] = "f.q.n" | 305 | // t[o] = "f.q.n" |
336 | lua_rawset(L_, dest); // ... {bfc} k | 306 | lua_rawset(L_, dest); // L_: ... {bfc} k |
337 | // remove table name from fqn stack | 307 | // remove table name from fqn stack |
338 | lua_pushnil(L_); // ... {bfc} k nil | 308 | lua_pushnil(L_); // L_: ... {bfc} k nil |
339 | lua_rawseti(L_, fqn, _depth); // ... {bfc} k | 309 | lua_rawseti(L_, fqn, _depth); // L_: ... {bfc} k |
340 | } | 310 | } |
341 | -- _depth; | 311 | --_depth; |
342 | STACK_CHECK(L_, -1); | 312 | STACK_CHECK(L_, -1); |
343 | } | 313 | } |
344 | 314 | ||
345 | // ################################################################################################# | 315 | // ################################################################################################# |
346 | 316 | ||
347 | static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L_, int _ctx_base, int _i, int _depth) | 317 | static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U_) lua_State* L_, int ctxBase_, int i_, int depth_) |
348 | { | 318 | { |
349 | // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i | 319 | // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot i_ |
350 | int const fqn = _ctx_base + 1; | 320 | int const fqn = ctxBase_ + 1; |
351 | // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops | 321 | // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops |
352 | int const cache = _ctx_base + 2; | 322 | int const cache = ctxBase_ + 2; |
353 | // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) | 323 | // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) |
354 | int const breadth_first_cache = lua_gettop(L_) + 1; | 324 | int const breadth_first_cache = lua_gettop(L_) + 1; |
355 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END)); | 325 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END(U_))); |
356 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 326 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ }); |
357 | 327 | ||
358 | STACK_GROW(L_, 6); | 328 | STACK_GROW(L_, 6); |
359 | // slot _i contains a table where we search for functions (or a full userdata with a metatable) | 329 | // slot i_ contains a table where we search for functions (or a full userdata with a metatable) |
360 | STACK_CHECK_START_REL(L_, 0); // ... {_i} | 330 | STACK_CHECK_START_REL(L_, 0); // L_: ... {i_} |
361 | 331 | ||
362 | // if object is a userdata, replace it by its metatable | 332 | // if object is a userdata, replace it by its metatable |
363 | if (lua_type(L_, _i) == LUA_TUSERDATA) | 333 | if (lua_type(L_, i_) == LUA_TUSERDATA) { |
364 | { | 334 | lua_getmetatable(L_, i_); // L_: ... {i_} mt |
365 | lua_getmetatable(L_, _i); // ... {_i} mt | 335 | lua_replace(L_, i_); // L_: ... {i_} |
366 | lua_replace(L_, _i); // ... {_i} | ||
367 | } | 336 | } |
368 | 337 | ||
369 | // if table is already visited, we are done | 338 | // if table is already visited, we are done |
370 | lua_pushvalue(L_, _i); // ... {_i} {} | 339 | lua_pushvalue(L_, i_); // L_: ... {i_} {} |
371 | lua_rawget(L_, cache); // ... {_i} nil|n | 340 | lua_rawget(L_, cache); // L_: ... {i_} nil|n |
372 | lua_Integer visit_count{ lua_tointeger(L_, -1) }; // 0 if nil, else n | 341 | lua_Integer visit_count{ lua_tointeger(L_, -1) }; // 0 if nil, else n |
373 | lua_pop(L_, 1); // ... {_i} | 342 | lua_pop(L_, 1); // L_: ... {i_} |
374 | STACK_CHECK(L_, 0); | 343 | STACK_CHECK(L_, 0); |
375 | if (visit_count > 0) | 344 | if (visit_count > 0) { |
376 | { | 345 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "already visited\n" INDENT_END(U_))); |
377 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END)); | ||
378 | return; | 346 | return; |
379 | } | 347 | } |
380 | 348 | ||
381 | // remember we visited this table (1-visit count) | 349 | // remember we visited this table (1-visit count) |
382 | lua_pushvalue(L_, _i); // ... {_i} {} | 350 | lua_pushvalue(L_, i_); // L_: ... {i_} {} |
383 | lua_pushinteger(L_, visit_count + 1); // ... {_i} {} 1 | 351 | lua_pushinteger(L_, visit_count + 1); // L_: ... {i_} {} 1 |
384 | lua_rawset(L_, cache); // ... {_i} | 352 | lua_rawset(L_, cache); // L_: ... {i_} |
385 | STACK_CHECK(L_, 0); | 353 | STACK_CHECK(L_, 0); |
386 | 354 | ||
387 | // this table is at breadth_first_cache index | 355 | // this table is at breadth_first_cache index |
388 | lua_newtable(L_); // ... {_i} {bfc} | 356 | lua_newtable(L_); // L_: ... {i_} {bfc} |
389 | LUA_ASSERT(L_, lua_gettop(L_) == breadth_first_cache); | 357 | LUA_ASSERT(L_, lua_gettop(L_) == breadth_first_cache); |
390 | // iterate over all entries in the processed table | 358 | // iterate over all entries in the processed table |
391 | lua_pushnil(L_); // ... {_i} {bfc} nil | 359 | lua_pushnil(L_); // L_: ... {i_} {bfc} nil |
392 | while( lua_next(L_, _i) != 0) // ... {_i} {bfc} k v | 360 | while (lua_next(L_, i_) != 0) { // L_: ... {i_} {bfc} k v |
393 | { | ||
394 | // just for debug, not actually needed | 361 | // just for debug, not actually needed |
395 | //char const* key = (lua_type(L, -2) == LUA_TSTRING) ? lua_tostring(L, -2) : "not a string"; | 362 | // char const* key = (lua_type(L, -2) == LUA_TSTRING) ? lua_tostring(L, -2) : "not a string"; |
396 | // subtable: process it recursively | 363 | // subtable: process it recursively |
397 | if (lua_istable(L_, -1)) // ... {_i} {bfc} k {} | 364 | if (lua_istable(L_, -1)) { // L_: ... {i_} {bfc} k {} |
398 | { | ||
399 | // increment visit count to make sure we will actually scan it at this recursive level | 365 | // increment visit count to make sure we will actually scan it at this recursive level |
400 | lua_pushvalue(L_, -1); // ... {_i} {bfc} k {} {} | 366 | lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} |
401 | lua_pushvalue(L_, -1); // ... {_i} {bfc} k {} {} {} | 367 | lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} {} |
402 | lua_rawget(L_, cache); // ... {_i} {bfc} k {} {} n? | 368 | lua_rawget(L_, cache); // L_: ... {i_} {bfc} k {} {} n? |
403 | visit_count = lua_tointeger(L_, -1) + 1; // 1 if we got nil, else n+1 | 369 | visit_count = lua_tointeger(L_, -1) + 1; // 1 if we got nil, else n+1 |
404 | lua_pop(L_, 1); // ... {_i} {bfc} k {} {} | 370 | lua_pop(L_, 1); // L_: ... {i_} {bfc} k {} {} |
405 | lua_pushinteger(L_, visit_count); // ... {_i} {bfc} k {} {} n | 371 | lua_pushinteger(L_, visit_count); // L_: ... {i_} {bfc} k {} {} n |
406 | lua_rawset(L_, cache); // ... {_i} {bfc} k {} | 372 | lua_rawset(L_, cache); // L_: ... {i_} {bfc} k {} |
407 | // store the table in the breadth-first cache | 373 | // store the table in the breadth-first cache |
408 | lua_pushvalue(L_, -2); // ... {_i} {bfc} k {} k | 374 | lua_pushvalue(L_, -2); // L_: ... {i_} {bfc} k {} k |
409 | lua_pushvalue(L_, -2); // ... {_i} {bfc} k {} k {} | 375 | lua_pushvalue(L_, -2); // L_: ... {i_} {bfc} k {} k {} |
410 | lua_rawset(L_, breadth_first_cache); // ... {_i} {bfc} k {} | 376 | lua_rawset(L_, breadth_first_cache); // L_: ... {i_} {bfc} k {} |
411 | // generate a name, and if we already had one name, keep whichever is the shorter | 377 | // generate a name, and if we already had one name, keep whichever is the shorter |
412 | update_lookup_entry( DEBUGSPEW_PARAM_COMMA(U) L_, _ctx_base, _depth); // ... {_i} {bfc} k | 378 | update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U_) L_, ctxBase_, depth_); // L_: ... {i_} {bfc} k |
413 | } | 379 | } else if (lua_isfunction(L_, -1) && (luaG_getfuncsubtype(L_, -1) != FuncSubType::Bytecode)) { |
414 | else if (lua_isfunction(L_, -1) && (luaG_getfuncsubtype(L_, -1) != FuncSubType::Bytecode)) | ||
415 | { | ||
416 | // generate a name, and if we already had one name, keep whichever is the shorter | 380 | // generate a name, and if we already had one name, keep whichever is the shorter |
417 | // this pops the function from the stack | 381 | // this pops the function from the stack |
418 | update_lookup_entry( DEBUGSPEW_PARAM_COMMA(U) L_, _ctx_base, _depth); // ... {_i} {bfc} k | 382 | update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U_) L_, ctxBase_, depth_); // L_: ... {i_} {bfc} k |
419 | } | 383 | } else { |
420 | else | 384 | lua_pop(L_, 1); // L_: ... {i_} {bfc} k |
421 | { | ||
422 | lua_pop(L_, 1); // ... {_i} {bfc} k | ||
423 | } | 385 | } |
424 | STACK_CHECK(L_, 2); | 386 | STACK_CHECK(L_, 2); |
425 | } | 387 | } |
426 | // now process the tables we encountered at that depth | 388 | // now process the tables we encountered at that depth |
427 | ++ _depth; | 389 | ++depth_; |
428 | lua_pushnil(L_); // ... {_i} {bfc} nil | 390 | lua_pushnil(L_); // L_: ... {i_} {bfc} nil |
429 | while (lua_next(L_, breadth_first_cache) != 0) // ... {_i} {bfc} k {} | 391 | while (lua_next(L_, breadth_first_cache) != 0) { // L_: ... {i_} {bfc} k {} |
430 | { | ||
431 | DEBUGSPEW_CODE(char const* key = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : "not a string"); | 392 | DEBUGSPEW_CODE(char const* key = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : "not a string"); |
432 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key)); | 393 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "table '%s'\n" INDENT_END(U_), key)); |
433 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 394 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U_ }); |
434 | // un-visit this table in case we do need to process it | 395 | // un-visit this table in case we do need to process it |
435 | lua_pushvalue(L_, -1); // ... {_i} {bfc} k {} {} | 396 | lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} |
436 | lua_rawget(L_, cache); // ... {_i} {bfc} k {} n | 397 | lua_rawget(L_, cache); // L_: ... {i_} {bfc} k {} n |
437 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TNUMBER); | 398 | LUA_ASSERT(L_, lua_type(L_, -1) == LUA_TNUMBER); |
438 | visit_count = lua_tointeger(L_, -1) - 1; | 399 | visit_count = lua_tointeger(L_, -1) - 1; |
439 | lua_pop(L_, 1); // ... {_i} {bfc} k {} | 400 | lua_pop(L_, 1); // L_: ... {i_} {bfc} k {} |
440 | lua_pushvalue(L_, -1); // ... {_i} {bfc} k {} {} | 401 | lua_pushvalue(L_, -1); // L_: ... {i_} {bfc} k {} {} |
441 | if (visit_count > 0) | 402 | if (visit_count > 0) { |
442 | { | 403 | lua_pushinteger(L_, visit_count); // L_: ... {i_} {bfc} k {} {} n |
443 | lua_pushinteger(L_, visit_count); // ... {_i} {bfc} k {} {} n | 404 | } else { |
444 | } | 405 | lua_pushnil(L_); // L_: ... {i_} {bfc} k {} {} nil |
445 | else | ||
446 | { | ||
447 | lua_pushnil(L_); // ... {_i} {bfc} k {} {} nil | ||
448 | } | 406 | } |
449 | lua_rawset(L_, cache); // ... {_i} {bfc} k {} | 407 | lua_rawset(L_, cache); // L_: ... {i_} {bfc} k {} |
450 | // push table name in fqn stack (note that concatenation will crash if name is a not string!) | 408 | // push table name in fqn stack (note that concatenation will crash if name is a not string!) |
451 | lua_pushvalue(L_, -2); // ... {_i} {bfc} k {} k | 409 | lua_pushvalue(L_, -2); // L_: ... {i_} {bfc} k {} k |
452 | lua_rawseti(L_, fqn, _depth); // ... {_i} {bfc} k {} | 410 | lua_rawseti(L_, fqn, depth_); // L_: ... {i_} {bfc} k {} |
453 | populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U) L_, _ctx_base, lua_gettop(L_), _depth); | 411 | populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U_) L_, ctxBase_, lua_gettop(L_), depth_); |
454 | lua_pop(L_, 1); // ... {_i} {bfc} k | 412 | lua_pop(L_, 1); // L_: ... {i_} {bfc} k |
455 | STACK_CHECK(L_, 2); | 413 | STACK_CHECK(L_, 2); |
456 | } | 414 | } |
457 | // remove table name from fqn stack | 415 | // remove table name from fqn stack |
458 | lua_pushnil(L_); // ... {_i} {bfc} nil | 416 | lua_pushnil(L_); // L_: ... {i_} {bfc} nil |
459 | lua_rawseti(L_, fqn, _depth); // ... {_i} {bfc} | 417 | lua_rawseti(L_, fqn, depth_); // L_: ... {i_} {bfc} |
460 | -- _depth; | 418 | --depth_; |
461 | // we are done with our cache | 419 | // we are done with our cache |
462 | lua_pop(L_, 1); // ... {_i} | 420 | lua_pop(L_, 1); // L_: ... {i_} |
463 | STACK_CHECK(L_, 0); | 421 | STACK_CHECK(L_, 0); |
464 | // we are done // ... {_i} {bfc} | 422 | // we are done // L_: ... {i_} {bfc} |
465 | } | 423 | } |
466 | 424 | ||
467 | // ################################################################################################# | 425 | // ################################################################################################# |
468 | 426 | ||
469 | /* | 427 | // create a "fully.qualified.name" <-> function equivalence database |
470 | * create a "fully.qualified.name" <-> function equivalence database | ||
471 | */ | ||
472 | void populate_func_lookup_table(lua_State* L_, int i_, char const* name_) | 428 | void populate_func_lookup_table(lua_State* L_, int i_, char const* name_) |
473 | { | 429 | { |
474 | int const ctx_base = lua_gettop(L_) + 1; | 430 | int const ctx_base = lua_gettop(L_) + 1; |
475 | int const in_base = lua_absindex(L_, i_); | 431 | int const in_base = lua_absindex(L_, i_); |
476 | int start_depth = 0; | 432 | int start_depth = 0; |
477 | DEBUGSPEW_CODE(Universe* U = universe_get(L_)); | 433 | DEBUGSPEW_CODE(Universe* U = universe_get(L_)); |
478 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L_, name_ ? name_ : "nullptr")); | 434 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END(U), L_, name_ ? name_ : "nullptr")); |
479 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 435 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
480 | STACK_GROW(L_, 3); | 436 | STACK_GROW(L_, 3); |
481 | STACK_CHECK_START_REL(L_, 0); | 437 | STACK_CHECK_START_REL(L_, 0); |
482 | kLookupRegKey.pushValue(L_); // {} | 438 | kLookupRegKey.pushValue(L_); // L_: {} |
483 | STACK_CHECK(L_, 1); | 439 | STACK_CHECK(L_, 1); |
484 | LUA_ASSERT(L_, lua_istable(L_, -1)); | 440 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
485 | if (lua_type(L_, in_base) == LUA_TFUNCTION) // for example when a module is a simple function | 441 | if (lua_type(L_, in_base) == LUA_TFUNCTION) { // for example when a module is a simple function |
486 | { | ||
487 | name_ = name_ ? name_ : "nullptr"; | 442 | name_ = name_ ? name_ : "nullptr"; |
488 | lua_pushvalue(L_, in_base); // {} f | 443 | lua_pushvalue(L_, in_base); // L_: {} f |
489 | lua_pushstring(L_, name_); // {} f _name | 444 | lua_pushstring(L_, name_); // L_: {} f _name |
490 | lua_rawset(L_, -3); // {} | 445 | lua_rawset(L_, -3); // L_: {} |
491 | lua_pushstring(L_, name_); // {} _name | 446 | lua_pushstring(L_, name_); // L_: {} _name |
492 | lua_pushvalue(L_, in_base); // {} _name f | 447 | lua_pushvalue(L_, in_base); // L_: {} _name f |
493 | lua_rawset(L_, -3); // {} | 448 | lua_rawset(L_, -3); // L_: {} |
494 | lua_pop(L_, 1); // | 449 | lua_pop(L_, 1); // L_: |
495 | } | 450 | } else if (lua_type(L_, in_base) == LUA_TTABLE) { |
496 | else if (lua_type(L_, in_base) == LUA_TTABLE) | 451 | lua_newtable(L_); // L_: {} {fqn} |
497 | { | 452 | if (name_) { |
498 | lua_newtable(L_); // {} {fqn} | ||
499 | if (name_) | ||
500 | { | ||
501 | STACK_CHECK(L_, 2); | 453 | STACK_CHECK(L_, 2); |
502 | lua_pushstring(L_, name_); // {} {fqn} "name" | 454 | lua_pushstring(L_, name_); // L_: {} {fqn} "name" |
503 | // generate a name, and if we already had one name, keep whichever is the shorter | 455 | // generate a name, and if we already had one name, keep whichever is the shorter |
504 | lua_pushvalue(L_, in_base); // {} {fqn} "name" t | 456 | lua_pushvalue(L_, in_base); // L_: {} {fqn} "name" t |
505 | update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U) L_, ctx_base, start_depth); // {} {fqn} "name" | 457 | update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U) L_, ctx_base, start_depth); // L_: {} {fqn} "name" |
506 | // don't forget to store the name at the bottom of the fqn stack | 458 | // don't forget to store the name at the bottom of the fqn stack |
507 | ++ start_depth; | 459 | ++start_depth; |
508 | lua_rawseti(L_, -2, start_depth); // {} {fqn} | 460 | lua_rawseti(L_, -2, start_depth); // L_: {} {fqn} |
509 | STACK_CHECK(L_, 2); | 461 | STACK_CHECK(L_, 2); |
510 | } | 462 | } |
511 | // retrieve the cache, create it if we haven't done it yet | 463 | // retrieve the cache, create it if we haven't done it yet |
512 | kLookupCacheRegKey.pushValue(L_); // {} {fqn} {cache}? | 464 | kLookupCacheRegKey.pushValue(L_); // L_: {} {fqn} {cache}? |
513 | if (lua_isnil(L_, -1)) | 465 | if (lua_isnil(L_, -1)) { |
514 | { | 466 | lua_pop(L_, 1); // L_: {} {fqn} |
515 | lua_pop(L_, 1); // {} {fqn} | 467 | lua_newtable(L_); // L_: {} {fqn} {cache} |
516 | lua_newtable(L_); // {} {fqn} {cache} | ||
517 | kLookupCacheRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); | 468 | kLookupCacheRegKey.setValue(L_, [](lua_State* L_) { lua_pushvalue(L_, -2); }); |
518 | STACK_CHECK(L_, 3); | 469 | STACK_CHECK(L_, 3); |
519 | } | 470 | } |
520 | // process everything we find in that table, filling in lookup data for all functions and tables we see there | 471 | // process everything we find in that table, filling in lookup data for all functions and tables we see there |
521 | populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U) L_, ctx_base, in_base, start_depth); | 472 | populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U) L_, ctx_base, in_base, start_depth); |
522 | lua_pop(L_, 3); | 473 | lua_pop(L_, 3); |
523 | } | 474 | } else { |
524 | else | 475 | lua_pop(L_, 1); // L_: |
525 | { | ||
526 | lua_pop(L_, 1); // | ||
527 | raise_luaL_error(L_, "unsupported module type %s", lua_typename(L_, lua_type(L_, in_base))); | 476 | raise_luaL_error(L_, "unsupported module type %s", lua_typename(L_, lua_type(L_, in_base))); |
528 | } | 477 | } |
529 | STACK_CHECK(L_, 0); | 478 | STACK_CHECK(L_, 0); |
@@ -536,38 +485,35 @@ void populate_func_lookup_table(lua_State* L_, int i_, char const* name_) | |||
536 | // xxh64 of string "kMtIdRegKey" generated at https://www.pelock.com/products/hash-calculator | 485 | // xxh64 of string "kMtIdRegKey" generated at https://www.pelock.com/products/hash-calculator |
537 | static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | 486 | static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; |
538 | 487 | ||
539 | /* | 488 | // get a unique ID for metatable at [i]. |
540 | * Get a unique ID for metatable at [i]. | 489 | [[nodiscard]] static lua_Integer get_mt_id(Universe* U_, lua_State* L_, int i) |
541 | */ | ||
542 | [[nodiscard]] static lua_Integer get_mt_id(Universe* U, lua_State* L_, int i) | ||
543 | { | 490 | { |
544 | i = lua_absindex(L_, i); | 491 | i = lua_absindex(L_, i); |
545 | 492 | ||
546 | STACK_GROW(L_, 3); | 493 | STACK_GROW(L_, 3); |
547 | 494 | ||
548 | STACK_CHECK_START_REL(L_, 0); | 495 | STACK_CHECK_START_REL(L_, 0); |
549 | push_registry_subtable(L_, kMtIdRegKey); // ... _R[kMtIdRegKey] | 496 | push_registry_subtable(L_, kMtIdRegKey); // L_: ... _R[kMtIdRegKey] |
550 | lua_pushvalue(L_, i); // ... _R[kMtIdRegKey] {mt} | 497 | lua_pushvalue(L_, i); // L_: ... _R[kMtIdRegKey] {mt} |
551 | lua_rawget(L_, -2); // ... _R[kMtIdRegKey] mtk? | 498 | lua_rawget(L_, -2); // L_: ... _R[kMtIdRegKey] mtk? |
552 | 499 | ||
553 | lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil | 500 | lua_Integer id{ lua_tointeger(L_, -1) }; // 0 for nil |
554 | lua_pop(L_, 1); // ... _R[kMtIdRegKey] | 501 | lua_pop(L_, 1); // L_: ... _R[kMtIdRegKey] |
555 | STACK_CHECK(L_, 1); | 502 | STACK_CHECK(L_, 1); |
556 | 503 | ||
557 | if (id == 0) | 504 | if (id == 0) { |
558 | { | 505 | id = U_->next_mt_id.fetch_add(1, std::memory_order_relaxed); |
559 | id = U->next_mt_id.fetch_add(1, std::memory_order_relaxed); | ||
560 | 506 | ||
561 | // Create two-way references: id_uint <-> table | 507 | // Create two-way references: id_uint <-> table |
562 | lua_pushvalue(L_, i); // ... _R[kMtIdRegKey] {mt} | 508 | lua_pushvalue(L_, i); // L_: ... _R[kMtIdRegKey] {mt} |
563 | lua_pushinteger(L_, id); // ... _R[kMtIdRegKey] {mt} id | 509 | lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] {mt} id |
564 | lua_rawset(L_, -3); // ... _R[kMtIdRegKey] | 510 | lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey] |
565 | 511 | ||
566 | lua_pushinteger(L_, id); // ... _R[kMtIdRegKey] id | 512 | lua_pushinteger(L_, id); // L_: ... _R[kMtIdRegKey] id |
567 | lua_pushvalue(L_, i); // ... _R[kMtIdRegKey] id {mt} | 513 | lua_pushvalue(L_, i); // L_: ... _R[kMtIdRegKey] id {mt} |
568 | lua_rawset(L_, -3); // ... _R[kMtIdRegKey] | 514 | lua_rawset(L_, -3); // L_: ... _R[kMtIdRegKey] |
569 | } | 515 | } |
570 | lua_pop(L_, 1); // ... | 516 | lua_pop(L_, 1); // L_: ... |
571 | STACK_CHECK(L_, 0); | 517 | STACK_CHECK(L_, 0); |
572 | 518 | ||
573 | return id; | 519 | return id; |
@@ -599,74 +545,62 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | |||
599 | 545 | ||
600 | // ################################################################################################# | 546 | // ################################################################################################# |
601 | 547 | ||
602 | /* | 548 | // retrieve the name of a function/table in the lookup database |
603 | * retrieve the name of a function/table in the lookup database | 549 | [[nodiscard]] static char const* find_lookup_name(lua_State* L_, int i_, LookupMode mode_, char const* upName_, size_t* len_) |
604 | */ | ||
605 | [[nodiscard]] static char const* find_lookup_name(lua_State* L_, int i, LookupMode mode_, char const* upName_, size_t* len_) | ||
606 | { | 550 | { |
607 | DEBUGSPEW_CODE( Universe* const U = universe_get( L_)); | 551 | DEBUGSPEW_CODE(Universe* const U = universe_get(L_)); |
608 | char const* fqn; | 552 | char const* fqn; |
609 | LUA_ASSERT(L_, lua_isfunction( L_, i) || lua_istable( L_, i)); // ... v ... | 553 | LUA_ASSERT(L_, lua_isfunction(L_, i_) || lua_istable(L_, i_)); // L_: ... v ... |
610 | STACK_CHECK_START_REL(L_, 0); | 554 | STACK_CHECK_START_REL(L_, 0); |
611 | STACK_GROW( L_, 3); // up to 3 slots are necessary on error | 555 | STACK_GROW(L_, 3); // up to 3 slots are necessary on error |
612 | if (mode_ == LookupMode::FromKeeper) | 556 | if (mode_ == LookupMode::FromKeeper) { |
613 | { | 557 | lua_CFunction f = lua_tocfunction(L_, i_); // should *always* be func_lookup_sentinel or table_lookup_sentinel! |
614 | lua_CFunction f = lua_tocfunction( L_, i); // should *always* be func_lookup_sentinel or table_lookup_sentinel! | 558 | if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) { |
615 | if (f == func_lookup_sentinel || f == table_lookup_sentinel || f == userdata_clone_sentinel) | 559 | lua_getupvalue(L_, i_, 1); // L_: ... v ... "f.q.n" |
616 | { | 560 | } else { |
617 | lua_getupvalue( L_, i, 1); // ... v ... "f.q.n" | ||
618 | } | ||
619 | else | ||
620 | { | ||
621 | // if this is not a sentinel, this is some user-created table we wanted to lookup | 561 | // if this is not a sentinel, this is some user-created table we wanted to lookup |
622 | LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i)); | 562 | LUA_ASSERT(L_, nullptr == f && lua_istable(L_, i_)); |
623 | // push anything that will convert to nullptr string | 563 | // push anything that will convert to nullptr string |
624 | lua_pushnil( L_); // ... v ... nil | 564 | lua_pushnil(L_); // L_: ... v ... nil |
625 | } | 565 | } |
626 | } | 566 | } else { |
627 | else | ||
628 | { | ||
629 | // fetch the name from the source state's lookup table | 567 | // fetch the name from the source state's lookup table |
630 | kLookupRegKey.pushValue(L_); // ... v ... {} | 568 | kLookupRegKey.pushValue(L_); // L_: ... v ... {} |
631 | STACK_CHECK( L_, 1); | 569 | STACK_CHECK(L_, 1); |
632 | LUA_ASSERT(L_, lua_istable( L_, -1)); | 570 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
633 | lua_pushvalue( L_, i); // ... v ... {} v | 571 | lua_pushvalue(L_, i_); // L_: ... v ... {} v |
634 | lua_rawget( L_, -2); // ... v ... {} "f.q.n" | 572 | lua_rawget(L_, -2); // L_: ... v ... {} "f.q.n" |
635 | } | 573 | } |
636 | fqn = lua_tolstring( L_, -1, len_); | 574 | fqn = lua_tolstring(L_, -1, len_); |
637 | DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END, fqn)); | 575 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "function [C] %s \n" INDENT_END(U), fqn)); |
638 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database | 576 | // popping doesn't invalidate the pointer since this is an interned string gotten from the lookup database |
639 | lua_pop( L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // ... v ... | 577 | lua_pop(L_, (mode_ == LookupMode::FromKeeper) ? 1 : 2); // L_: ... v ... |
640 | STACK_CHECK( L_, 0); | 578 | STACK_CHECK(L_, 0); |
641 | if (nullptr == fqn && !lua_istable(L_, i)) // raise an error if we try to send an unknown function (but not for tables) | 579 | if (nullptr == fqn && !lua_istable(L_, i_)) { // raise an error if we try to send an unknown function (but not for tables) |
642 | { | ||
643 | char const *from, *typewhat, *what, *gotchaA, *gotchaB; | 580 | char const *from, *typewhat, *what, *gotchaA, *gotchaB; |
644 | // try to discover the name of the function we want to send | 581 | // try to discover the name of the function we want to send |
645 | lua_getglobal( L_, "decoda_name"); // ... v ... decoda_name | 582 | lua_getglobal(L_, "decoda_name"); // L_: ... v ... decoda_name |
646 | from = lua_tostring( L_, -1); | 583 | from = lua_tostring(L_, -1); |
647 | lua_pushcfunction( L_, luaG_nameof); // ... v ... decoda_name luaG_nameof | 584 | lua_pushcfunction(L_, luaG_nameof); // L_: ... v ... decoda_name luaG_nameof |
648 | lua_pushvalue( L_, i); // ... v ... decoda_name luaG_nameof t | 585 | lua_pushvalue(L_, i_); // L_: ... v ... decoda_name luaG_nameof t |
649 | lua_call( L_, 1, 2); // ... v ... decoda_name "type" "name"|nil | 586 | lua_call(L_, 1, 2); // L_: ... v ... decoda_name "type" "name"|nil |
650 | typewhat = (lua_type( L_, -2) == LUA_TSTRING) ? lua_tostring( L_, -2) : luaL_typename( L_, -2); | 587 | typewhat = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : luaL_typename(L_, -2); |
651 | // second return value can be nil if the table was not found | 588 | // second return value can be nil if the table was not found |
652 | // probable reason: the function was removed from the source Lua state before Lanes was required. | 589 | // probable reason: the function was removed from the source Lua state before Lanes was required. |
653 | if (lua_isnil( L_, -1)) | 590 | if (lua_isnil(L_, -1)) { |
654 | { | ||
655 | gotchaA = " referenced by"; | 591 | gotchaA = " referenced by"; |
656 | gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; | 592 | gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; |
657 | what = upName_; | 593 | what = upName_; |
658 | } | 594 | } else { |
659 | else | ||
660 | { | ||
661 | gotchaA = ""; | 595 | gotchaA = ""; |
662 | gotchaB = ""; | 596 | gotchaB = ""; |
663 | what = (lua_type( L_, -1) == LUA_TSTRING) ? lua_tostring( L_, -1) : luaL_typename( L_, -1); | 597 | what = (lua_type(L_, -1) == LUA_TSTRING) ? lua_tostring(L_, -1) : luaL_typename(L_, -1); |
664 | } | 598 | } |
665 | raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB); | 599 | raise_luaL_error(L_, "%s%s '%s' not found in %s origin transfer database.%s", typewhat, gotchaA, what, from ? from : "main", gotchaB); |
666 | *len_ = 0; | 600 | *len_ = 0; |
667 | return nullptr; | 601 | return nullptr; |
668 | } | 602 | } |
669 | STACK_CHECK( L_, 0); | 603 | STACK_CHECK(L_, 0); |
670 | return fqn; | 604 | return fqn; |
671 | } | 605 | } |
672 | 606 | ||
@@ -678,60 +612,54 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | |||
678 | // get the name of the table we want to send | 612 | // get the name of the table we want to send |
679 | size_t len; | 613 | size_t len; |
680 | char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len); | 614 | char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len); |
681 | if (nullptr == fqn) // name not found, it is some user-created table | 615 | if (nullptr == fqn) { // name not found, it is some user-created table |
682 | { | ||
683 | return false; | 616 | return false; |
684 | } | 617 | } |
685 | // push the equivalent table in the destination's stack, retrieved from the lookup table | 618 | // push the equivalent table in the destination's stack, retrieved from the lookup table |
686 | STACK_CHECK_START_REL(L2, 0); // L // L2 | 619 | STACK_CHECK_START_REL(L2, 0); |
687 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error | 620 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error |
688 | switch (mode) | 621 | switch (mode) { |
689 | { | 622 | default: // shouldn't happen, in theory... |
690 | default: // shouldn't happen, in theory... | ||
691 | raise_luaL_error(L1, "internal error: unknown lookup mode"); | 623 | raise_luaL_error(L1, "internal error: unknown lookup mode"); |
692 | break; | 624 | break; |
693 | 625 | ||
694 | case LookupMode::ToKeeper: | 626 | case LookupMode::ToKeeper: |
695 | // push a sentinel closure that holds the lookup name as upvalue | 627 | // push a sentinel closure that holds the lookup name as upvalue |
696 | lua_pushlstring(L2, fqn, len); // "f.q.n" | 628 | lua_pushlstring(L2, fqn, len); // L1: ... t ... L2: "f.q.n" |
697 | lua_pushcclosure(L2, table_lookup_sentinel, 1); // f | 629 | lua_pushcclosure(L2, table_lookup_sentinel, 1); // L1: ... t ... L2: f |
698 | break; | 630 | break; |
699 | 631 | ||
700 | case LookupMode::LaneBody: | 632 | case LookupMode::LaneBody: |
701 | case LookupMode::FromKeeper: | 633 | case LookupMode::FromKeeper: |
702 | kLookupRegKey.pushValue(L2); // {} | 634 | kLookupRegKey.pushValue(L2); // L1: ... t ... L2: {} |
703 | STACK_CHECK(L2, 1); | 635 | STACK_CHECK(L2, 1); |
704 | LUA_ASSERT(L1, lua_istable(L2, -1)); | 636 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
705 | lua_pushlstring(L2, fqn, len); // {} "f.q.n" | 637 | lua_pushlstring(L2, fqn, len); // L2: {} "f.q.n" |
706 | lua_rawget(L2, -2); // {} t | 638 | lua_rawget(L2, -2); // L2: {} t |
707 | // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) | 639 | // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) |
708 | // but not when we extract something out of a keeper, as there is nothing to clone! | 640 | // but not when we extract something out of a keeper, as there is nothing to clone! |
709 | if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody) | 641 | if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody) { |
710 | { | 642 | lua_pop(L2, 2); // L1: ... t ... L2: |
711 | lua_pop(L2, 2); // | ||
712 | STACK_CHECK(L2, 0); | 643 | STACK_CHECK(L2, 0); |
713 | return false; | 644 | return false; |
714 | } | 645 | } else if (!lua_istable(L2, -1)) { |
715 | else if (!lua_istable(L2, -1)) | 646 | char const *from, *to; |
716 | { | 647 | lua_getglobal(L1, "decoda_name"); // L1: ... t ... decoda_name |
717 | char const* from, *to; | ||
718 | lua_getglobal(L1, "decoda_name"); // ... t ... decoda_name | ||
719 | from = lua_tostring(L1, -1); | 648 | from = lua_tostring(L1, -1); |
720 | lua_pop(L1, 1); // ... t ... | 649 | lua_pop(L1, 1); // L1: ... t ... |
721 | lua_getglobal(L2, "decoda_name"); // {} t decoda_name | 650 | lua_getglobal(L2, "decoda_name"); // L1: ... t ... L2: {} t decoda_name |
722 | to = lua_tostring(L2, -1); | 651 | to = lua_tostring(L2, -1); |
723 | lua_pop(L2, 1); // {} t | 652 | lua_pop(L2, 1); // L1: ... t ... L2: {} t |
724 | // 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 | 653 | // 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 |
725 | raise_luaL_error( | 654 | raise_luaL_error( |
726 | (mode == LookupMode::FromKeeper) ? L2 : L1 | 655 | (mode == LookupMode::FromKeeper) ? L2 : L1, |
727 | , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database." | 656 | "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database.", |
728 | , from ? from : "main" | 657 | from ? from : "main", |
729 | , fqn | 658 | fqn, |
730 | , to ? to : "main" | 659 | to ? to : "main"); |
731 | ); | ||
732 | return false; | 660 | return false; |
733 | } | 661 | } |
734 | lua_remove(L2, -2); // t | 662 | lua_remove(L2, -2); // L1: ... t ... L2: t |
735 | break; | 663 | break; |
736 | } | 664 | } |
737 | STACK_CHECK(L2, 1); | 665 | STACK_CHECK(L2, 1); |
@@ -740,12 +668,12 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | |||
740 | 668 | ||
741 | // ################################################################################################# | 669 | // ################################################################################################# |
742 | 670 | ||
743 | /* | 671 | /* |
744 | * Check if we've already copied the same table from 'L', and | 672 | * Check if we've already copied the same table from 'L1_', and |
745 | * reuse the old copy. This allows table upvalues shared by multiple | 673 | * reuse the old copy. This allows table upvalues shared by multiple |
746 | * local functions to point to the same table, also in the target. | 674 | * local functions to point to the same table, also in the target. |
747 | * | 675 | * |
748 | * Always pushes a table to 'L2'. | 676 | * Always pushes a table to 'L2_'. |
749 | * | 677 | * |
750 | * Returns true if the table was cached (no need to fill it!); false if | 678 | * Returns true if the table was cached (no need to fill it!); false if |
751 | * it's a virgin. | 679 | * it's a virgin. |
@@ -755,271 +683,247 @@ static constexpr RegistryUniqueKey kMtIdRegKey{ 0xA8895DCF4EC3FE3Cull }; | |||
755 | void const* p{ lua_topointer(L1, i) }; | 683 | void const* p{ lua_topointer(L1, i) }; |
756 | 684 | ||
757 | LUA_ASSERT(L1, L2_cache_i != 0); | 685 | LUA_ASSERT(L1, L2_cache_i != 0); |
758 | STACK_GROW(L2, 3); // L2 | 686 | STACK_GROW(L2, 3); |
759 | STACK_CHECK_START_REL(L2, 0); | 687 | STACK_CHECK_START_REL(L2, 0); |
760 | 688 | ||
761 | // We don't need to use the from state ('L1') in ID since the life span | 689 | // We don't need to use the from state ('L1') in ID since the life span |
762 | // is only for the duration of a copy (both states are locked). | 690 | // is only for the duration of a copy (both states are locked). |
763 | // push a light userdata uniquely representing the table | 691 | // push a light userdata uniquely representing the table |
764 | lua_pushlightuserdata(L2, const_cast<void*>(p)); // ... p | 692 | lua_pushlightuserdata(L2, const_cast<void*>(p)); // L2: ... p |
765 | 693 | ||
766 | //fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1)); | 694 | // fprintf(stderr, "<< ID: %s >>\n", lua_tostring(L2, -1)); |
767 | 695 | ||
768 | lua_rawget(L2, L2_cache_i); // ... {cached|nil} | 696 | lua_rawget(L2, L2_cache_i); // L2: ... {cached|nil} |
769 | bool const not_found_in_cache{ lua_isnil(L2, -1) }; | 697 | bool const not_found_in_cache{ lua_isnil(L2, -1) }; |
770 | if (not_found_in_cache) | 698 | if (not_found_in_cache) { |
771 | { | ||
772 | // create a new entry in the cache | 699 | // create a new entry in the cache |
773 | lua_pop(L2, 1); // ... | 700 | lua_pop(L2, 1); // L2: ... |
774 | lua_newtable(L2); // ... {} | 701 | lua_newtable(L2); // L2: ... {} |
775 | lua_pushlightuserdata(L2, const_cast<void*>(p)); // ... {} p | 702 | lua_pushlightuserdata(L2, const_cast<void*>(p)); // L2: ... {} p |
776 | lua_pushvalue(L2, -2); // ... {} p {} | 703 | lua_pushvalue(L2, -2); // L2: ... {} p {} |
777 | lua_rawset(L2, L2_cache_i); // ... {} | 704 | lua_rawset(L2, L2_cache_i); // L2: ... {} |
778 | } | 705 | } |
779 | STACK_CHECK(L2, 1); | 706 | STACK_CHECK(L2, 1); |
780 | LUA_ASSERT(L1, lua_istable( L2, -1)); | 707 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
781 | return !not_found_in_cache; | 708 | return !not_found_in_cache; |
782 | } | 709 | } |
783 | 710 | ||
784 | // ################################################################################################# | 711 | // ################################################################################################# |
785 | 712 | ||
786 | /* | 713 | // Return some name helping to identify an object |
787 | * Return some name helping to identify an object | ||
788 | */ | ||
789 | [[nodiscard]] static int discover_object_name_recur(lua_State* L_, int shortest_, int depth_) | 714 | [[nodiscard]] static int discover_object_name_recur(lua_State* L_, int shortest_, int depth_) |
790 | { | 715 | { |
791 | int const what = 1; // o "r" {c} {fqn} ... {?} | 716 | int const what = 1; // L_: o "r" {c} {fqn} ... {?} |
792 | int const result = 2; | 717 | int const result = 2; |
793 | int const cache = 3; | 718 | int const cache = 3; |
794 | int const fqn = 4; | 719 | int const fqn = 4; |
795 | // no need to scan this table if the name we will discover is longer than one we already know | 720 | // no need to scan this table if the name we will discover is longer than one we already know |
796 | if (shortest_ <= depth_ + 1) | 721 | if (shortest_ <= depth_ + 1) { |
797 | { | ||
798 | return shortest_; | 722 | return shortest_; |
799 | } | 723 | } |
800 | STACK_GROW(L_, 3); | 724 | STACK_GROW(L_, 3); |
801 | STACK_CHECK_START_REL(L_, 0); | 725 | STACK_CHECK_START_REL(L_, 0); |
802 | // stack top contains the table to search in | 726 | // stack top contains the table to search in |
803 | lua_pushvalue(L_, -1); // o "r" {c} {fqn} ... {?} {?} | 727 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} |
804 | lua_rawget(L_, cache); // o "r" {c} {fqn} ... {?} nil/1 | 728 | lua_rawget(L_, cache); // L_: o "r" {c} {fqn} ... {?} nil/1 |
805 | // if table is already visited, we are done | 729 | // if table is already visited, we are done |
806 | if (!lua_isnil(L_, -1)) | 730 | if (!lua_isnil(L_, -1)) { |
807 | { | 731 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} |
808 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} | ||
809 | return shortest_; | 732 | return shortest_; |
810 | } | 733 | } |
811 | // examined table is not in the cache, add it now | 734 | // examined table is not in the cache, add it now |
812 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} | 735 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} |
813 | lua_pushvalue(L_, -1); // o "r" {c} {fqn} ... {?} {?} | 736 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} |
814 | lua_pushinteger(L_, 1); // o "r" {c} {fqn} ... {?} {?} 1 | 737 | lua_pushinteger(L_, 1); // L_: o "r" {c} {fqn} ... {?} {?} 1 |
815 | lua_rawset(L_, cache); // o "r" {c} {fqn} ... {?} | 738 | lua_rawset(L_, cache); // L_: o "r" {c} {fqn} ... {?} |
816 | // scan table contents | 739 | // scan table contents |
817 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} nil | 740 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} nil |
818 | while (lua_next(L_, -2)) // o "r" {c} {fqn} ... {?} k v | 741 | while (lua_next(L_, -2)) { // L_: o "r" {c} {fqn} ... {?} k v |
819 | { | 742 | // char const *const strKey = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : nullptr; // only for debugging |
820 | //char const *const strKey = (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostring(L_, -2) : nullptr; // only for debugging | 743 | // lua_Number const numKey = (lua_type(L_, -2) == LUA_TNUMBER) ? lua_tonumber(L_, -2) : -6666; // only for debugging |
821 | //lua_Number const numKey = (lua_type(L_, -2) == LUA_TNUMBER) ? lua_tonumber(L_, -2) : -6666; // only for debugging | ||
822 | STACK_CHECK(L_, 2); | 744 | STACK_CHECK(L_, 2); |
823 | // append key name to fqn stack | 745 | // append key name to fqn stack |
824 | ++ depth_; | 746 | ++depth_; |
825 | lua_pushvalue(L_, -2); // o "r" {c} {fqn} ... {?} k v k | 747 | lua_pushvalue(L_, -2); // L_: o "r" {c} {fqn} ... {?} k v k |
826 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k v | 748 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k v |
827 | if (lua_rawequal(L_, -1, what)) // is it what we are looking for? | 749 | if (lua_rawequal(L_, -1, what)) { // is it what we are looking for? |
828 | { | ||
829 | STACK_CHECK(L_, 2); | 750 | STACK_CHECK(L_, 2); |
830 | // update shortest name | 751 | // update shortest name |
831 | if (depth_ < shortest_) | 752 | if (depth_ < shortest_) { |
832 | { | ||
833 | shortest_ = depth_; | 753 | shortest_ = depth_; |
834 | std::ignore = luaG_pushFQN(L_, fqn, depth_, nullptr); // o "r" {c} {fqn} ... {?} k v "fqn" | 754 | std::ignore = luaG_pushFQN(L_, fqn, depth_, nullptr); // L_: o "r" {c} {fqn} ... {?} k v "fqn" |
835 | lua_replace(L_, result); // o "r" {c} {fqn} ... {?} k v | 755 | lua_replace(L_, result); // L_: o "r" {c} {fqn} ... {?} k v |
836 | } | 756 | } |
837 | // no need to search further at this level | 757 | // no need to search further at this level |
838 | lua_pop(L_, 2); // o "r" {c} {fqn} ... {?} | 758 | lua_pop(L_, 2); // L_: o "r" {c} {fqn} ... {?} |
839 | STACK_CHECK(L_, 0); | 759 | STACK_CHECK(L_, 0); |
840 | break; | 760 | break; |
841 | } | 761 | } |
842 | switch (lua_type(L_, -1)) // o "r" {c} {fqn} ... {?} k v | 762 | switch (lua_type(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k v |
843 | { | 763 | default: // nil, boolean, light userdata, number and string aren't identifiable |
844 | default: // nil, boolean, light userdata, number and string aren't identifiable | ||
845 | break; | 764 | break; |
846 | 765 | ||
847 | case LUA_TTABLE: // o "r" {c} {fqn} ... {?} k {} | 766 | case LUA_TTABLE: // L_: o "r" {c} {fqn} ... {?} k {} |
848 | STACK_CHECK(L_, 2); | 767 | STACK_CHECK(L_, 2); |
849 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); | 768 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); |
850 | // search in the table's metatable too | 769 | // search in the table's metatable too |
851 | if (lua_getmetatable(L_, -1)) // o "r" {c} {fqn} ... {?} k {} {mt} | 770 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k {} {mt} |
852 | { | 771 | if (lua_istable(L_, -1)) { |
853 | if (lua_istable(L_, -1)) | 772 | ++depth_; |
854 | { | 773 | lua_pushliteral(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" |
855 | ++ depth_; | 774 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} |
856 | lua_pushliteral(L_, "__metatable"); // o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" | ||
857 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt} | ||
858 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); | 775 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); |
859 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} k {} {mt} nil | 776 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} nil |
860 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k {} {mt} | 777 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} |
861 | -- depth_; | 778 | --depth_; |
862 | } | 779 | } |
863 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k {} | 780 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k {} |
864 | } | 781 | } |
865 | STACK_CHECK(L_, 2); | 782 | STACK_CHECK(L_, 2); |
866 | break; | 783 | break; |
867 | 784 | ||
868 | case LUA_TTHREAD: // o "r" {c} {fqn} ... {?} k T | 785 | case LUA_TTHREAD: // L_: o "r" {c} {fqn} ... {?} k T |
869 | // TODO: explore the thread's stack frame looking for our culprit? | 786 | // TODO: explore the thread's stack frame looking for our culprit? |
870 | break; | 787 | break; |
871 | 788 | ||
872 | case LUA_TUSERDATA: // o "r" {c} {fqn} ... {?} k U | 789 | case LUA_TUSERDATA: // L_: o "r" {c} {fqn} ... {?} k U |
873 | STACK_CHECK(L_, 2); | 790 | STACK_CHECK(L_, 2); |
874 | // search in the object's metatable (some modules are built that way) | 791 | // search in the object's metatable (some modules are built that way) |
875 | if (lua_getmetatable(L_, -1)) // o "r" {c} {fqn} ... {?} k U {mt} | 792 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k U {mt} |
876 | { | 793 | if (lua_istable(L_, -1)) { |
877 | if (lua_istable(L_, -1)) | 794 | ++depth_; |
878 | { | 795 | lua_pushliteral(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} k U {mt} "__metatable" |
879 | ++ depth_; | 796 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k U {mt} |
880 | lua_pushliteral(L_, "__metatable"); // o "r" {c} {fqn} ... {?} k U {mt} "__metatable" | ||
881 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt} | ||
882 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); | 797 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); |
883 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} k U {mt} nil | 798 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k U {mt} nil |
884 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k U {mt} | 799 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k U {mt} |
885 | -- depth_; | 800 | --depth_; |
886 | } | 801 | } |
887 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k U | 802 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U |
888 | } | 803 | } |
889 | STACK_CHECK(L_, 2); | 804 | STACK_CHECK(L_, 2); |
890 | // search in the object's uservalues | 805 | // search in the object's uservalues |
891 | { | 806 | { |
892 | int uvi = 1; | 807 | int uvi = 1; |
893 | while (lua_getiuservalue(L_, -1, uvi) != LUA_TNONE) // o "r" {c} {fqn} ... {?} k U {u} | 808 | while (lua_getiuservalue(L_, -1, uvi) != LUA_TNONE) { // L_: o "r" {c} {fqn} ... {?} k U {u} |
894 | { | 809 | if (lua_istable(L_, -1)) { // if it is a table, look inside |
895 | if (lua_istable(L_, -1)) // if it is a table, look inside | 810 | ++depth_; |
896 | { | 811 | lua_pushliteral(L_, "uservalue"); // L_: o "r" {c} {fqn} ... {?} k v {u} "uservalue" |
897 | ++ depth_; | 812 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k v {u} |
898 | lua_pushliteral(L_, "uservalue"); // o "r" {c} {fqn} ... {?} k v {u} "uservalue" | ||
899 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u} | ||
900 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); | 813 | shortest_ = discover_object_name_recur(L_, shortest_, depth_); |
901 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} k v {u} nil | 814 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k v {u} nil |
902 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k v {u} | 815 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k v {u} |
903 | -- depth_; | 816 | --depth_; |
904 | } | 817 | } |
905 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k U | 818 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U |
906 | ++ uvi; | 819 | ++uvi; |
907 | } | 820 | } |
908 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 821 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now |
909 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k U | 822 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U |
910 | } | 823 | } |
911 | STACK_CHECK(L_, 2); | 824 | STACK_CHECK(L_, 2); |
912 | break; | 825 | break; |
913 | } | 826 | } |
914 | // make ready for next iteration | 827 | // make ready for next iteration |
915 | lua_pop(L_, 1); // o "r" {c} {fqn} ... {?} k | 828 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k |
916 | // remove name from fqn stack | 829 | // remove name from fqn stack |
917 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} k nil | 830 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k nil |
918 | lua_rawseti(L_, fqn, depth_); // o "r" {c} {fqn} ... {?} k | 831 | lua_rawseti(L_, fqn, depth_); // L_: o "r" {c} {fqn} ... {?} k |
919 | STACK_CHECK(L_, 1); | 832 | STACK_CHECK(L_, 1); |
920 | -- depth_; | 833 | --depth_; |
921 | } // o "r" {c} {fqn} ... {?} | 834 | } // L_: o "r" {c} {fqn} ... {?} |
922 | STACK_CHECK(L_, 0); | 835 | STACK_CHECK(L_, 0); |
923 | // remove the visited table from the cache, in case a shorter path to the searched object exists | 836 | // remove the visited table from the cache, in case a shorter path to the searched object exists |
924 | lua_pushvalue(L_, -1); // o "r" {c} {fqn} ... {?} {?} | 837 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} |
925 | lua_pushnil(L_); // o "r" {c} {fqn} ... {?} {?} nil | 838 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} {?} nil |
926 | lua_rawset(L_, cache); // o "r" {c} {fqn} ... {?} | 839 | lua_rawset(L_, cache); // L_: o "r" {c} {fqn} ... {?} |
927 | STACK_CHECK(L_, 0); | 840 | STACK_CHECK(L_, 0); |
928 | return shortest_; | 841 | return shortest_; |
929 | } | 842 | } |
930 | 843 | ||
931 | // ################################################################################################# | 844 | // ################################################################################################# |
932 | 845 | ||
933 | /* | 846 | // "type", "name" = lanes.nameof( o) |
934 | * "type", "name" = lanes.nameof( o) | 847 | int luaG_nameof(lua_State* L_) |
935 | */ | ||
936 | int luaG_nameof( lua_State* L_) | ||
937 | { | 848 | { |
938 | int const what{ lua_gettop(L_) }; | 849 | int const what{ lua_gettop(L_) }; |
939 | if (what > 1) | 850 | if (what > 1) { |
940 | { | 851 | raise_luaL_argerror(L_, what, "too many arguments."); |
941 | raise_luaL_argerror( L_, what, "too many arguments."); | ||
942 | } | 852 | } |
943 | 853 | ||
944 | // nil, boolean, light userdata, number and string aren't identifiable | 854 | // nil, boolean, light userdata, number and string aren't identifiable |
945 | if (lua_type( L_, 1) < LUA_TTABLE) | 855 | if (lua_type(L_, 1) < LUA_TTABLE) { |
946 | { | 856 | lua_pushstring(L_, luaL_typename(L_, 1)); // L_: o "type" |
947 | lua_pushstring( L_, luaL_typename( L_, 1)); // o "type" | 857 | lua_insert(L_, -2); // L_: "type" o |
948 | lua_insert( L_, -2); // "type" o | ||
949 | return 2; | 858 | return 2; |
950 | } | 859 | } |
951 | 860 | ||
952 | STACK_GROW( L_, 4); | 861 | STACK_GROW(L_, 4); |
953 | STACK_CHECK_START_REL(L_, 0); | 862 | STACK_CHECK_START_REL(L_, 0); |
954 | // this slot will contain the shortest name we found when we are done | 863 | // this slot will contain the shortest name we found when we are done |
955 | lua_pushnil( L_); // o nil | 864 | lua_pushnil(L_); // L_: o nil |
956 | // push a cache that will contain all already visited tables | 865 | // push a cache that will contain all already visited tables |
957 | lua_newtable( L_); // o nil {c} | 866 | lua_newtable(L_); // o nil {c} |
958 | // push a table whose contents are strings that, when concatenated, produce unique name | 867 | // push a table whose contents are strings that, when concatenated, produce unique name |
959 | lua_newtable( L_); // o nil {c} {fqn} | 868 | lua_newtable(L_); // L_: o nil {c} {fqn} |
960 | lua_pushliteral( L_, "_G"); // o nil {c} {fqn} "_G" | 869 | lua_pushliteral(L_, "_G"); // L_: o nil {c} {fqn} "_G" |
961 | lua_rawseti( L_, -2, 1); // o nil {c} {fqn} | 870 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} |
962 | // this is where we start the search | 871 | // this is where we start the search |
963 | lua_pushglobaltable( L_); // o nil {c} {fqn} _G | 872 | lua_pushglobaltable(L_); // L_: o nil {c} {fqn} _G |
964 | (void) discover_object_name_recur( L_, 6666, 1); | 873 | std::ignore = discover_object_name_recur(L_, 6666, 1); |
965 | if (lua_isnil( L_, 2)) // try again with registry, just in case... | 874 | if (lua_isnil(L_, 2)) { // try again with registry, just in case... |
966 | { | 875 | lua_pop(L_, 1); // L_: o nil {c} {fqn} |
967 | lua_pop( L_, 1); // o nil {c} {fqn} | 876 | lua_pushliteral(L_, "_R"); // L_: o nil {c} {fqn} "_R" |
968 | lua_pushliteral( L_, "_R"); // o nil {c} {fqn} "_R" | 877 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} |
969 | lua_rawseti( L_, -2, 1); // o nil {c} {fqn} | 878 | lua_pushvalue(L_, LUA_REGISTRYINDEX); // L_: o nil {c} {fqn} _R |
970 | lua_pushvalue( L_, LUA_REGISTRYINDEX); // o nil {c} {fqn} _R | 879 | (void) discover_object_name_recur(L_, 6666, 1); |
971 | (void) discover_object_name_recur( L_, 6666, 1); | 880 | } |
972 | } | 881 | lua_pop(L_, 3); // L_: o "result" |
973 | lua_pop( L_, 3); // o "result" | 882 | STACK_CHECK(L_, 1); |
974 | STACK_CHECK( L_, 1); | 883 | lua_pushstring(L_, luaL_typename(L_, 1)); // L_: o "result" "type" |
975 | lua_pushstring( L_, luaL_typename( L_, 1)); // o "result" "type" | 884 | lua_replace(L_, -3); // L_: "type" "result" |
976 | lua_replace( L_, -3); // "type" "result" | ||
977 | return 2; | 885 | return 2; |
978 | } | 886 | } |
979 | 887 | ||
980 | // ################################################################################################# | 888 | // ################################################################################################# |
981 | 889 | ||
982 | /* | 890 | // Push a looked-up native/LuaJIT function. |
983 | * Push a looked-up native/LuaJIT function. | ||
984 | */ | ||
985 | void InterCopyContext::lookup_native_func() const | 891 | void InterCopyContext::lookup_native_func() const |
986 | { | 892 | { |
987 | // get the name of the function we want to send | 893 | // get the name of the function we want to send |
988 | size_t len; | 894 | size_t len; |
989 | char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len); | 895 | char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len); |
990 | // push the equivalent function in the destination's stack, retrieved from the lookup table | 896 | // push the equivalent function in the destination's stack, retrieved from the lookup table |
991 | STACK_CHECK_START_REL(L2, 0); // L1 // L2 | 897 | STACK_CHECK_START_REL(L2, 0); |
992 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error | 898 | STACK_GROW(L2, 3); // up to 3 slots are necessary on error |
993 | switch (mode) | 899 | switch (mode) { |
994 | { | 900 | default: // shouldn't happen, in theory... |
995 | default: // shouldn't happen, in theory... | ||
996 | raise_luaL_error(L1, "internal error: unknown lookup mode"); | 901 | raise_luaL_error(L1, "internal error: unknown lookup mode"); |
997 | break; | 902 | break; |
998 | 903 | ||
999 | case LookupMode::ToKeeper: | 904 | case LookupMode::ToKeeper: |
1000 | // push a sentinel closure that holds the lookup name as upvalue | 905 | // push a sentinel closure that holds the lookup name as upvalue |
1001 | lua_pushlstring(L2, fqn, len); // "f.q.n" | 906 | lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: "f.q.n" |
1002 | lua_pushcclosure(L2, func_lookup_sentinel, 1); // f | 907 | lua_pushcclosure(L2, func_lookup_sentinel, 1); // L1: ... f ... L2: f |
1003 | break; | 908 | break; |
1004 | 909 | ||
1005 | case LookupMode::LaneBody: | 910 | case LookupMode::LaneBody: |
1006 | case LookupMode::FromKeeper: | 911 | case LookupMode::FromKeeper: |
1007 | kLookupRegKey.pushValue(L2); // {} | 912 | kLookupRegKey.pushValue(L2); // L1: ... f ... L2: {} |
1008 | STACK_CHECK(L2, 1); | 913 | STACK_CHECK(L2, 1); |
1009 | LUA_ASSERT(L1, lua_istable(L2, -1)); | 914 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
1010 | lua_pushlstring(L2, fqn, len); // {} "f.q.n" | 915 | lua_pushlstring(L2, fqn, len); // L1: ... f ... L2: {} "f.q.n" |
1011 | lua_rawget(L2, -2); // {} f | 916 | lua_rawget(L2, -2); // L1: ... f ... L2: {} f |
1012 | // nil means we don't know how to transfer stuff: user should do something | 917 | // nil means we don't know how to transfer stuff: user should do something |
1013 | // anything other than function or table should not happen! | 918 | // anything other than function or table should not happen! |
1014 | if (!lua_isfunction( L2, -1) && !lua_istable( L2, -1)) | 919 | if (!lua_isfunction(L2, -1) && !lua_istable(L2, -1)) { |
1015 | { | 920 | char const *from, *to; |
1016 | char const* from, * to; | 921 | lua_getglobal(L1, "decoda_name"); // L1: ... f ... decoda_name |
1017 | lua_getglobal(L1, "decoda_name"); // ... f ... decoda_name | ||
1018 | from = lua_tostring(L1, -1); | 922 | from = lua_tostring(L1, -1); |
1019 | lua_pop(L1, 1); // ... f ... | 923 | lua_pop(L1, 1); // L1: ... f ... |
1020 | lua_getglobal(L2, "decoda_name"); // {} f decoda_name | 924 | lua_getglobal(L2, "decoda_name"); // L1: ... f ... L2: {} f decoda_name |
1021 | to = lua_tostring(L2, -1); | 925 | to = lua_tostring(L2, -1); |
1022 | lua_pop(L2, 1); // {} f | 926 | lua_pop(L2, 1); // L2: {} f |
1023 | // 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 | 927 | // 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 |
1024 | raise_luaL_error( | 928 | raise_luaL_error( |
1025 | (mode == LookupMode::FromKeeper) ? L2 : L1 | 929 | (mode == LookupMode::FromKeeper) ? L2 : L1 |
@@ -1031,38 +935,31 @@ void InterCopyContext::lookup_native_func() const | |||
1031 | ); | 935 | ); |
1032 | return; | 936 | return; |
1033 | } | 937 | } |
1034 | lua_remove(L2, -2); // f | 938 | lua_remove(L2, -2); // L2: f |
1035 | break; | 939 | break; |
1036 | 940 | ||
1037 | /* keep it in case I need it someday, who knows... | 941 | /* keep it in case I need it someday, who knows... |
1038 | case LookupMode::RawFunctions: | 942 | case LookupMode::RawFunctions: |
1039 | { | 943 | { |
1040 | int n; | 944 | int n; |
1041 | char const* upname; | 945 | char const* upname; |
1042 | lua_CFunction f = lua_tocfunction( L, i); | 946 | lua_CFunction f = lua_tocfunction( L, i); |
1043 | // copy upvalues | 947 | // copy upvalues |
1044 | for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) | 948 | for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) { |
1045 | { | 949 | luaG_inter_move( U, L, L2, 1, mode_); // L2: [up[,up ...]] |
1046 | luaG_inter_move( U, L, L2, 1, mode_); // [up[,up ...]] | ||
1047 | } | ||
1048 | lua_pushcclosure( L2, f, n); // | ||
1049 | } | 950 | } |
1050 | break; | 951 | lua_pushcclosure( L2, f, n); // L2: |
1051 | */ | ||
1052 | } | 952 | } |
1053 | STACK_CHECK( L2, 1); | 953 | break; |
954 | */ | ||
955 | } | ||
956 | STACK_CHECK(L2, 1); | ||
1054 | } | 957 | } |
1055 | 958 | ||
1056 | // ################################################################################################# | 959 | // ################################################################################################# |
1057 | 960 | ||
1058 | /* | ||
1059 | * Copy a function over, which has not been found in the cache. | ||
1060 | * L2 has the cache key for this function at the top of the stack | ||
1061 | */ | ||
1062 | |||
1063 | #if USE_DEBUG_SPEW() | 961 | #if USE_DEBUG_SPEW() |
1064 | static char const* lua_type_names[] = | 962 | static char const* lua_type_names[] = { |
1065 | { | ||
1066 | "LUA_TNIL" | 963 | "LUA_TNIL" |
1067 | , "LUA_TBOOLEAN" | 964 | , "LUA_TBOOLEAN" |
1068 | , "LUA_TLIGHTUSERDATA" | 965 | , "LUA_TLIGHTUSERDATA" |
@@ -1075,9 +972,8 @@ static char const* lua_type_names[] = | |||
1075 | , "<LUA_NUMTAGS>" // not really a type | 972 | , "<LUA_NUMTAGS>" // not really a type |
1076 | , "LUA_TJITCDATA" // LuaJIT specific | 973 | , "LUA_TJITCDATA" // LuaJIT specific |
1077 | }; | 974 | }; |
1078 | static char const* vt_names[] = | 975 | static char const* vt_names[] = { |
1079 | { | 976 | "VT::NORMAL" |
1080 | "VT::NORMAL" | ||
1081 | , "VT::KEY" | 977 | , "VT::KEY" |
1082 | , "VT::METATABLE" | 978 | , "VT::METATABLE" |
1083 | }; | 979 | }; |
@@ -1092,82 +988,78 @@ static char const* vt_names[] = | |||
1092 | [[nodiscard]] static int buf_writer(lua_State* L_, void const* b, size_t size, void* ud) | 988 | [[nodiscard]] static int buf_writer(lua_State* L_, void const* b, size_t size, void* ud) |
1093 | { | 989 | { |
1094 | luaL_Buffer* B = (luaL_Buffer*) ud; | 990 | luaL_Buffer* B = (luaL_Buffer*) ud; |
1095 | if (!B->L) | 991 | if (!B->L) { |
1096 | { | 992 | luaL_buffinit(L_, B); |
1097 | luaL_buffinit( L_, B); | ||
1098 | } | 993 | } |
1099 | luaL_addlstring( B, (char const*) b, size); | 994 | luaL_addlstring(B, (char const*) b, size); |
1100 | return 0; | 995 | return 0; |
1101 | } | 996 | } |
1102 | 997 | ||
1103 | // ################################################################################################# | 998 | // ################################################################################################# |
1104 | 999 | ||
1000 | // Copy a function over, which has not been found in the cache. | ||
1001 | // L2 has the cache key for this function at the top of the stack | ||
1105 | void InterCopyContext::copy_func() const | 1002 | void InterCopyContext::copy_func() const |
1106 | { | 1003 | { |
1107 | LUA_ASSERT(L1, L2_cache_i != 0); // ... {cache} ... p | 1004 | LUA_ASSERT(L1, L2_cache_i != 0); // L2: ... {cache} ... p |
1108 | STACK_GROW(L1, 2); | 1005 | STACK_GROW(L1, 2); |
1109 | STACK_CHECK_START_REL(L1, 0); | 1006 | STACK_CHECK_START_REL(L1, 0); |
1110 | 1007 | ||
1111 | |||
1112 | // 'lua_dump()' needs the function at top of stack | 1008 | // 'lua_dump()' needs the function at top of stack |
1113 | // if already on top of the stack, no need to push again | 1009 | // if already on top of the stack, no need to push again |
1114 | bool const needToPush{ L1_i != lua_gettop(L1) }; | 1010 | bool const needToPush{ L1_i != lua_gettop(L1) }; |
1115 | if (needToPush) | 1011 | if (needToPush) { |
1116 | { | 1012 | lua_pushvalue(L1, L1_i); // L1: ... f |
1117 | lua_pushvalue(L1, L1_i); // ... f | ||
1118 | } | 1013 | } |
1119 | 1014 | ||
1120 | // | 1015 | // |
1121 | // "value returned is the error code returned by the last call | 1016 | // "value returned is the error code returned by the last call |
1122 | // to the writer" (and we only return 0) | 1017 | // to the writer" (and we only return 0) |
1123 | // not sure this could ever fail but for memory shortage reasons | 1018 | // not sure this could ever fail but for memory shortage reasons |
1124 | // last parameter is Lua 5.4-specific (no stripping) | 1019 | // last parameter is Lua 5.4-specific (no stripping) |
1125 | luaL_Buffer B; | 1020 | luaL_Buffer B; |
1126 | B.L = nullptr; | 1021 | B.L = nullptr; |
1127 | if (lua504_dump(L1, buf_writer, &B, 0) != 0) | 1022 | if (lua504_dump(L1, buf_writer, &B, 0) != 0) { |
1128 | { | ||
1129 | raise_luaL_error(L1, "internal error: function dump failed."); | 1023 | raise_luaL_error(L1, "internal error: function dump failed."); |
1130 | } | 1024 | } |
1131 | 1025 | ||
1132 | // pushes dumped string on 'L1' | 1026 | // pushes dumped string on 'L1' |
1133 | luaL_pushresult(&B); // ... f b | 1027 | luaL_pushresult(&B); // L1: ... f b |
1134 | 1028 | ||
1135 | // if not pushed, no need to pop | 1029 | // if not pushed, no need to pop |
1136 | if (needToPush) | 1030 | if (needToPush) { |
1137 | { | 1031 | lua_remove(L1, -2); // L1: ... b |
1138 | lua_remove(L1, -2); // ... b | ||
1139 | } | 1032 | } |
1140 | 1033 | ||
1141 | // transfer the bytecode, then the upvalues, to create a similar closure | 1034 | // transfer the bytecode, then the upvalues, to create a similar closure |
1142 | { | 1035 | { |
1143 | char const* name = nullptr; | 1036 | char const* name = nullptr; |
1144 | 1037 | #define LOG_FUNC_INFO 0 | |
1145 | #if LOG_FUNC_INFO | 1038 | #if LOG_FUNC_INFO |
1146 | // "To get information about a function you push it onto the | 1039 | // "To get information about a function you push it onto the |
1147 | // stack and start the what string with the character '>'." | 1040 | // stack and start the what string with the character '>'." |
1148 | // | 1041 | // |
1149 | { | 1042 | { |
1150 | lua_Debug ar; | 1043 | lua_Debug ar; |
1151 | lua_pushvalue( L, i); // ... b f | 1044 | lua_pushvalue(L1, L1_i); // L1: ... b f |
1152 | // fills 'name' 'namewhat' and 'linedefined', pops function | 1045 | // fills 'name' 'namewhat' and 'linedefined', pops function |
1153 | lua_getinfo( L, ">nS", &ar); // ... b | 1046 | lua_getinfo(L1, ">nS", &ar); // L1: ... b |
1154 | name = ar.namewhat; | 1047 | name = ar.namewhat; |
1155 | fprintf( stderr, INDENT_BEGIN "FNAME: %s @ %d\n", i, s_indent, ar.short_src, ar.linedefined); // just gives nullptr | 1048 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "FNAME: %s @ %d" INDENT_END(U), ar.short_src, ar.linedefined)); // just gives nullptr |
1156 | } | 1049 | } |
1157 | #endif // LOG_FUNC_INFO | 1050 | #endif // LOG_FUNC_INFO |
1158 | { | 1051 | { |
1159 | size_t sz; | 1052 | size_t sz; |
1160 | char const* s = lua_tolstring(L1, -1, &sz); // ... b | 1053 | char const* s = lua_tolstring(L1, -1, &sz); // L1: ... b |
1161 | LUA_ASSERT(L1, s && sz); | 1054 | LUA_ASSERT(L1, s && sz); |
1162 | STACK_GROW(L2, 2); | 1055 | STACK_GROW(L2, 2); |
1163 | // Note: Line numbers seem to be taken precisely from the | 1056 | // Note: Line numbers seem to be taken precisely from the |
1164 | // original function. 'name' is not used since the chunk | 1057 | // original function. 'name' is not used since the chunk |
1165 | // is precompiled (it seems...). | 1058 | // is precompiled (it seems...). |
1166 | // | 1059 | // |
1167 | // TBD: Can we get the function's original name through, as well? | 1060 | // TBD: Can we get the function's original name through, as well? |
1168 | // | 1061 | // |
1169 | if (luaL_loadbuffer(L2, s, sz, name) != 0) // ... {cache} ... p function | 1062 | if (luaL_loadbuffer(L2, s, sz, name) != 0) { // L2: ... {cache} ... p function |
1170 | { | ||
1171 | // chunk is precompiled so only LUA_ERRMEM can happen | 1063 | // chunk is precompiled so only LUA_ERRMEM can happen |
1172 | // "Otherwise, it pushes an error message" | 1064 | // "Otherwise, it pushes an error message" |
1173 | // | 1065 | // |
@@ -1175,22 +1067,22 @@ void InterCopyContext::copy_func() const | |||
1175 | raise_luaL_error(L1, "%s: %s", name, lua_tostring(L2, -1)); | 1067 | raise_luaL_error(L1, "%s: %s", name, lua_tostring(L2, -1)); |
1176 | } | 1068 | } |
1177 | // remove the dumped string | 1069 | // remove the dumped string |
1178 | lua_pop(L1, 1); // ... | 1070 | lua_pop(L1, 1); // ... |
1179 | // now set the cache as soon as we can. | 1071 | // now set the cache as soon as we can. |
1180 | // this is necessary if one of the function's upvalues references it indirectly | 1072 | // this is necessary if one of the function's upvalues references it indirectly |
1181 | // we need to find it in the cache even if it isn't fully transfered yet | 1073 | // we need to find it in the cache even if it isn't fully transfered yet |
1182 | lua_insert(L2, -2); // ... {cache} ... function p | 1074 | lua_insert(L2, -2); // L2: ... {cache} ... function p |
1183 | lua_pushvalue(L2, -2); // ... {cache} ... function p function | 1075 | lua_pushvalue(L2, -2); // L2: ... {cache} ... function p function |
1184 | // cache[p] = function | 1076 | // cache[p] = function |
1185 | lua_rawset(L2, L2_cache_i); // ... {cache} ... function | 1077 | lua_rawset(L2, L2_cache_i); // L2: ... {cache} ... function |
1186 | } | 1078 | } |
1187 | STACK_CHECK(L1, 0); | 1079 | STACK_CHECK(L1, 0); |
1188 | 1080 | ||
1189 | /* push over any upvalues; references to this function will come from | 1081 | /* push over any upvalues; references to this function will come from |
1190 | * cache so we don't end up in eternal loop. | 1082 | * cache so we don't end up in eternal loop. |
1191 | * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy! | 1083 | * Lua5.2 and Lua5.3: one of the upvalues is _ENV, which we don't want to copy! |
1192 | * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! | 1084 | * instead, the function shall have LUA_RIDX_GLOBALS taken in the destination state! |
1193 | */ | 1085 | */ |
1194 | int n{ 0 }; | 1086 | int n{ 0 }; |
1195 | { | 1087 | { |
1196 | InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} }; | 1088 | InterCopyContext c{ U, L2, L1, L2_cache_i, {}, VT::NORMAL, mode, {} }; |
@@ -1198,50 +1090,45 @@ void InterCopyContext::copy_func() const | |||
1198 | // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) | 1090 | // Starting with Lua 5.2, each Lua function gets its environment as one of its upvalues (named LUA_ENV, aka "_ENV" by default) |
1199 | // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... | 1091 | // Generally this is LUA_RIDX_GLOBALS, which we don't want to copy from the source to the destination state... |
1200 | // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table | 1092 | // -> if we encounter an upvalue equal to the global table in the source, bind it to the destination's global table |
1201 | lua_pushglobaltable(L1); // ... _G | 1093 | lua_pushglobaltable(L1); // L1: ... _G |
1202 | #endif // LUA_VERSION_NUM | 1094 | #endif // LUA_VERSION_NUM |
1203 | for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) | 1095 | for (n = 0; (c.name = lua_getupvalue(L1, L1_i, 1 + n)) != nullptr; ++n) { // L1: ... _G up[n] |
1204 | { // ... _G up[n] | 1096 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END(U), n, c.name)); |
1205 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END, n, c.name)); | ||
1206 | #if LUA_VERSION_NUM >= 502 | 1097 | #if LUA_VERSION_NUM >= 502 |
1207 | if (lua_rawequal(L1, -1, -2)) // is the upvalue equal to the global table? | 1098 | if (lua_rawequal(L1, -1, -2)) { // is the upvalue equal to the global table? |
1208 | { | ||
1209 | DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n")); | 1099 | DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n")); |
1210 | lua_pushglobaltable(L2); // ... {cache} ... function <upvalues> | 1100 | lua_pushglobaltable(L2); // L2: ... {cache} ... function <upvalues> |
1211 | } | 1101 | } else |
1212 | else | ||
1213 | #endif // LUA_VERSION_NUM | 1102 | #endif // LUA_VERSION_NUM |
1214 | { | 1103 | { |
1215 | DEBUGSPEW_CODE(fprintf(stderr, "copying value\n")); | 1104 | DEBUGSPEW_CODE(fprintf(stderr, "copying value\n")); |
1216 | c.L1_i = SourceIndex{ lua_gettop(L1) }; | 1105 | c.L1_i = SourceIndex{ lua_gettop(L1) }; |
1217 | if (!c.inter_copy_one()) // ... {cache} ... function <upvalues> | 1106 | if (!c.inter_copy_one()) { // L2: ... {cache} ... function <upvalues> |
1218 | { | ||
1219 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); | 1107 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); |
1220 | } | 1108 | } |
1221 | } | 1109 | } |
1222 | lua_pop(L1, 1); // ... _G | 1110 | lua_pop(L1, 1); // L1: ... _G |
1223 | } | 1111 | } |
1224 | #if LUA_VERSION_NUM >= 502 | 1112 | #if LUA_VERSION_NUM >= 502 |
1225 | lua_pop(L1, 1); // ... | 1113 | lua_pop(L1, 1); // L1: ... |
1226 | #endif // LUA_VERSION_NUM | 1114 | #endif // LUA_VERSION_NUM |
1227 | } | 1115 | } |
1228 | // L2: function + 'n' upvalues (>=0) | 1116 | // L2: ... {cache} ... function + 'n' upvalues (>=0) |
1229 | 1117 | ||
1230 | STACK_CHECK(L1, 0); | 1118 | STACK_CHECK(L1, 0); |
1231 | 1119 | ||
1232 | // Set upvalues (originally set to 'nil' by 'lua_load') | 1120 | // Set upvalues (originally set to 'nil' by 'lua_load') |
1233 | { | 1121 | { |
1234 | for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) | 1122 | for (int const func_index{ lua_gettop(L2) - n }; n > 0; --n) { |
1235 | { | 1123 | char const* rc{ lua_setupvalue(L2, func_index, n) }; // L2: ... {cache} ... function |
1236 | char const* rc{ lua_setupvalue(L2, func_index, n) }; // ... {cache} ... function | ||
1237 | // | 1124 | // |
1238 | // "assigns the value at the top of the stack to the upvalue and returns its name. | 1125 | // "assigns the value at the top of the stack to the upvalue and returns its name. |
1239 | // It also pops the value from the stack." | 1126 | // It also pops the value from the stack." |
1240 | 1127 | ||
1241 | LUA_ASSERT(L1, rc); // not having enough slots? | 1128 | LUA_ASSERT(L1, rc); // not having enough slots? |
1242 | } | 1129 | } |
1243 | // once all upvalues have been set we are left | 1130 | // once all upvalues have been set we are left |
1244 | // with the function at the top of the stack // ... {cache} ... function | 1131 | // with the function at the top of the stack // L2: ... {cache} ... function |
1245 | } | 1132 | } |
1246 | } | 1133 | } |
1247 | STACK_CHECK(L1, 0); | 1134 | STACK_CHECK(L1, 0); |
@@ -1249,16 +1136,12 @@ void InterCopyContext::copy_func() const | |||
1249 | 1136 | ||
1250 | // ################################################################################################# | 1137 | // ################################################################################################# |
1251 | 1138 | ||
1252 | /* | 1139 | // Check if we've already copied the same function from 'L1', and reuse the old copy. |
1253 | * Check if we've already copied the same function from 'L1', and reuse the old copy. | 1140 | // Always pushes a function to 'L2'. |
1254 | * | ||
1255 | * Always pushes a function to 'L2'. | ||
1256 | */ | ||
1257 | void InterCopyContext::copy_cached_func() const | 1141 | void InterCopyContext::copy_cached_func() const |
1258 | { | 1142 | { |
1259 | FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) }; | 1143 | FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) }; |
1260 | if (funcSubType == FuncSubType::Bytecode) | 1144 | if (funcSubType == FuncSubType::Bytecode) { |
1261 | { | ||
1262 | void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i)); | 1145 | void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i)); |
1263 | // TBD: Merge this and same code for tables | 1146 | // TBD: Merge this and same code for tables |
1264 | LUA_ASSERT(L1, L2_cache_i != 0); | 1147 | LUA_ASSERT(L1, L2_cache_i != 0); |
@@ -1274,33 +1157,28 @@ void InterCopyContext::copy_cached_func() const | |||
1274 | // | 1157 | // |
1275 | 1158 | ||
1276 | // push a light userdata uniquely representing the function | 1159 | // push a light userdata uniquely representing the function |
1277 | lua_pushlightuserdata(L2, aspointer); // ... {cache} ... p | 1160 | lua_pushlightuserdata(L2, aspointer); // L2: ... {cache} ... p |
1278 | 1161 | ||
1279 | //fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); | 1162 | // fprintf( stderr, "<< ID: %s >>\n", lua_tostring( L2, -1)); |
1280 | 1163 | ||
1281 | lua_pushvalue(L2, -1); // ... {cache} ... p p | 1164 | lua_pushvalue(L2, -1); // L2: ... {cache} ... p p |
1282 | lua_rawget(L2, L2_cache_i); // ... {cache} ... p function|nil|true | 1165 | lua_rawget(L2, L2_cache_i); // L2: ... {cache} ... p function|nil|true |
1283 | 1166 | ||
1284 | if (lua_isnil(L2, -1)) // function is unknown | 1167 | if (lua_isnil(L2, -1)) { // function is unknown |
1285 | { | 1168 | lua_pop(L2, 1); // L2: ... {cache} ... p |
1286 | lua_pop(L2, 1); // ... {cache} ... p | ||
1287 | 1169 | ||
1288 | // Set to 'true' for the duration of creation; need to find self-references | 1170 | // Set to 'true' for the duration of creation; need to find self-references |
1289 | // via upvalues | 1171 | // via upvalues |
1290 | // | 1172 | // |
1291 | // pushes a copy of the func, stores a reference in the cache | 1173 | // pushes a copy of the func, stores a reference in the cache |
1292 | copy_func(); // ... {cache} ... function | 1174 | copy_func(); // L2: ... {cache} ... function |
1293 | } | 1175 | } else { // found function in the cache |
1294 | else // found function in the cache | 1176 | lua_remove(L2, -2); // L2: ... {cache} ... function |
1295 | { | ||
1296 | lua_remove(L2, -2); // ... {cache} ... function | ||
1297 | } | 1177 | } |
1298 | STACK_CHECK(L2, 1); | 1178 | STACK_CHECK(L2, 1); |
1299 | LUA_ASSERT(L1, lua_isfunction(L2, -1)); | 1179 | LUA_ASSERT(L1, lua_isfunction(L2, -1)); |
1300 | } | 1180 | } else { // function is native/LuaJIT: no need to cache |
1301 | else // function is native/LuaJIT: no need to cache | 1181 | lookup_native_func(); // L2: ... {cache} ... function |
1302 | { | ||
1303 | lookup_native_func(); // ... {cache} ... function | ||
1304 | // if the function was in fact a lookup sentinel, we can either get a function or a table here | 1182 | // if the function was in fact a lookup sentinel, we can either get a function or a table here |
1305 | LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1)); | 1183 | LUA_ASSERT(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1)); |
1306 | } | 1184 | } |
@@ -1311,8 +1189,7 @@ void InterCopyContext::copy_cached_func() const | |||
1311 | [[nodiscard]] bool InterCopyContext::push_cached_metatable() const | 1189 | [[nodiscard]] bool InterCopyContext::push_cached_metatable() const |
1312 | { | 1190 | { |
1313 | STACK_CHECK_START_REL(L1, 0); | 1191 | STACK_CHECK_START_REL(L1, 0); |
1314 | if (!lua_getmetatable(L1, L1_i)) // ... mt | 1192 | if (!lua_getmetatable(L1, L1_i)) { // L1: ... mt |
1315 | { | ||
1316 | STACK_CHECK(L1, 0); | 1193 | STACK_CHECK(L1, 0); |
1317 | return false; | 1194 | return false; |
1318 | } | 1195 | } |
@@ -1323,35 +1200,33 @@ void InterCopyContext::copy_cached_func() const | |||
1323 | STACK_CHECK_START_REL(L2, 0); | 1200 | STACK_CHECK_START_REL(L2, 0); |
1324 | STACK_GROW(L2, 4); | 1201 | STACK_GROW(L2, 4); |
1325 | // do we already know this metatable? | 1202 | // do we already know this metatable? |
1326 | push_registry_subtable(L2, kMtIdRegKey); // _R[kMtIdRegKey] | 1203 | push_registry_subtable(L2, kMtIdRegKey); // L2: _R[kMtIdRegKey] |
1327 | lua_pushinteger(L2, mt_id); // _R[kMtIdRegKey] id | 1204 | lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] id |
1328 | lua_rawget(L2, -2); // _R[kMtIdRegKey] mt|nil | 1205 | lua_rawget(L2, -2); // L2: _R[kMtIdRegKey] mt|nil |
1329 | STACK_CHECK(L2, 2); | 1206 | STACK_CHECK(L2, 2); |
1330 | 1207 | ||
1331 | if (lua_isnil(L2, -1)) | 1208 | if (lua_isnil(L2, -1)) { // L2 did not know the metatable |
1332 | { // L2 did not know the metatable | 1209 | lua_pop(L2, 1); // L2: _R[kMtIdRegKey] |
1333 | lua_pop(L2, 1); // _R[kMtIdRegKey] | ||
1334 | InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name }; | 1210 | InterCopyContext const c{ U, L2, L1, L2_cache_i, SourceIndex{ lua_gettop(L1) }, VT::METATABLE, mode, name }; |
1335 | if (!c.inter_copy_one()) // _R[kMtIdRegKey] mt? | 1211 | if (!c.inter_copy_one()) { // L2: _R[kMtIdRegKey] mt? |
1336 | { | ||
1337 | raise_luaL_error(L1, "Error copying a metatable"); | 1212 | raise_luaL_error(L1, "Error copying a metatable"); |
1338 | } | 1213 | } |
1339 | 1214 | ||
1340 | STACK_CHECK(L2, 2); // _R[kMtIdRegKey] mt | 1215 | STACK_CHECK(L2, 2); // L2: _R[kMtIdRegKey] mt |
1341 | // mt_id -> metatable | 1216 | // mt_id -> metatable |
1342 | lua_pushinteger(L2, mt_id); // _R[kMtIdRegKey] mt id | 1217 | lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt id |
1343 | lua_pushvalue(L2, -2); // _R[kMtIdRegKey] mt id mt | 1218 | lua_pushvalue(L2, -2); // L2: _R[kMtIdRegKey] mt id mt |
1344 | lua_rawset(L2, -4); // _R[kMtIdRegKey] mt | 1219 | lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt |
1345 | 1220 | ||
1346 | // metatable -> mt_id | 1221 | // metatable -> mt_id |
1347 | lua_pushvalue(L2, -1); // _R[kMtIdRegKey] mt mt | 1222 | lua_pushvalue(L2, -1); // L2: _R[kMtIdRegKey] mt mt |
1348 | lua_pushinteger(L2, mt_id); // _R[kMtIdRegKey] mt mt id | 1223 | lua_pushinteger(L2, mt_id); // L2: _R[kMtIdRegKey] mt mt id |
1349 | lua_rawset(L2, -4); // _R[kMtIdRegKey] mt | 1224 | lua_rawset(L2, -4); // L2: _R[kMtIdRegKey] mt |
1350 | STACK_CHECK(L2, 2); | 1225 | STACK_CHECK(L2, 2); |
1351 | } | 1226 | } |
1352 | lua_remove(L2, -2); // mt | 1227 | lua_remove(L2, -2); // L2: mt |
1353 | 1228 | ||
1354 | lua_pop(L1, 1); // ... | 1229 | lua_pop(L1, 1); // L1: ... |
1355 | STACK_CHECK(L2, 1); | 1230 | STACK_CHECK(L2, 1); |
1356 | STACK_CHECK(L1, 0); | 1231 | STACK_CHECK(L1, 0); |
1357 | return true; | 1232 | return true; |
@@ -1366,49 +1241,40 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1366 | 1241 | ||
1367 | // For the key, only basic key types are copied over. others ignored | 1242 | // For the key, only basic key types are copied over. others ignored |
1368 | InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name }; | 1243 | InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name }; |
1369 | if (!c.inter_copy_one()) | 1244 | if (!c.inter_copy_one()) { |
1370 | { | ||
1371 | return; | 1245 | return; |
1372 | // we could raise an error instead of ignoring the table entry, like so: | 1246 | // we could raise an error instead of ignoring the table entry, like so: |
1373 | //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)); | 1247 | // 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)); |
1374 | // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer? | 1248 | // maybe offer this possibility as a global configuration option, or a linda setting, or as a parameter of the call causing the transfer? |
1375 | } | 1249 | } |
1376 | 1250 | ||
1377 | char* valPath{ nullptr }; | 1251 | char* valPath{ nullptr }; |
1378 | if (U->verboseErrors) | 1252 | if (U->verboseErrors) { |
1379 | { | ||
1380 | // for debug purposes, let's try to build a useful name | 1253 | // for debug purposes, let's try to build a useful name |
1381 | if (lua_type(L1, key_i) == LUA_TSTRING) | 1254 | if (lua_type(L1, key_i) == LUA_TSTRING) { |
1382 | { | ||
1383 | char const* key{ lua_tostring(L1, key_i) }; | 1255 | char const* key{ lua_tostring(L1, key_i) }; |
1384 | size_t const keyRawLen = lua_rawlen(L1, key_i); | 1256 | size_t const keyRawLen = lua_rawlen(L1, key_i); |
1385 | size_t const bufLen = strlen(name) + keyRawLen + 2; | 1257 | size_t const bufLen = strlen(name) + keyRawLen + 2; |
1386 | valPath = (char*) alloca( bufLen); | 1258 | valPath = (char*) alloca(bufLen); |
1387 | sprintf( valPath, "%s.%*s", name, (int) keyRawLen, key); | 1259 | sprintf(valPath, "%s.%*s", name, (int) keyRawLen, key); |
1388 | key = nullptr; | 1260 | key = nullptr; |
1389 | } | 1261 | } |
1390 | #if defined LUA_LNUM || LUA_VERSION_NUM >= 503 | 1262 | #if defined LUA_LNUM || LUA_VERSION_NUM >= 503 |
1391 | else if (lua_isinteger(L1, key_i)) | 1263 | else if (lua_isinteger(L1, key_i)) { |
1392 | { | ||
1393 | lua_Integer const key{ lua_tointeger(L1, key_i) }; | 1264 | lua_Integer const key{ lua_tointeger(L1, key_i) }; |
1394 | valPath = (char*) alloca(strlen(name) + 32 + 3); | 1265 | valPath = (char*) alloca(strlen(name) + 32 + 3); |
1395 | sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key); | 1266 | sprintf(valPath, "%s[" LUA_INTEGER_FMT "]", name, key); |
1396 | } | 1267 | } |
1397 | #endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 | 1268 | #endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 |
1398 | else if (lua_type(L1, key_i) == LUA_TNUMBER) | 1269 | else if (lua_type(L1, key_i) == LUA_TNUMBER) { |
1399 | { | ||
1400 | lua_Number const key{ lua_tonumber(L1, key_i) }; | 1270 | lua_Number const key{ lua_tonumber(L1, key_i) }; |
1401 | valPath = (char*) alloca(strlen(name) + 32 + 3); | 1271 | valPath = (char*) alloca(strlen(name) + 32 + 3); |
1402 | sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key); | 1272 | sprintf(valPath, "%s[" LUA_NUMBER_FMT "]", name, key); |
1403 | } | 1273 | } else if (lua_type(L1, key_i) == LUA_TLIGHTUSERDATA) { |
1404 | else if (lua_type( L1, key_i) == LUA_TLIGHTUSERDATA) | ||
1405 | { | ||
1406 | void* const key{ lua_touserdata(L1, key_i) }; | 1274 | void* const key{ lua_touserdata(L1, key_i) }; |
1407 | valPath = (char*) alloca(strlen(name) + 16 + 5); | 1275 | valPath = (char*) alloca(strlen(name) + 16 + 5); |
1408 | sprintf(valPath, "%s[U:%p]", name, key); | 1276 | sprintf(valPath, "%s[U:%p]", name, key); |
1409 | } | 1277 | } else if (lua_type(L1, key_i) == LUA_TBOOLEAN) { |
1410 | else if (lua_type( L1, key_i) == LUA_TBOOLEAN) | ||
1411 | { | ||
1412 | int const key{ lua_toboolean(L1, key_i) }; | 1278 | int const key{ lua_toboolean(L1, key_i) }; |
1413 | valPath = (char*) alloca(strlen(name) + 8); | 1279 | valPath = (char*) alloca(strlen(name) + 8); |
1414 | sprintf(valPath, "%s[%s]", name, key ? "true" : "false"); | 1280 | sprintf(valPath, "%s[%s]", name, key ? "true" : "false"); |
@@ -1418,132 +1284,114 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1418 | // Contents of metatables are copied with cache checking. important to detect loops. | 1284 | // Contents of metatables are copied with cache checking. important to detect loops. |
1419 | c.vt = VT::NORMAL; | 1285 | c.vt = VT::NORMAL; |
1420 | c.name = valPath ? valPath : name; | 1286 | c.name = valPath ? valPath : name; |
1421 | if (c.inter_copy_one()) | 1287 | if (c.inter_copy_one()) { |
1422 | { | 1288 | LUA_ASSERT(L1, lua_istable(L2, -3)); |
1423 | LUA_ASSERT(L1, lua_istable( L2, -3)); | 1289 | lua_rawset(L2, -3); // add to table (pops key & val) |
1424 | lua_rawset(L2, -3); // add to table (pops key & val) | 1290 | } else { |
1425 | } | ||
1426 | else | ||
1427 | { | ||
1428 | raise_luaL_error(L1, "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i)); | 1291 | raise_luaL_error(L1, "Unable to copy %s entry '%s' because of value is of type '%s'", (vt == VT::NORMAL) ? "table" : "metatable", valPath, luaL_typename(L1, val_i)); |
1429 | } | 1292 | } |
1430 | } | 1293 | } |
1431 | 1294 | ||
1432 | // ################################################################################################# | 1295 | // ################################################################################################# |
1433 | 1296 | ||
1434 | [[nodiscard]] bool InterCopyContext::copyclone() const | 1297 | [[nodiscard]] bool InterCopyContext::copyClonable() const |
1435 | { | 1298 | { |
1436 | SourceIndex const L1_i{ lua_absindex(L1, this->L1_i) }; | 1299 | SourceIndex const L1_i{ lua_absindex(L1, this->L1_i) }; |
1437 | void* const source{ lua_touserdata(L1, L1_i) }; | 1300 | void* const source{ lua_touserdata(L1, L1_i) }; |
1438 | 1301 | ||
1439 | STACK_CHECK_START_REL(L1, 0); // L1 (source) // L2 (destination) | 1302 | STACK_CHECK_START_REL(L1, 0); |
1440 | STACK_CHECK_START_REL(L2, 0); | 1303 | STACK_CHECK_START_REL(L2, 0); |
1441 | 1304 | ||
1442 | // Check if the source was already cloned during this copy | 1305 | // Check if the source was already cloned during this copy |
1443 | lua_pushlightuserdata(L2, source); // ... source | 1306 | lua_pushlightuserdata(L2, source); // L2: ... source |
1444 | lua_rawget(L2, L2_cache_i); // ... clone? | 1307 | lua_rawget(L2, L2_cache_i); // L2: ... clone? |
1445 | if (!lua_isnil(L2, -1)) | 1308 | if (!lua_isnil(L2, -1)) { |
1446 | { | ||
1447 | STACK_CHECK(L2, 1); | 1309 | STACK_CHECK(L2, 1); |
1448 | return true; | 1310 | return true; |
1449 | } | 1311 | } else { |
1450 | else | 1312 | lua_pop(L2, 1); // L2: ... |
1451 | { | ||
1452 | lua_pop(L2, 1); // ... | ||
1453 | } | 1313 | } |
1454 | STACK_CHECK(L2, 0); | 1314 | STACK_CHECK(L2, 0); |
1455 | 1315 | ||
1456 | // no metatable? -> not clonable | 1316 | // no metatable? -> not clonable |
1457 | if (!lua_getmetatable(L1, L1_i)) // ... mt? | 1317 | if (!lua_getmetatable(L1, L1_i)) { // L1: ... mt? |
1458 | { | ||
1459 | STACK_CHECK(L1, 0); | 1318 | STACK_CHECK(L1, 0); |
1460 | return false; | 1319 | return false; |
1461 | } | 1320 | } |
1462 | 1321 | ||
1463 | // no __lanesclone? -> not clonable | 1322 | // no __lanesclone? -> not clonable |
1464 | lua_getfield(L1, -1, "__lanesclone"); // ... mt __lanesclone? | 1323 | lua_getfield(L1, -1, "__lanesclone"); // L1: ... mt __lanesclone? |
1465 | if (lua_isnil(L1, -1)) | 1324 | if (lua_isnil(L1, -1)) { |
1466 | { | 1325 | lua_pop(L1, 2); // L1: ... |
1467 | lua_pop(L1, 2); // ... | ||
1468 | STACK_CHECK(L1, 0); | 1326 | STACK_CHECK(L1, 0); |
1469 | return false; | 1327 | return false; |
1470 | } | 1328 | } |
1471 | 1329 | ||
1472 | // we need to copy over the uservalues of the userdata as well | 1330 | // we need to copy over the uservalues of the userdata as well |
1473 | { | 1331 | { |
1474 | int const mt{ lua_absindex(L1, -2) }; // ... mt __lanesclone | 1332 | int const mt{ lua_absindex(L1, -2) }; // L1: ... mt __lanesclone |
1475 | size_t const userdata_size{ lua_rawlen(L1, L1_i) }; | 1333 | size_t const userdata_size{ lua_rawlen(L1, L1_i) }; |
1476 | // extract all the uservalues, but don't transfer them yet | 1334 | // extract all the uservalues, but don't transfer them yet |
1477 | int uvi = 0; | 1335 | int uvi = 0; |
1478 | while (lua_getiuservalue( L1, L1_i, ++ uvi) != LUA_TNONE) {} // ... mt __lanesclone [uv]+ nil | 1336 | while (lua_getiuservalue(L1, L1_i, ++uvi) != LUA_TNONE) {} // L1: ... mt __lanesclone [uv]+ nil |
1479 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 1337 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now |
1480 | lua_pop(L1, 1); // ... mt __lanesclone [uv]+ | 1338 | lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]+ |
1481 | -- uvi; | 1339 | --uvi; |
1482 | // create the clone userdata with the required number of uservalue slots | 1340 | // create the clone userdata with the required number of uservalue slots |
1483 | void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // ... u | 1341 | void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // L2: ... u |
1484 | // copy the metatable in the target state, and give it to the clone we put there | 1342 | // copy the metatable in the target state, and give it to the clone we put there |
1485 | InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name }; | 1343 | InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name }; |
1486 | if (c.inter_copy_one()) // ... u mt|sentinel | 1344 | if (c.inter_copy_one()) { // L2: ... u mt|sentinel |
1487 | { | 1345 | if (LookupMode::ToKeeper == mode) { // L2: ... u sentinel |
1488 | if (LookupMode::ToKeeper == mode) // ... u sentinel | ||
1489 | { | ||
1490 | LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel); | 1346 | LUA_ASSERT(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel); |
1491 | // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn | 1347 | // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn |
1492 | lua_getupvalue(L2, -1, 1); // ... u sentinel fqn | 1348 | lua_getupvalue(L2, -1, 1); // L2: ... u sentinel fqn |
1493 | lua_remove(L2, -2); // ... u fqn | 1349 | lua_remove(L2, -2); // L2: ... u fqn |
1494 | lua_insert(L2, -2); // ... fqn u | 1350 | lua_insert(L2, -2); // L2: ... fqn u |
1495 | lua_pushcclosure(L2, userdata_clone_sentinel, 2); // ... userdata_clone_sentinel | 1351 | lua_pushcclosure(L2, userdata_clone_sentinel, 2); // L2: ... userdata_clone_sentinel |
1496 | } | 1352 | } else { // from keeper or direct // L2: ... u mt |
1497 | else // from keeper or direct // ... u mt | ||
1498 | { | ||
1499 | LUA_ASSERT(L1, lua_istable(L2, -1)); | 1353 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
1500 | lua_setmetatable(L2, -2); // ... u | 1354 | lua_setmetatable(L2, -2); // L2: ... u |
1501 | } | 1355 | } |
1502 | STACK_CHECK(L2, 1); | 1356 | STACK_CHECK(L2, 1); |
1503 | } | 1357 | } else { |
1504 | else | ||
1505 | { | ||
1506 | raise_luaL_error(L1, "Error copying a metatable"); | 1358 | raise_luaL_error(L1, "Error copying a metatable"); |
1507 | } | 1359 | } |
1508 | // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel | 1360 | // first, add the entry in the cache (at this point it is either the actual userdata or the keeper sentinel |
1509 | lua_pushlightuserdata( L2, source); // ... u source | 1361 | lua_pushlightuserdata(L2, source); // L2: ... u source |
1510 | lua_pushvalue( L2, -2); // ... u source u | 1362 | lua_pushvalue(L2, -2); // L2: ... u source u |
1511 | lua_rawset( L2, L2_cache_i); // ... u | 1363 | lua_rawset(L2, L2_cache_i); // L2: ... u |
1512 | // make sure we have the userdata now | 1364 | // make sure we have the userdata now |
1513 | if (LookupMode::ToKeeper == mode) // ... userdata_clone_sentinel | 1365 | if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel |
1514 | { | 1366 | lua_getupvalue(L2, -1, 2); // L2: ... userdata_clone_sentinel u |
1515 | lua_getupvalue(L2, -1, 2); // ... userdata_clone_sentinel u | ||
1516 | } | 1367 | } |
1517 | // assign uservalues | 1368 | // assign uservalues |
1518 | while (uvi > 0) | 1369 | while (uvi > 0) { |
1519 | { | ||
1520 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; | 1370 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; |
1521 | if (!c.inter_copy_one()) // ... u uv | 1371 | if (!c.inter_copy_one()) { // L2: ... u uv |
1522 | { | ||
1523 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); | 1372 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); |
1524 | } | 1373 | } |
1525 | lua_pop(L1, 1); // ... mt __lanesclone [uv]* | 1374 | lua_pop(L1, 1); // L1: ... mt __lanesclone [uv]* |
1526 | // this pops the value from the stack | 1375 | // this pops the value from the stack |
1527 | lua_setiuservalue(L2, -2, uvi); // ... u | 1376 | lua_setiuservalue(L2, -2, uvi); // L2: ... u |
1528 | -- uvi; | 1377 | --uvi; |
1529 | } | 1378 | } |
1530 | // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination | 1379 | // when we are done, all uservalues are popped from the source stack, and we want only the single transferred value in the destination |
1531 | if (LookupMode::ToKeeper == mode) // ... userdata_clone_sentinel u | 1380 | if (LookupMode::ToKeeper == mode) { // L2: ... userdata_clone_sentinel u |
1532 | { | 1381 | lua_pop(L2, 1); // L2: ... userdata_clone_sentinel |
1533 | lua_pop(L2, 1); // ... userdata_clone_sentinel | ||
1534 | } | 1382 | } |
1535 | STACK_CHECK(L2, 1); | 1383 | STACK_CHECK(L2, 1); |
1536 | STACK_CHECK(L1, 2); | 1384 | STACK_CHECK(L1, 2); |
1537 | // call cloning function in source state to perform the actual memory cloning | 1385 | // call cloning function in source state to perform the actual memory cloning |
1538 | lua_pushlightuserdata(L1, clone); // ... mt __lanesclone clone | 1386 | lua_pushlightuserdata(L1, clone); // L1: ... mt __lanesclone clone |
1539 | lua_pushlightuserdata(L1, source); // ... mt __lanesclone clone source | 1387 | lua_pushlightuserdata(L1, source); // L1: ... mt __lanesclone clone source |
1540 | lua_pushinteger(L1, static_cast<lua_Integer>(userdata_size)); // ... mt __lanesclone clone source size | 1388 | lua_pushinteger(L1, static_cast<lua_Integer>(userdata_size)); // L1: ... mt __lanesclone clone source size |
1541 | lua_call(L1, 3, 0); // ... mt | 1389 | lua_call(L1, 3, 0); // L1: ... mt |
1542 | STACK_CHECK(L1, 1); | 1390 | STACK_CHECK(L1, 1); |
1543 | } | 1391 | } |
1544 | 1392 | ||
1545 | STACK_CHECK(L2, 1); | 1393 | STACK_CHECK(L2, 1); |
1546 | lua_pop(L1, 1); // ... | 1394 | lua_pop(L1, 1); // L1: ... |
1547 | STACK_CHECK(L1, 0); | 1395 | STACK_CHECK(L1, 0); |
1548 | return true; | 1396 | return true; |
1549 | } | 1397 | } |
@@ -1554,14 +1402,12 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1554 | { | 1402 | { |
1555 | STACK_CHECK_START_REL(L1, 0); | 1403 | STACK_CHECK_START_REL(L1, 0); |
1556 | STACK_CHECK_START_REL(L2, 0); | 1404 | STACK_CHECK_START_REL(L2, 0); |
1557 | if (vt == VT::KEY) | 1405 | if (vt == VT::KEY) { |
1558 | { | ||
1559 | return false; | 1406 | return false; |
1560 | } | 1407 | } |
1561 | 1408 | ||
1562 | // try clonable userdata first | 1409 | // try clonable userdata first |
1563 | if (copyclone()) | 1410 | if (copyClonable()) { |
1564 | { | ||
1565 | STACK_CHECK(L1, 0); | 1411 | STACK_CHECK(L1, 0); |
1566 | STACK_CHECK(L2, 1); | 1412 | STACK_CHECK(L2, 1); |
1567 | return true; | 1413 | return true; |
@@ -1572,8 +1418,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1572 | 1418 | ||
1573 | // Allow only deep userdata entities to be copied across | 1419 | // Allow only deep userdata entities to be copied across |
1574 | DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n")); | 1420 | DEBUGSPEW_CODE(fprintf(stderr, "USERDATA\n")); |
1575 | if (copydeep()) | 1421 | if (copyDeep()) { |
1576 | { | ||
1577 | STACK_CHECK(L1, 0); | 1422 | STACK_CHECK(L1, 0); |
1578 | STACK_CHECK(L2, 1); | 1423 | STACK_CHECK(L2, 1); |
1579 | return true; | 1424 | return true; |
@@ -1583,13 +1428,10 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1583 | STACK_CHECK(L2, 0); | 1428 | STACK_CHECK(L2, 0); |
1584 | 1429 | ||
1585 | // Not a deep or clonable full userdata | 1430 | // Not a deep or clonable full userdata |
1586 | if (U->demoteFullUserdata) // attempt demotion to light userdata | 1431 | if (U->demoteFullUserdata) { // attempt demotion to light userdata |
1587 | { | ||
1588 | void* const lud{ lua_touserdata(L1, L1_i) }; | 1432 | void* const lud{ lua_touserdata(L1, L1_i) }; |
1589 | lua_pushlightuserdata(L2, lud); | 1433 | lua_pushlightuserdata(L2, lud); |
1590 | } | 1434 | } else { // raise an error |
1591 | else // raise an error | ||
1592 | { | ||
1593 | raise_luaL_error(L1, "can't copy non-deep full userdata across lanes"); | 1435 | raise_luaL_error(L1, "can't copy non-deep full userdata across lanes"); |
1594 | } | 1436 | } |
1595 | 1437 | ||
@@ -1602,37 +1444,33 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1602 | 1444 | ||
1603 | [[nodiscard]] bool InterCopyContext::inter_copy_function() const | 1445 | [[nodiscard]] bool InterCopyContext::inter_copy_function() const |
1604 | { | 1446 | { |
1605 | if (vt == VT::KEY) | 1447 | if (vt == VT::KEY) { |
1606 | { | ||
1607 | return false; | 1448 | return false; |
1608 | } | 1449 | } |
1609 | 1450 | ||
1610 | STACK_CHECK_START_REL(L1, 0); // L1 (source) // L2 (destination) | 1451 | STACK_CHECK_START_REL(L1, 0); |
1611 | STACK_CHECK_START_REL(L2, 0); | 1452 | STACK_CHECK_START_REL(L2, 0); |
1612 | DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name)); | 1453 | DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name)); |
1613 | 1454 | ||
1614 | if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) // we are actually copying a clonable full userdata from a keeper | 1455 | if (lua_tocfunction(L1, L1_i) == userdata_clone_sentinel) { // we are actually copying a clonable full userdata from a keeper |
1615 | { | ||
1616 | // clone the full userdata again | 1456 | // clone the full userdata again |
1617 | 1457 | ||
1618 | // let's see if we already restored this userdata | 1458 | // let's see if we already restored this userdata |
1619 | lua_getupvalue(L1, L1_i, 2); // ... u | 1459 | lua_getupvalue(L1, L1_i, 2); // L1: ... u |
1620 | void* source = lua_touserdata(L1, -1); | 1460 | void* source = lua_touserdata(L1, -1); |
1621 | lua_pushlightuserdata(L2, source); // ... source | 1461 | lua_pushlightuserdata(L2, source); // L2: ... source |
1622 | lua_rawget(L2, L2_cache_i); // ... u? | 1462 | lua_rawget(L2, L2_cache_i); // L2: ... u? |
1623 | if (!lua_isnil(L2, -1)) | 1463 | if (!lua_isnil(L2, -1)) { |
1624 | { | 1464 | lua_pop(L1, 1); // L1: ... |
1625 | lua_pop(L1, 1); // ... | ||
1626 | STACK_CHECK(L1, 0); | 1465 | STACK_CHECK(L1, 0); |
1627 | STACK_CHECK(L2, 1); | 1466 | STACK_CHECK(L2, 1); |
1628 | return true; | 1467 | return true; |
1629 | } | 1468 | } |
1630 | lua_pop(L2, 1); // ... | 1469 | lua_pop(L2, 1); // L2: ... |
1631 | 1470 | ||
1632 | // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself | 1471 | // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself |
1633 | bool const found{ lookup_table() }; // ... mt? | 1472 | bool const found{ lookup_table() }; // L2: ... mt? |
1634 | if (!found) | 1473 | if (!found) { |
1635 | { | ||
1636 | STACK_CHECK(L2, 0); | 1474 | STACK_CHECK(L2, 0); |
1637 | return false; | 1475 | return false; |
1638 | } | 1476 | } |
@@ -1645,55 +1483,51 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1645 | { | 1483 | { |
1646 | // extract uservalues (don't transfer them yet) | 1484 | // extract uservalues (don't transfer them yet) |
1647 | int uvi = 0; | 1485 | int uvi = 0; |
1648 | while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // ... u uv | 1486 | while (lua_getiuservalue(L1, source_i, ++uvi) != LUA_TNONE) {} // L1: ... u uv |
1649 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 1487 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now |
1650 | lua_pop(L1, 1); // ... u [uv]* | 1488 | lua_pop(L1, 1); // L1: ... u [uv]* |
1651 | --uvi; | 1489 | --uvi; |
1652 | STACK_CHECK(L1, uvi + 1); | 1490 | STACK_CHECK(L1, uvi + 1); |
1653 | // create the clone userdata with the required number of uservalue slots | 1491 | // create the clone userdata with the required number of uservalue slots |
1654 | clone = lua_newuserdatauv(L2, userdata_size, uvi); // ... mt u | 1492 | clone = lua_newuserdatauv(L2, userdata_size, uvi); // L2: ... mt u |
1655 | // add it in the cache | 1493 | // add it in the cache |
1656 | lua_pushlightuserdata(L2, source); // ... mt u source | 1494 | lua_pushlightuserdata(L2, source); // L2: ... mt u source |
1657 | lua_pushvalue(L2, -2); // ... mt u source u | 1495 | lua_pushvalue(L2, -2); // L2: ... mt u source u |
1658 | lua_rawset(L2, L2_cache_i); // ... mt u | 1496 | lua_rawset(L2, L2_cache_i); // L2: ... mt u |
1659 | // set metatable | 1497 | // set metatable |
1660 | lua_pushvalue(L2, -2); // ... mt u mt | 1498 | lua_pushvalue(L2, -2); // L2: ... mt u mt |
1661 | lua_setmetatable(L2, -2); // ... mt u | 1499 | lua_setmetatable(L2, -2); // L2: ... mt u |
1662 | // transfer and assign uservalues | 1500 | // transfer and assign uservalues |
1663 | InterCopyContext c{ *this }; | 1501 | InterCopyContext c{ *this }; |
1664 | while (uvi > 0) | 1502 | while (uvi > 0) { |
1665 | { | ||
1666 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; | 1503 | c.L1_i = SourceIndex{ lua_absindex(L1, -1) }; |
1667 | if (!c.inter_copy_one()) // ... mt u uv | 1504 | if (!c.inter_copy_one()) { // L2: ... mt u uv |
1668 | { | ||
1669 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); | 1505 | raise_luaL_error(L1, "Cannot copy upvalue type '%s'", luaL_typename(L1, -1)); |
1670 | } | 1506 | } |
1671 | lua_pop(L1, 1); // ... u [uv]* | 1507 | lua_pop(L1, 1); // L1: ... u [uv]* |
1672 | // this pops the value from the stack | 1508 | // this pops the value from the stack |
1673 | lua_setiuservalue(L2, -2, uvi); // ... mt u | 1509 | lua_setiuservalue(L2, -2, uvi); // L2: ... mt u |
1674 | -- uvi; | 1510 | --uvi; |
1675 | } | 1511 | } |
1676 | // when we are done, all uservalues are popped from the stack, we can pop the source as well | 1512 | // when we are done, all uservalues are popped from the stack, we can pop the source as well |
1677 | lua_pop(L1, 1); // ... | 1513 | lua_pop(L1, 1); // L1: ... |
1678 | STACK_CHECK(L1, 0); | 1514 | STACK_CHECK(L1, 0); |
1679 | STACK_CHECK(L2, 2); // ... mt u | 1515 | STACK_CHECK(L2, 2); // L2: ... mt u |
1680 | } | 1516 | } |
1681 | // perform the custom cloning part | 1517 | // perform the custom cloning part |
1682 | lua_insert(L2, -2); // ... u mt | 1518 | lua_insert(L2, -2); // L2: ... u mt |
1683 | // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with | 1519 | // __lanesclone should always exist because we wouldn't be restoring data from a userdata_clone_sentinel closure to begin with |
1684 | lua_getfield(L2, -1, "__lanesclone"); // ... u mt __lanesclone | 1520 | lua_getfield(L2, -1, "__lanesclone"); // L2: ... u mt __lanesclone |
1685 | lua_remove(L2, -2); // ... u __lanesclone | 1521 | lua_remove(L2, -2); // L2: ... u __lanesclone |
1686 | lua_pushlightuserdata(L2, clone); // ... u __lanesclone clone | 1522 | lua_pushlightuserdata(L2, clone); // L2: ... u __lanesclone clone |
1687 | lua_pushlightuserdata(L2, source); // ... u __lanesclone clone source | 1523 | lua_pushlightuserdata(L2, source); // L2: ... u __lanesclone clone source |
1688 | lua_pushinteger(L2, userdata_size); // ... u __lanesclone clone source size | 1524 | lua_pushinteger(L2, userdata_size); // L2: ... u __lanesclone clone source size |
1689 | // clone:__lanesclone(dest, source, size) | 1525 | // clone:__lanesclone(dest, source, size) |
1690 | lua_call(L2, 3, 0); // ... u | 1526 | lua_call(L2, 3, 0); // L2: ... u |
1691 | } | 1527 | } else { // regular function |
1692 | else // regular function | 1528 | DEBUGSPEW_CODE(fprintf(stderr, "FUNCTION %s\n", name)); |
1693 | { | ||
1694 | DEBUGSPEW_CODE(fprintf( stderr, "FUNCTION %s\n", name)); | ||
1695 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1529 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1696 | copy_cached_func(); // ... f | 1530 | copy_cached_func(); // L2: ... f |
1697 | } | 1531 | } |
1698 | STACK_CHECK(L2, 1); | 1532 | STACK_CHECK(L2, 1); |
1699 | STACK_CHECK(L1, 0); | 1533 | STACK_CHECK(L1, 0); |
@@ -1704,8 +1538,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1704 | 1538 | ||
1705 | [[nodiscard]] bool InterCopyContext::inter_copy_table() const | 1539 | [[nodiscard]] bool InterCopyContext::inter_copy_table() const |
1706 | { | 1540 | { |
1707 | if (vt == VT::KEY) | 1541 | if (vt == VT::KEY) { |
1708 | { | ||
1709 | return false; | 1542 | return false; |
1710 | } | 1543 | } |
1711 | 1544 | ||
@@ -1717,24 +1550,22 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1717 | * 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?) | 1550 | * 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?) |
1718 | * 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 | 1551 | * 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 |
1719 | */ | 1552 | */ |
1720 | if (lookup_table()) | 1553 | if (lookup_table()) { |
1721 | { | ||
1722 | 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 | 1554 | 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 |
1723 | return true; | 1555 | return true; |
1724 | } | 1556 | } |
1725 | 1557 | ||
1726 | /* Check if we've already copied the same table from 'L1' (during this transmission), and | 1558 | /* Check if we've already copied the same table from 'L1' (during this transmission), and |
1727 | * reuse the old copy. This allows table upvalues shared by multiple | 1559 | * reuse the old copy. This allows table upvalues shared by multiple |
1728 | * local functions to point to the same table, also in the target. | 1560 | * local functions to point to the same table, also in the target. |
1729 | * Also, this takes care of cyclic tables and multiple references | 1561 | * Also, this takes care of cyclic tables and multiple references |
1730 | * to the same subtable. | 1562 | * to the same subtable. |
1731 | * | 1563 | * |
1732 | * Note: Even metatables need to go through this test; to detect | 1564 | * Note: Even metatables need to go through this test; to detect |
1733 | * loops such as those in required module tables (getmetatable(lanes).lanes == lanes) | 1565 | * loops such as those in required module tables (getmetatable(lanes).lanes == lanes) |
1734 | */ | 1566 | */ |
1735 | if (push_cached_table(L2, L2_cache_i, L1, L1_i)) | 1567 | if (push_cached_table(L2, L2_cache_i, L1, L1_i)) { |
1736 | { | 1568 | LUA_ASSERT(L1, lua_istable(L2, -1)); // from cache |
1737 | LUA_ASSERT(L1, lua_istable(L2, -1)); // from cache | ||
1738 | return true; | 1569 | return true; |
1739 | } | 1570 | } |
1740 | LUA_ASSERT(L1, lua_istable(L2, -1)); | 1571 | LUA_ASSERT(L1, lua_istable(L2, -1)); |
@@ -1743,19 +1574,17 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1743 | STACK_GROW(L2, 2); | 1574 | STACK_GROW(L2, 2); |
1744 | 1575 | ||
1745 | lua_pushnil(L1); // start iteration | 1576 | lua_pushnil(L1); // start iteration |
1746 | while (lua_next(L1, L1_i)) | 1577 | while (lua_next(L1, L1_i)) { |
1747 | { | ||
1748 | // need a function to prevent overflowing the stack with verboseErrors-induced alloca() | 1578 | // need a function to prevent overflowing the stack with verboseErrors-induced alloca() |
1749 | inter_copy_keyvaluepair(); | 1579 | inter_copy_keyvaluepair(); |
1750 | lua_pop(L1, 1); // pop value (next round) | 1580 | lua_pop(L1, 1); // pop value (next round) |
1751 | } | 1581 | } |
1752 | STACK_CHECK(L1, 0); | 1582 | STACK_CHECK(L1, 0); |
1753 | STACK_CHECK(L2, 1); | 1583 | STACK_CHECK(L2, 1); |
1754 | 1584 | ||
1755 | // Metatables are expected to be immutable, and copied only once. | 1585 | // Metatables are expected to be immutable, and copied only once. |
1756 | if (push_cached_metatable()) // ... t mt? | 1586 | if (push_cached_metatable()) { // L2: ... t mt? |
1757 | { | 1587 | lua_setmetatable(L2, -2); // L2: ... t |
1758 | lua_setmetatable(L2, -2); // ... t | ||
1759 | } | 1588 | } |
1760 | STACK_CHECK(L2, 1); | 1589 | STACK_CHECK(L2, 1); |
1761 | STACK_CHECK(L1, 0); | 1590 | STACK_CHECK(L1, 0); |
@@ -1786,8 +1615,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1786 | 1615 | ||
1787 | [[nodiscard]] bool InterCopyContext::inter_copy_nil() const | 1616 | [[nodiscard]] bool InterCopyContext::inter_copy_nil() const |
1788 | { | 1617 | { |
1789 | if (vt == VT::KEY) | 1618 | if (vt == VT::KEY) { |
1790 | { | ||
1791 | return false; | 1619 | return false; |
1792 | } | 1620 | } |
1793 | lua_pushnil(L2); | 1621 | lua_pushnil(L2); |
@@ -1798,15 +1626,13 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1798 | 1626 | ||
1799 | [[nodiscard]] bool InterCopyContext::inter_copy_number() const | 1627 | [[nodiscard]] bool InterCopyContext::inter_copy_number() const |
1800 | { | 1628 | { |
1801 | /* LNUM patch support (keeping integer accuracy) */ | 1629 | // LNUM patch support (keeping integer accuracy) |
1802 | #if defined LUA_LNUM || LUA_VERSION_NUM >= 503 | 1630 | #if defined LUA_LNUM || LUA_VERSION_NUM >= 503 |
1803 | if (lua_isinteger(L1, L1_i)) | 1631 | if (lua_isinteger(L1, L1_i)) { |
1804 | { | ||
1805 | lua_Integer const v{ lua_tointeger(L1, L1_i) }; | 1632 | lua_Integer const v{ lua_tointeger(L1, L1_i) }; |
1806 | DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v)); | 1633 | DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v)); |
1807 | lua_pushinteger(L2, v); | 1634 | lua_pushinteger(L2, v); |
1808 | } | 1635 | } else |
1809 | else | ||
1810 | #endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 | 1636 | #endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503 |
1811 | { | 1637 | { |
1812 | lua_Number const v{ lua_tonumber(L1, L1_i) }; | 1638 | lua_Number const v{ lua_tonumber(L1, L1_i) }; |
@@ -1830,63 +1656,78 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1830 | // ################################################################################################# | 1656 | // ################################################################################################# |
1831 | 1657 | ||
1832 | /* | 1658 | /* |
1833 | * Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove | 1659 | * Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove |
1834 | * the original value. | 1660 | * the original value. |
1835 | * | 1661 | * |
1836 | * NOTE: Both the states must be solely in the current OS thread's possession. | 1662 | * NOTE: Both the states must be solely in the current OS thread's possession. |
1837 | * | 1663 | * |
1838 | * 'i' is an absolute index (no -1, ...) | 1664 | * 'i' is an absolute index (no -1, ...) |
1839 | * | 1665 | * |
1840 | * Returns true if value was pushed, false if its type is non-supported. | 1666 | * Returns true if value was pushed, false if its type is non-supported. |
1841 | */ | 1667 | */ |
1842 | [[nodiscard]] bool InterCopyContext::inter_copy_one() const | 1668 | [[nodiscard]] bool InterCopyContext::inter_copy_one() const |
1843 | { | 1669 | { |
1844 | static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING); | 1670 | static constexpr int kPODmask = (1 << LUA_TNIL) | (1 << LUA_TBOOLEAN) | (1 << LUA_TLIGHTUSERDATA) | (1 << LUA_TNUMBER) | (1 << LUA_TSTRING); |
1845 | STACK_GROW(L2, 1); | 1671 | STACK_GROW(L2, 1); |
1846 | STACK_CHECK_START_REL(L1, 0); // L1 // L2 | 1672 | STACK_CHECK_START_REL(L1, 0); |
1847 | STACK_CHECK_START_REL(L2, 0); // L1 // L2 | 1673 | STACK_CHECK_START_REL(L2, 0); |
1848 | 1674 | ||
1849 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END)); | 1675 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "inter_copy_one()\n" INDENT_END(U))); |
1850 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1676 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1851 | 1677 | ||
1852 | LuaType val_type{ lua_type_as_enum(L1, L1_i) }; | 1678 | LuaType val_type{ lua_type_as_enum(L1, L1_i) }; |
1853 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "%s %s: " INDENT_END, lua_type_names[static_cast<int>(val_type)], vt_names[static_cast<int>(vt)])); | 1679 | 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)])); |
1854 | 1680 | ||
1855 | // Non-POD can be skipped if its metatable contains { __lanesignore = true } | 1681 | // Non-POD can be skipped if its metatable contains { __lanesignore = true } |
1856 | if (((1 << static_cast<int>(val_type)) & kPODmask) == 0) | 1682 | if (((1 << static_cast<int>(val_type)) & kPODmask) == 0) { |
1857 | { | 1683 | if (lua_getmetatable(L1, L1_i)) { // L1: ... mt |
1858 | if (lua_getmetatable(L1, L1_i)) // ... mt | 1684 | lua_getfield(L1, -1, "__lanesignore"); // L1: ... mt ignore? |
1859 | { | 1685 | if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) { |
1860 | lua_getfield(L1, -1, "__lanesignore"); // ... mt ignore? | 1686 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END(U))); |
1861 | if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) | ||
1862 | { | ||
1863 | DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END)); | ||
1864 | val_type = LuaType::NIL; | 1687 | val_type = LuaType::NIL; |
1865 | } | 1688 | } |
1866 | lua_pop(L1, 2); // ... | 1689 | lua_pop(L1, 2); // L1: ... |
1867 | } | 1690 | } |
1868 | } | 1691 | } |
1869 | STACK_CHECK(L1, 0); | 1692 | STACK_CHECK(L1, 0); |
1870 | 1693 | ||
1871 | /* Lets push nil to L2 if the object should be ignored */ | 1694 | // Lets push nil to L2 if the object should be ignored |
1872 | bool ret{ true }; | 1695 | bool ret{ true }; |
1873 | switch (val_type) | 1696 | switch (val_type) { |
1874 | { | 1697 | // Basic types allowed both as values, and as table keys |
1875 | // Basic types allowed both as values, and as table keys | 1698 | case LuaType::BOOLEAN: |
1876 | case LuaType::BOOLEAN: ret = inter_copy_boolean(); break; | 1699 | ret = inter_copy_boolean(); |
1877 | case LuaType::NUMBER: ret = inter_copy_number(); break; | 1700 | break; |
1878 | case LuaType::STRING: ret = inter_copy_string(); break; | 1701 | case LuaType::NUMBER: |
1879 | case LuaType::LIGHTUSERDATA: ret = inter_copy_lightuserdata();break; | 1702 | ret = inter_copy_number(); |
1880 | 1703 | break; | |
1881 | // The following types are not allowed as table keys | 1704 | case LuaType::STRING: |
1882 | case LuaType::USERDATA: ret = inter_copy_userdata(); break; | 1705 | ret = inter_copy_string(); |
1883 | case LuaType::NIL: ret = inter_copy_nil(); break; | 1706 | break; |
1884 | case LuaType::FUNCTION: ret = inter_copy_function(); break; | 1707 | case LuaType::LIGHTUSERDATA: |
1885 | case LuaType::TABLE: ret = inter_copy_table(); break; | 1708 | ret = inter_copy_lightuserdata(); |
1886 | 1709 | break; | |
1887 | // The following types cannot be copied | 1710 | |
1888 | case LuaType::CDATA: [[fallthrough]]; | 1711 | // The following types are not allowed as table keys |
1889 | case LuaType::THREAD: ret = false; break; | 1712 | case LuaType::USERDATA: |
1713 | ret = inter_copy_userdata(); | ||
1714 | break; | ||
1715 | case LuaType::NIL: | ||
1716 | ret = inter_copy_nil(); | ||
1717 | break; | ||
1718 | case LuaType::FUNCTION: | ||
1719 | ret = inter_copy_function(); | ||
1720 | break; | ||
1721 | case LuaType::TABLE: | ||
1722 | ret = inter_copy_table(); | ||
1723 | break; | ||
1724 | |||
1725 | // The following types cannot be copied | ||
1726 | case LuaType::CDATA: | ||
1727 | [[fallthrough]]; | ||
1728 | case LuaType::THREAD: | ||
1729 | ret = false; | ||
1730 | break; | ||
1890 | } | 1731 | } |
1891 | 1732 | ||
1892 | STACK_CHECK(L2, ret ? 1 : 0); | 1733 | STACK_CHECK(L2, ret ? 1 : 0); |
@@ -1902,14 +1743,13 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1902 | { | 1743 | { |
1903 | LUA_ASSERT(L1, vt == VT::NORMAL); | 1744 | LUA_ASSERT(L1, vt == VT::NORMAL); |
1904 | 1745 | ||
1905 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END)); | 1746 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy()\n" INDENT_END(U))); |
1906 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1747 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
1907 | 1748 | ||
1908 | int const top_L1{ lua_gettop(L1) }; | 1749 | int const top_L1{ lua_gettop(L1) }; |
1909 | if (n_ > top_L1) | 1750 | if (n_ > top_L1) { |
1910 | { | ||
1911 | // requesting to copy more than is available? | 1751 | // requesting to copy more than is available? |
1912 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END)); | 1752 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "nothing to copy()\n" INDENT_END(U))); |
1913 | return InterCopyResult::NotEnoughValues; | 1753 | return InterCopyResult::NotEnoughValues; |
1914 | } | 1754 | } |
1915 | 1755 | ||
@@ -1917,35 +1757,31 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1917 | STACK_GROW(L2, n_ + 1); | 1757 | STACK_GROW(L2, n_ + 1); |
1918 | 1758 | ||
1919 | /* | 1759 | /* |
1920 | * Make a cache table for the duration of this copy. Collects tables and | 1760 | * Make a cache table for the duration of this copy. Collects tables and |
1921 | * function entries, avoiding the same entries to be passed on as multiple | 1761 | * function entries, avoiding the same entries to be passed on as multiple |
1922 | * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner! | 1762 | * copies. ESSENTIAL i.e. for handling upvalue tables in the right manner! |
1923 | */ | 1763 | */ |
1924 | int const top_L2{ lua_gettop(L2) }; // ... | 1764 | int const top_L2{ lua_gettop(L2) }; // L2: ... |
1925 | lua_newtable(L2); // ... cache | 1765 | lua_newtable(L2); // L2: ... cache |
1926 | 1766 | ||
1927 | char tmpBuf[16]; | 1767 | char tmpBuf[16]; |
1928 | char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" }; | 1768 | char const* const pBuf{ U->verboseErrors ? tmpBuf : "?" }; |
1929 | InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf }; | 1769 | InterCopyContext c{ U, L2, L1, CacheIndex{ top_L2 + 1 }, {}, VT::NORMAL, mode, pBuf }; |
1930 | bool copyok{ true }; | 1770 | bool copyok{ true }; |
1931 | STACK_CHECK_START_REL(L1, 0); | 1771 | STACK_CHECK_START_REL(L1, 0); |
1932 | for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) | 1772 | for (int i{ top_L1 - n_ + 1 }, j{ 1 }; i <= top_L1; ++i, ++j) { |
1933 | { | 1773 | if (U->verboseErrors) { |
1934 | if (U->verboseErrors) | ||
1935 | { | ||
1936 | sprintf(tmpBuf, "arg_%d", j); | 1774 | sprintf(tmpBuf, "arg_%d", j); |
1937 | } | 1775 | } |
1938 | c.L1_i = SourceIndex{ i }; | 1776 | c.L1_i = SourceIndex{ i }; |
1939 | copyok = c.inter_copy_one(); // ... cache {}n | 1777 | copyok = c.inter_copy_one(); // L2: ... cache {}n |
1940 | if (!copyok) | 1778 | if (!copyok) { |
1941 | { | ||
1942 | break; | 1779 | break; |
1943 | } | 1780 | } |
1944 | } | 1781 | } |
1945 | STACK_CHECK(L1, 0); | 1782 | STACK_CHECK(L1, 0); |
1946 | 1783 | ||
1947 | if (copyok) | 1784 | if (copyok) { |
1948 | { | ||
1949 | STACK_CHECK(L2, n_ + 1); | 1785 | STACK_CHECK(L2, n_ + 1); |
1950 | // Remove the cache table. Persistent caching would cause i.e. multiple | 1786 | // Remove the cache table. Persistent caching would cause i.e. multiple |
1951 | // messages passed in the same table to use the same table also in receiving end. | 1787 | // messages passed in the same table to use the same table also in receiving end. |
@@ -1964,7 +1800,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1964 | [[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const | 1800 | [[nodiscard]] InterCopyResult InterCopyContext::inter_move(int n_) const |
1965 | { | 1801 | { |
1966 | InterCopyResult const ret{ inter_copy(n_) }; | 1802 | InterCopyResult const ret{ inter_copy(n_) }; |
1967 | lua_pop( L1, n_); | 1803 | lua_pop(L1, n_); |
1968 | return ret; | 1804 | return ret; |
1969 | } | 1805 | } |
1970 | 1806 | ||
@@ -1976,22 +1812,19 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
1976 | // else raise an error in L1 | 1812 | // else raise an error in L1 |
1977 | [[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const | 1813 | [[nodiscard]] InterCopyResult InterCopyContext::inter_copy_package() const |
1978 | { | 1814 | { |
1979 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END)); | 1815 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "InterCopyContext::inter_copy_package()\n" INDENT_END(U))); |
1980 | 1816 | ||
1981 | class OnExit | 1817 | class OnExit |
1982 | { | 1818 | { |
1983 | private: | 1819 | private: |
1984 | |||
1985 | lua_State* const L2; | 1820 | lua_State* const L2; |
1986 | int const top_L2; | 1821 | int const top_L2; |
1987 | DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); | 1822 | DEBUGSPEW_CODE(DebugSpewIndentScope m_scope); |
1988 | 1823 | ||
1989 | public: | 1824 | public: |
1990 | |||
1991 | OnExit(Universe* U_, lua_State* L2_) | 1825 | OnExit(Universe* U_, lua_State* L2_) |
1992 | : L2{ L2_ } | 1826 | : L2{ L2_ } |
1993 | , top_L2{ lua_gettop(L2) } | 1827 | , top_L2{ lua_gettop(L2) } DEBUGSPEW_COMMA_PARAM(m_scope{ U_ }) |
1994 | DEBUGSPEW_COMMA_PARAM(m_scope{ U_ }) | ||
1995 | { | 1828 | { |
1996 | } | 1829 | } |
1997 | 1830 | ||
@@ -2002,21 +1835,18 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
2002 | } onExit{ U, L2 }; | 1835 | } onExit{ U, L2 }; |
2003 | 1836 | ||
2004 | STACK_CHECK_START_REL(L1, 0); | 1837 | STACK_CHECK_START_REL(L1, 0); |
2005 | if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) | 1838 | if (lua_type_as_enum(L1, L1_i) != LuaType::TABLE) { |
2006 | { | ||
2007 | lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i)); | 1839 | lua_pushfstring(L1, "expected package as table, got %s", luaL_typename(L1, L1_i)); |
2008 | STACK_CHECK(L1, 1); | 1840 | STACK_CHECK(L1, 1); |
2009 | // raise the error when copying from lane to lane, else just leave it on the stack to be raised later | 1841 | // raise the error when copying from lane to lane, else just leave it on the stack to be raised later |
2010 | if (mode == LookupMode::LaneBody) | 1842 | if (mode == LookupMode::LaneBody) { |
2011 | { | ||
2012 | raise_lua_error(L1); | 1843 | raise_lua_error(L1); |
2013 | } | 1844 | } |
2014 | return InterCopyResult::Error; | 1845 | return InterCopyResult::Error; |
2015 | } | 1846 | } |
2016 | lua_getglobal(L2, "package"); // TODO: use _R._LOADED.package instead of _G.package | 1847 | lua_getglobal(L2, "package"); // TODO: use _R._LOADED.package instead of _G.package |
2017 | if (lua_isnil(L2, -1)) // package library not loaded: do nothing | 1848 | if (lua_isnil(L2, -1)) { // package library not loaded: do nothing |
2018 | { | 1849 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END(U))); |
2019 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "'package' not loaded, nothing to do\n" INDENT_END)); | ||
2020 | STACK_CHECK(L1, 0); | 1850 | STACK_CHECK(L1, 0); |
2021 | return InterCopyResult::Success; | 1851 | return InterCopyResult::Success; |
2022 | } | 1852 | } |
@@ -2027,35 +1857,26 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
2027 | // users should provide an on_state_create function to setup custom loaders instead | 1857 | // users should provide an on_state_create function to setup custom loaders instead |
2028 | // don't copy package.preload in keeper states (they don't know how to translate functions) | 1858 | // don't copy package.preload in keeper states (they don't know how to translate functions) |
2029 | char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr }; | 1859 | char const* entries[] = { "path", "cpath", (mode == LookupMode::LaneBody) ? "preload" : nullptr /*, (LUA_VERSION_NUM == 501) ? "loaders" : "searchers"*/, nullptr }; |
2030 | for (char const* const entry : entries) | 1860 | for (char const* const entry : entries) { |
2031 | { | 1861 | if (!entry) { |
2032 | if (!entry) | ||
2033 | { | ||
2034 | continue; | 1862 | continue; |
2035 | } | 1863 | } |
2036 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END, entry)); | 1864 | DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "package.%s\n" INDENT_END(U), entry)); |
2037 | lua_getfield(L1, L1_i, entry); | 1865 | lua_getfield(L1, L1_i, entry); |
2038 | if (lua_isnil(L1, -1)) | 1866 | if (lua_isnil(L1, -1)) { |
2039 | { | ||
2040 | lua_pop(L1, 1); | 1867 | lua_pop(L1, 1); |
2041 | } | 1868 | } else { |
2042 | else | ||
2043 | { | ||
2044 | { | 1869 | { |
2045 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); | 1870 | DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); |
2046 | result = inter_move(1); // moves the entry to L2 | 1871 | result = inter_move(1); // moves the entry to L2 |
2047 | STACK_CHECK(L1, 0); | 1872 | STACK_CHECK(L1, 0); |
2048 | } | 1873 | } |
2049 | if (result == InterCopyResult::Success) | 1874 | if (result == InterCopyResult::Success) { |
2050 | { | ||
2051 | lua_setfield(L2, -2, entry); // set package[entry] | 1875 | lua_setfield(L2, -2, entry); // set package[entry] |
2052 | } | 1876 | } else { |
2053 | else | ||
2054 | { | ||
2055 | lua_pushfstring(L1, "failed to copy package entry %s", entry); | 1877 | lua_pushfstring(L1, "failed to copy package entry %s", entry); |
2056 | // raise the error when copying from lane to lane, else just leave it on the stack to be raised later | 1878 | // raise the error when copying from lane to lane, else just leave it on the stack to be raised later |
2057 | if (mode == LookupMode::LaneBody) | 1879 | if (mode == LookupMode::LaneBody) { |
2058 | { | ||
2059 | raise_lua_error(L1); | 1880 | raise_lua_error(L1); |
2060 | } | 1881 | } |
2061 | lua_pop(L1, 1); | 1882 | lua_pop(L1, 1); |