diff options
Diffstat (limited to 'src/deep.cpp')
-rw-r--r-- | src/deep.cpp | 494 |
1 files changed, 494 insertions, 0 deletions
diff --git a/src/deep.cpp b/src/deep.cpp new file mode 100644 index 0000000..d0b8123 --- /dev/null +++ b/src/deep.cpp | |||
@@ -0,0 +1,494 @@ | |||
1 | /* | ||
2 | * DEEP.CPP Copyright (c) 2024, Benoit Germain | ||
3 | * | ||
4 | * Deep userdata support, separate in its own source file to help integration | ||
5 | * without enforcing a Lanes dependency | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | =============================================================================== | ||
10 | |||
11 | Copyright (C) 2002-10 Asko Kauppi <akauppi@gmail.com> | ||
12 | 2011-24 Benoit Germain <bnt.germain@gmail.com> | ||
13 | |||
14 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
15 | of this software and associated documentation files (the "Software"), to deal | ||
16 | in the Software without restriction, including without limitation the rights | ||
17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
18 | copies of the Software, and to permit persons to whom the Software is | ||
19 | furnished to do so, subject to the following conditions: | ||
20 | |||
21 | The above copyright notice and this permission notice shall be included in | ||
22 | all copies or substantial portions of the Software. | ||
23 | |||
24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
30 | THE SOFTWARE. | ||
31 | |||
32 | =============================================================================== | ||
33 | */ | ||
34 | |||
35 | #include "deep.h" | ||
36 | |||
37 | #include "compat.h" | ||
38 | #include "tools.h" | ||
39 | #include "uniquekey.h" | ||
40 | #include "universe.h" | ||
41 | |||
42 | #include <bit> | ||
43 | #include <cassert> | ||
44 | |||
45 | /*-- Metatable copying --*/ | ||
46 | |||
47 | /*---=== Deep userdata ===---*/ | ||
48 | |||
49 | /* | ||
50 | * 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's | ||
51 | * metatables: | ||
52 | * | ||
53 | * metatable -> idfunc | ||
54 | * idfunc -> metatable | ||
55 | */ | ||
56 | // crc64/we of string "DEEP_LOOKUP_KEY" generated at http://www.nitrxgen.net/hashgen/ | ||
57 | static constexpr UniqueKey DEEP_LOOKUP_KEY{ 0x9fb9b4f3f633d83dull }; | ||
58 | |||
59 | /* | ||
60 | * The deep proxy cache is a weak valued table listing all deep UD proxies indexed by the deep UD that they are proxying | ||
61 | * crc64/we of string "DEEP_PROXY_CACHE_KEY" generated at http://www.nitrxgen.net/hashgen/ | ||
62 | */ | ||
63 | static constexpr UniqueKey DEEP_PROXY_CACHE_KEY{ 0x05773d6fc26be106ull }; | ||
64 | |||
65 | /* | ||
66 | * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. | ||
67 | * Pops the both values off the stack. | ||
68 | */ | ||
69 | static void set_deep_lookup(lua_State* L) | ||
70 | { | ||
71 | STACK_GROW( L, 3); | ||
72 | STACK_CHECK_START_REL(L, 2); // a b | ||
73 | push_registry_subtable( L, DEEP_LOOKUP_KEY); // a b {} | ||
74 | STACK_CHECK( L, 3); | ||
75 | lua_insert( L, -3); // {} a b | ||
76 | lua_pushvalue( L, -1); // {} a b b | ||
77 | lua_pushvalue( L,-3); // {} a b b a | ||
78 | lua_rawset( L, -5); // {} a b | ||
79 | lua_rawset( L, -3); // {} | ||
80 | lua_pop( L, 1); // | ||
81 | STACK_CHECK( L, 0); | ||
82 | } | ||
83 | |||
84 | // ################################################################################################ | ||
85 | |||
86 | /* | ||
87 | * Pops the key (metatable or idfunc) off the stack, and replaces with the | ||
88 | * deep lookup value (idfunc/metatable/nil). | ||
89 | */ | ||
90 | static void get_deep_lookup(lua_State* L) | ||
91 | { | ||
92 | STACK_GROW( L, 1); | ||
93 | STACK_CHECK_START_REL(L, 1); // a | ||
94 | DEEP_LOOKUP_KEY.pushValue(L); // a {} | ||
95 | if( !lua_isnil( L, -1)) | ||
96 | { | ||
97 | lua_insert( L, -2); // {} a | ||
98 | lua_rawget( L, -2); // {} b | ||
99 | } | ||
100 | lua_remove( L, -2); // a|b | ||
101 | STACK_CHECK( L, 1); | ||
102 | } | ||
103 | |||
104 | // ################################################################################################ | ||
105 | |||
106 | /* | ||
107 | * Return the registered ID function for 'index' (deep userdata proxy), | ||
108 | * or nullptr if 'index' is not a deep userdata proxy. | ||
109 | */ | ||
110 | [[nodiscard]] static inline luaG_IdFunction get_idfunc(lua_State* L, int index, LookupMode mode_) | ||
111 | { | ||
112 | // when looking inside a keeper, we are 100% sure the object is a deep userdata | ||
113 | if (mode_ == LookupMode::FromKeeper) | ||
114 | { | ||
115 | DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L, index) }; | ||
116 | // we can (and must) cast and fetch the internally stored idfunc | ||
117 | return (*proxy)->idfunc; | ||
118 | } | ||
119 | else | ||
120 | { | ||
121 | // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database | ||
122 | // it is the only way to ensure that the userdata is indeed a deep userdata! | ||
123 | // of course, we could just trust the caller, but we won't | ||
124 | STACK_GROW( L, 1); | ||
125 | STACK_CHECK_START_REL(L, 0); | ||
126 | |||
127 | if( !lua_getmetatable( L, index)) // deep ... metatable? | ||
128 | { | ||
129 | return nullptr; // no metatable: can't be a deep userdata object! | ||
130 | } | ||
131 | |||
132 | // replace metatable with the idfunc pointer, if it is actually a deep userdata | ||
133 | get_deep_lookup( L); // deep ... idfunc|nil | ||
134 | |||
135 | luaG_IdFunction const ret{ *lua_tolightuserdata<luaG_IdFunction>(L, -1) }; // nullptr if not a userdata | ||
136 | lua_pop( L, 1); | ||
137 | STACK_CHECK( L, 0); | ||
138 | return ret; | ||
139 | } | ||
140 | } | ||
141 | |||
142 | // ################################################################################################ | ||
143 | |||
144 | void free_deep_prelude(lua_State* L, DeepPrelude* prelude_) | ||
145 | { | ||
146 | ASSERT_L(prelude_->idfunc); | ||
147 | STACK_CHECK_START_REL(L, 0); | ||
148 | // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup | ||
149 | lua_pushlightuserdata( L, prelude_); | ||
150 | prelude_->idfunc( L, DeepOp::Delete); | ||
151 | lua_pop(L, 1); | ||
152 | STACK_CHECK(L, 0); | ||
153 | } | ||
154 | |||
155 | // ################################################################################################ | ||
156 | |||
157 | /* | ||
158 | * void= mt.__gc( proxy_ud ) | ||
159 | * | ||
160 | * End of life for a proxy object; reduce the deep reference count and clean it up if reaches 0. | ||
161 | * | ||
162 | */ | ||
163 | [[nodiscard]] static int deep_userdata_gc(lua_State* L) | ||
164 | { | ||
165 | DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L, 1) }; | ||
166 | DeepPrelude* p = *proxy; | ||
167 | |||
168 | // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded | ||
169 | // in that case, we are not multithreaded and locking isn't necessary anyway | ||
170 | bool const isLastRef{ p->m_refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; | ||
171 | |||
172 | if (isLastRef) | ||
173 | { | ||
174 | // retrieve wrapped __gc | ||
175 | lua_pushvalue( L, lua_upvalueindex( 1)); // self __gc? | ||
176 | if( !lua_isnil( L, -1)) | ||
177 | { | ||
178 | lua_insert( L, -2); // __gc self | ||
179 | lua_call( L, 1, 0); // | ||
180 | } | ||
181 | // 'idfunc' expects a clean stack to work on | ||
182 | lua_settop( L, 0); | ||
183 | free_deep_prelude( L, p); | ||
184 | |||
185 | // top was set to 0, then userdata was pushed. "delete" might want to pop the userdata (we don't care), but should not push anything! | ||
186 | if ( lua_gettop( L) > 1) | ||
187 | { | ||
188 | return luaL_error( L, "Bad idfunc(DeepOp::Delete): should not push anything"); | ||
189 | } | ||
190 | } | ||
191 | *proxy = nullptr; // make sure we don't use it any more, just in case | ||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | // ################################################################################################ | ||
196 | |||
197 | /* | ||
198 | * Push a proxy userdata on the stack. | ||
199 | * returns nullptr if ok, else some error string related to bad idfunc behavior or module require problem | ||
200 | * (error cannot happen with mode_ == LookupMode::ToKeeper) | ||
201 | * | ||
202 | * Initializes necessary structures if it's the first time 'idfunc' is being | ||
203 | * used in this Lua state (metatable, registring it). Otherwise, increments the | ||
204 | * reference count. | ||
205 | */ | ||
206 | char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_) | ||
207 | { | ||
208 | // Check if a proxy already exists | ||
209 | push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC | ||
210 | lua_pushlightuserdata( L, prelude); // DPC deep | ||
211 | lua_rawget( L, -2); // DPC proxy | ||
212 | if ( !lua_isnil( L, -1)) | ||
213 | { | ||
214 | lua_remove( L, -2); // proxy | ||
215 | return nullptr; | ||
216 | } | ||
217 | else | ||
218 | { | ||
219 | lua_pop( L, 1); // DPC | ||
220 | } | ||
221 | |||
222 | STACK_GROW( L, 7); | ||
223 | STACK_CHECK_START_REL(L, 0); | ||
224 | |||
225 | // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4) | ||
226 | DeepPrelude** proxy = (DeepPrelude**) lua_newuserdatauv(L, sizeof(DeepPrelude*), nuv_); // DPC proxy | ||
227 | ASSERT_L( proxy); | ||
228 | *proxy = prelude; | ||
229 | prelude->m_refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data | ||
230 | |||
231 | // Get/create metatable for 'idfunc' (in this state) | ||
232 | lua_pushlightuserdata( L, std::bit_cast<void*>(prelude->idfunc)); // DPC proxy idfunc | ||
233 | get_deep_lookup( L); // DPC proxy metatable? | ||
234 | |||
235 | if( lua_isnil( L, -1)) // // No metatable yet. | ||
236 | { | ||
237 | char const* modname; | ||
238 | int oldtop = lua_gettop( L); // DPC proxy nil | ||
239 | lua_pop( L, 1); // DPC proxy | ||
240 | // 1 - make one and register it | ||
241 | if (mode_ != LookupMode::ToKeeper) | ||
242 | { | ||
243 | (void) prelude->idfunc( L, DeepOp::Metatable); // DPC proxy metatable | ||
244 | if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1)) | ||
245 | { | ||
246 | lua_settop( L, oldtop); // DPC proxy X | ||
247 | lua_pop( L, 3); // | ||
248 | return "Bad idfunc(eOP_metatable): unexpected pushed value"; | ||
249 | } | ||
250 | // if the metatable contains a __gc, we will call it from our own | ||
251 | lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc | ||
252 | } | ||
253 | else | ||
254 | { | ||
255 | // keepers need a minimal metatable that only contains our own __gc | ||
256 | lua_newtable( L); // DPC proxy metatable | ||
257 | lua_pushnil( L); // DPC proxy metatable nil | ||
258 | } | ||
259 | if( lua_isnil( L, -1)) | ||
260 | { | ||
261 | // Add our own '__gc' method | ||
262 | lua_pop( L, 1); // DPC proxy metatable | ||
263 | lua_pushcfunction( L, deep_userdata_gc); // DPC proxy metatable deep_userdata_gc | ||
264 | } | ||
265 | else | ||
266 | { | ||
267 | // Add our own '__gc' method wrapping the original | ||
268 | lua_pushcclosure( L, deep_userdata_gc, 1); // DPC proxy metatable deep_userdata_gc | ||
269 | } | ||
270 | lua_setfield( L, -2, "__gc"); // DPC proxy metatable | ||
271 | |||
272 | // Memorize for later rounds | ||
273 | lua_pushvalue( L, -1); // DPC proxy metatable metatable | ||
274 | lua_pushlightuserdata( L, std::bit_cast<void*>(prelude->idfunc)); // DPC proxy metatable metatable idfunc | ||
275 | set_deep_lookup( L); // DPC proxy metatable | ||
276 | |||
277 | // 2 - cause the target state to require the module that exported the idfunc | ||
278 | // this is needed because we must make sure the shared library is still loaded as long as we hold a pointer on the idfunc | ||
279 | { | ||
280 | int oldtop_module = lua_gettop( L); | ||
281 | modname = (char const*) prelude->idfunc( L, DeepOp::Module); // DPC proxy metatable | ||
282 | // make sure the function pushed nothing on the stack! | ||
283 | if( lua_gettop( L) - oldtop_module != 0) | ||
284 | { | ||
285 | lua_pop( L, 3); // | ||
286 | return "Bad idfunc(eOP_module): should not push anything"; | ||
287 | } | ||
288 | } | ||
289 | if (nullptr != modname) // we actually got a module name | ||
290 | { | ||
291 | // L.registry._LOADED exists without having registered the 'package' library. | ||
292 | lua_getglobal( L, "require"); // DPC proxy metatable require() | ||
293 | // check that the module is already loaded (or being loaded, we are happy either way) | ||
294 | if( lua_isfunction( L, -1)) | ||
295 | { | ||
296 | lua_pushstring( L, modname); // DPC proxy metatable require() "module" | ||
297 | lua_getfield( L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // DPC proxy metatable require() "module" _R._LOADED | ||
298 | if( lua_istable( L, -1)) | ||
299 | { | ||
300 | lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module" | ||
301 | lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module | ||
302 | int const alreadyloaded = lua_toboolean( L, -1); | ||
303 | if( !alreadyloaded) // not loaded | ||
304 | { | ||
305 | int require_result; | ||
306 | lua_pop( L, 2); // DPC proxy metatable require() "module" | ||
307 | // require "modname" | ||
308 | require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error? | ||
309 | if( require_result != LUA_OK) | ||
310 | { | ||
311 | // failed, return the error message | ||
312 | lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname); | ||
313 | lua_insert( L, -2); // DPC proxy metatable prefix error | ||
314 | lua_concat( L, 2); // DPC proxy metatable error | ||
315 | return lua_tostring( L, -1); | ||
316 | } | ||
317 | } | ||
318 | else // already loaded, we are happy | ||
319 | { | ||
320 | lua_pop( L, 4); // DPC proxy metatable | ||
321 | } | ||
322 | } | ||
323 | else // no L.registry._LOADED; can this ever happen? | ||
324 | { | ||
325 | lua_pop( L, 6); // | ||
326 | return "unexpected error while requiring a module identified by idfunc(eOP_module)"; | ||
327 | } | ||
328 | } | ||
329 | else // a module name, but no require() function :-( | ||
330 | { | ||
331 | lua_pop( L, 4); // | ||
332 | return "lanes receiving deep userdata should register the 'package' library"; | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | STACK_CHECK(L, 2); // DPC proxy metatable | ||
337 | ASSERT_L(lua_type(L, -2) == LUA_TUSERDATA); | ||
338 | ASSERT_L(lua_istable( L, -1)); | ||
339 | lua_setmetatable( L, -2); // DPC proxy | ||
340 | |||
341 | // If we're here, we obviously had to create a new proxy, so cache it. | ||
342 | lua_pushlightuserdata( L, prelude); // DPC proxy deep | ||
343 | lua_pushvalue( L, -2); // DPC proxy deep proxy | ||
344 | lua_rawset( L, -4); // DPC proxy | ||
345 | lua_remove( L, -2); // proxy | ||
346 | ASSERT_L(lua_type(L, -1) == LUA_TUSERDATA); | ||
347 | STACK_CHECK(L, 0); | ||
348 | return nullptr; | ||
349 | } | ||
350 | |||
351 | // ################################################################################################ | ||
352 | |||
353 | /* | ||
354 | * Create a deep userdata | ||
355 | * | ||
356 | * proxy_ud= deep_userdata( idfunc [, ...] ) | ||
357 | * | ||
358 | * Creates a deep userdata entry of the type defined by 'idfunc'. | ||
359 | * Parameters found on the stack are left as is passed on to the 'idfunc' "new" invocation. | ||
360 | * | ||
361 | * 'idfunc' must fulfill the following features: | ||
362 | * | ||
363 | * lightuserdata = idfunc( DeepOp::New [, ...] ) -- creates a new deep data instance | ||
364 | * void = idfunc( DeepOp::Delete, lightuserdata ) -- releases a deep data instance | ||
365 | * tbl = idfunc( DeepOp::Metatable ) -- gives metatable for userdata proxies | ||
366 | * | ||
367 | * Reference counting and true userdata proxying are taken care of for the | ||
368 | * actual data type. | ||
369 | * | ||
370 | * Types using the deep userdata system (and only those!) can be passed between | ||
371 | * separate Lua states via 'luaG_inter_move()'. | ||
372 | * | ||
373 | * Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' | ||
374 | */ | ||
375 | int luaG_newdeepuserdata(Dest L, luaG_IdFunction idfunc, int nuv_) | ||
376 | { | ||
377 | STACK_GROW( L, 1); | ||
378 | STACK_CHECK_START_REL(L, 0); | ||
379 | int const oldtop{ lua_gettop(L) }; | ||
380 | DeepPrelude* const prelude{ static_cast<DeepPrelude*>(idfunc(L, DeepOp::New)) }; | ||
381 | if (prelude == nullptr) | ||
382 | { | ||
383 | return luaL_error( L, "idfunc(DeepOp::New) failed to create deep userdata (out of memory)"); | ||
384 | } | ||
385 | |||
386 | if( prelude->magic != DEEP_VERSION) | ||
387 | { | ||
388 | // just in case, don't leak the newly allocated deep userdata object | ||
389 | lua_pushlightuserdata( L, prelude); | ||
390 | idfunc( L, DeepOp::Delete); | ||
391 | return luaL_error( L, "Bad idfunc(DeepOp::New): DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation"); | ||
392 | } | ||
393 | |||
394 | ASSERT_L(prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'push_deep_proxy' will lift it to 1 | ||
395 | prelude->idfunc = idfunc; | ||
396 | |||
397 | if( lua_gettop( L) - oldtop != 0) | ||
398 | { | ||
399 | // just in case, don't leak the newly allocated deep userdata object | ||
400 | lua_pushlightuserdata( L, prelude); | ||
401 | idfunc( L, DeepOp::Delete); | ||
402 | return luaL_error( L, "Bad idfunc(DeepOp::New): should not push anything on the stack"); | ||
403 | } | ||
404 | |||
405 | char const* const errmsg{ push_deep_proxy(L, prelude, nuv_, LookupMode::LaneBody) }; // proxy | ||
406 | if (errmsg != nullptr) | ||
407 | { | ||
408 | return luaL_error( L, errmsg); | ||
409 | } | ||
410 | STACK_CHECK( L, 1); | ||
411 | return 1; | ||
412 | } | ||
413 | |||
414 | // ################################################################################################ | ||
415 | |||
416 | /* | ||
417 | * Access deep userdata through a proxy. | ||
418 | * | ||
419 | * Reference count is not changed, and access to the deep userdata is not | ||
420 | * serialized. It is the module's responsibility to prevent conflicting usage. | ||
421 | */ | ||
422 | DeepPrelude* luaG_todeep(lua_State* L, luaG_IdFunction idfunc, int index) | ||
423 | { | ||
424 | STACK_CHECK_START_REL(L, 0); | ||
425 | // ensure it is actually a deep userdata | ||
426 | if (get_idfunc(L, index, LookupMode::LaneBody) != idfunc) | ||
427 | { | ||
428 | return nullptr; // no metatable, or wrong kind | ||
429 | } | ||
430 | STACK_CHECK(L, 0); | ||
431 | |||
432 | DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L, index) }; | ||
433 | return *proxy; | ||
434 | } | ||
435 | |||
436 | // ################################################################################################ | ||
437 | |||
438 | /* | ||
439 | * Copy deep userdata between two separate Lua states (from L to L2) | ||
440 | * | ||
441 | * Returns: | ||
442 | * the id function of the copied value, or nullptr for non-deep userdata | ||
443 | * (not copied) | ||
444 | */ | ||
445 | bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_) | ||
446 | { | ||
447 | luaG_IdFunction const idfunc { get_idfunc(L, i, mode_) }; | ||
448 | if (idfunc == nullptr) | ||
449 | { | ||
450 | return false; // not a deep userdata | ||
451 | } | ||
452 | |||
453 | STACK_CHECK_START_REL(L, 0); | ||
454 | STACK_CHECK_START_REL(L2, 0); | ||
455 | |||
456 | // extract all uservalues of the source | ||
457 | int nuv = 0; | ||
458 | while (lua_getiuservalue(L, i, nuv + 1) != LUA_TNONE) // ... u [uv]* nil | ||
459 | { | ||
460 | ++ nuv; | ||
461 | } | ||
462 | // last call returned TNONE and pushed nil, that we don't need | ||
463 | lua_pop( L, 1); // ... u [uv]* | ||
464 | STACK_CHECK( L, nuv); | ||
465 | |||
466 | char const* errmsg{ push_deep_proxy(L2, *lua_tofulluserdata<DeepPrelude*>(L, i), nuv, mode_) }; // u | ||
467 | |||
468 | // transfer all uservalues of the source in the destination | ||
469 | { | ||
470 | int const clone_i = lua_gettop( L2); | ||
471 | while( nuv) | ||
472 | { | ||
473 | if (!inter_copy_one(U, L2, L2_cache_i, L, lua_absindex(L, -1), VT::NORMAL, mode_, upName_)) // u uv | ||
474 | { | ||
475 | return luaL_error(L, "Cannot copy upvalue type '%s'", luaL_typename(L, -1)); | ||
476 | } | ||
477 | lua_pop( L, 1); // ... u [uv]* | ||
478 | // this pops the value from the stack | ||
479 | lua_setiuservalue(L2, clone_i, nuv); // u | ||
480 | -- nuv; | ||
481 | } | ||
482 | } | ||
483 | |||
484 | STACK_CHECK(L2, 1); | ||
485 | STACK_CHECK(L, 0); | ||
486 | |||
487 | if (errmsg != nullptr) | ||
488 | { | ||
489 | // raise the error in the proper state (not the keeper) | ||
490 | lua_State* const errL{ (mode_ == LookupMode::FromKeeper) ? L2 : L }; | ||
491 | luaL_error(errL, errmsg); // doesn't return | ||
492 | } | ||
493 | return true; | ||
494 | } \ No newline at end of file | ||