diff options
| author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-29 16:16:05 +0200 |
|---|---|---|
| committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-29 16:16:05 +0200 |
| commit | d47758d58d532a9716ad4fd85cc806704df13735 (patch) | |
| tree | 0d5a58ee870b5ecea789cbef17400f4489d30b73 /src | |
| parent | a2be64b72e7ef8efc0fd457d4f50a6d796533401 (diff) | |
| download | lanes-d47758d58d532a9716ad4fd85cc806704df13735.tar.gz lanes-d47758d58d532a9716ad4fd85cc806704df13735.tar.bz2 lanes-d47758d58d532a9716ad4fd85cc806704df13735.zip | |
Boyscouting deep.cpp|h
Diffstat (limited to 'src')
| -rw-r--r-- | src/deep.cpp | 182 | ||||
| -rw-r--r-- | src/deep.h | 21 | ||||
| -rw-r--r-- | src/intercopycontext.cpp | 2 |
3 files changed, 110 insertions, 95 deletions
diff --git a/src/deep.cpp b/src/deep.cpp index dfa3579..f5716bf 100644 --- a/src/deep.cpp +++ b/src/deep.cpp | |||
| @@ -60,51 +60,80 @@ static constexpr RegistryUniqueKey kDeepLookupRegKey{ 0xC6788345703C6059ull }; | |||
| 60 | static constexpr RegistryUniqueKey kDeepProxyCacheRegKey{ 0xEBCD49AE1A3DD35Eull }; | 60 | static constexpr RegistryUniqueKey kDeepProxyCacheRegKey{ 0xEBCD49AE1A3DD35Eull }; |
| 61 | 61 | ||
| 62 | // ################################################################################################# | 62 | // ################################################################################################# |
| 63 | // ################################################################################################# | ||
| 64 | namespace { | ||
| 65 | // ############################################################################################# | ||
| 66 | // ############################################################################################# | ||
| 67 | |||
| 68 | /* | ||
| 69 | * void= mt.__gc( proxy_ud ) | ||
| 70 | * | ||
| 71 | * End of life for a proxy object; reduce the deep reference count and clean it up if reaches 0. | ||
| 72 | * | ||
| 73 | */ | ||
| 74 | [[nodiscard]] static int DeepGC(lua_State* const L_) | ||
| 75 | { | ||
| 76 | DeepPrelude* const* const _proxy{ lua_tofulluserdata<DeepPrelude*>(L_, 1) }; | ||
| 77 | DeepPrelude* const _p{ *_proxy }; | ||
| 78 | |||
| 79 | // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded | ||
| 80 | // in that case, we are not multithreaded and locking isn't necessary anyway | ||
| 81 | bool const isLastRef{ _p->refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; | ||
| 82 | |||
| 83 | if (isLastRef) { | ||
| 84 | // retrieve wrapped __gc, if any | ||
| 85 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: self __gc? | ||
| 86 | if (!lua_isnil(L_, -1)) { | ||
| 87 | lua_insert(L_, -2); // L_: __gc self | ||
| 88 | lua_call(L_, 1, 0); // L_: | ||
| 89 | } else { | ||
| 90 | // need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered | ||
| 91 | lua_pop(L_, 2); // L_: | ||
| 92 | } | ||
| 93 | DeepFactory::DeleteDeepObject(L_, _p); | ||
| 94 | } | ||
| 95 | return 0; | ||
| 96 | } | ||
| 63 | 97 | ||
| 64 | /* | 98 | // ############################################################################################# |
| 65 | * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. | ||
| 66 | * Pops the both values off the stack. | ||
| 67 | */ | ||
| 68 | void DeepFactory::storeDeepLookup(lua_State* L_) const | ||
| 69 | { | ||
| 70 | // the deep metatable is at the top of the stack // L_: mt | ||
| 71 | STACK_GROW(L_, 3); | ||
| 72 | STACK_CHECK_START_REL(L_, 0); // L_: mt | ||
| 73 | std::ignore = kDeepLookupRegKey.getSubTable(L_, 0, 0); // L_: mt {} | ||
| 74 | lua_pushvalue(L_, -2); // L_: mt {} mt | ||
| 75 | lua_pushlightuserdata(L_, std::bit_cast<void*>(this)); // L_: mt {} mt factory | ||
| 76 | lua_rawset(L_, -3); // L_: mt {} | ||
| 77 | STACK_CHECK(L_, 1); | ||
| 78 | 99 | ||
| 79 | lua_pushlightuserdata(L_, std::bit_cast<void*>(this)); // L_: mt {} factory | 100 | // Pops the key (metatable or factory) off the stack, and replaces with the deep lookup value (factory/metatable/nil). |
| 80 | lua_pushvalue(L_, -3); // L_: mt {} factory mt | 101 | static void LookupDeep(lua_State* const L_) |
| 81 | lua_rawset(L_, -3); // L_: mt {} | 102 | { |
| 82 | STACK_CHECK(L_, 1); | 103 | STACK_GROW(L_, 1); |
| 104 | STACK_CHECK_START_REL(L_, 1); // L_: a | ||
| 105 | kDeepLookupRegKey.pushValue(L_); // L_: a {} | ||
| 106 | if (!lua_isnil(L_, -1)) { | ||
| 107 | lua_insert(L_, -2); // L_: {} a | ||
| 108 | lua_rawget(L_, -2); // L_: {} b | ||
| 109 | } | ||
| 110 | lua_remove(L_, -2); // L_: a|b | ||
| 111 | STACK_CHECK(L_, 1); | ||
| 112 | } | ||
| 83 | 113 | ||
| 84 | lua_pop(L_, 1); // L_: mt | 114 | // ############################################################################################# |
| 85 | STACK_CHECK(L_, 0); | 115 | // ############################################################################################# |
| 86 | } | 116 | } // namespace |
| 117 | // ################################################################################################# | ||
| 118 | // ################################################################################################# | ||
| 87 | 119 | ||
| 88 | // ################################################################################################# | 120 | // ################################################################################################# |
| 121 | // ################################################################################################# | ||
| 122 | // ####################################### DeepFactory ############################################# | ||
| 123 | // ################################################################################################# | ||
| 124 | // ################################################################################################# | ||
| 89 | 125 | ||
| 90 | // Pops the key (metatable or factory) off the stack, and replaces with the deep lookup value (factory/metatable/nil). | 126 | void DeepFactory::DeleteDeepObject(lua_State* const L_, DeepPrelude* const o_) |
| 91 | static void LookupDeep(lua_State* L_) | ||
| 92 | { | 127 | { |
| 93 | STACK_GROW(L_, 1); | 128 | STACK_CHECK_START_REL(L_, 0); |
| 94 | STACK_CHECK_START_REL(L_, 1); // L_: a | 129 | o_->factory.deleteDeepObjectInternal(L_, o_); |
| 95 | kDeepLookupRegKey.pushValue(L_); // L_: a {} | 130 | STACK_CHECK(L_, 0); |
| 96 | if (!lua_isnil(L_, -1)) { | ||
| 97 | lua_insert(L_, -2); // L_: {} a | ||
| 98 | lua_rawget(L_, -2); // L_: {} b | ||
| 99 | } | ||
| 100 | lua_remove(L_, -2); // L_: a|b | ||
| 101 | STACK_CHECK(L_, 1); | ||
| 102 | } | 131 | } |
| 103 | 132 | ||
| 104 | // ################################################################################################# | 133 | // ################################################################################################# |
| 105 | 134 | ||
| 106 | // Return the registered factory for 'index' (deep userdata proxy), or nullptr if 'index' is not a deep userdata proxy. | 135 | // Return the registered factory for 'index' (deep userdata proxy), or nullptr if 'index' is not a deep userdata proxy. |
| 107 | [[nodiscard]] DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_) | 136 | [[nodiscard]] DeepFactory* DeepFactory::LookupFactory(lua_State* const L_, int const index_, LookupMode const mode_) |
| 108 | { | 137 | { |
| 109 | // when looking inside a keeper, we are 100% sure the object is a deep userdata | 138 | // when looking inside a keeper, we are 100% sure the object is a deep userdata |
| 110 | if (mode_ == LookupMode::FromKeeper) { | 139 | if (mode_ == LookupMode::FromKeeper) { |
| @@ -134,47 +163,6 @@ static void LookupDeep(lua_State* L_) | |||
| 134 | 163 | ||
| 135 | // ################################################################################################# | 164 | // ################################################################################################# |
| 136 | 165 | ||
| 137 | void DeepFactory::DeleteDeepObject(lua_State* L_, DeepPrelude* o_) | ||
| 138 | { | ||
| 139 | STACK_CHECK_START_REL(L_, 0); | ||
| 140 | o_->factory.deleteDeepObjectInternal(L_, o_); | ||
| 141 | STACK_CHECK(L_, 0); | ||
| 142 | } | ||
| 143 | |||
| 144 | // ################################################################################################# | ||
| 145 | |||
| 146 | /* | ||
| 147 | * void= mt.__gc( proxy_ud ) | ||
| 148 | * | ||
| 149 | * End of life for a proxy object; reduce the deep reference count and clean it up if reaches 0. | ||
| 150 | * | ||
| 151 | */ | ||
| 152 | [[nodiscard]] static int deep_userdata_gc(lua_State* L_) | ||
| 153 | { | ||
| 154 | DeepPrelude* const* const _proxy{ lua_tofulluserdata<DeepPrelude*>(L_, 1) }; | ||
| 155 | DeepPrelude* const _p{ *_proxy }; | ||
| 156 | |||
| 157 | // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded | ||
| 158 | // in that case, we are not multithreaded and locking isn't necessary anyway | ||
| 159 | bool const isLastRef{ _p->refcount.fetch_sub(1, std::memory_order_relaxed) == 1 }; | ||
| 160 | |||
| 161 | if (isLastRef) { | ||
| 162 | // retrieve wrapped __gc, if any | ||
| 163 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: self __gc? | ||
| 164 | if (!lua_isnil(L_, -1)) { | ||
| 165 | lua_insert(L_, -2); // L_: __gc self | ||
| 166 | lua_call(L_, 1, 0); // L_: | ||
| 167 | } else { | ||
| 168 | // need an empty stack in case we are GC_ing from a Keeper, so that empty stack checks aren't triggered | ||
| 169 | lua_pop(L_, 2); // L_: | ||
| 170 | } | ||
| 171 | DeepFactory::DeleteDeepObject(L_, _p); | ||
| 172 | } | ||
| 173 | return 0; | ||
| 174 | } | ||
| 175 | |||
| 176 | // ################################################################################################# | ||
| 177 | |||
| 178 | /* | 166 | /* |
| 179 | * Push a proxy userdata on the stack. | 167 | * Push a proxy userdata on the stack. |
| 180 | * raises an error in case of problem (error cannot happen with mode_ == LookupMode::ToKeeper) | 168 | * raises an error in case of problem (error cannot happen with mode_ == LookupMode::ToKeeper) |
| @@ -182,7 +170,7 @@ void DeepFactory::DeleteDeepObject(lua_State* L_, DeepPrelude* o_) | |||
| 182 | * Initializes necessary structures if it's the first time 'factory' is being used in | 170 | * Initializes necessary structures if it's the first time 'factory' is being used in |
| 183 | * this Lua state (metatable, registring it). Otherwise, increments the reference count. | 171 | * this Lua state (metatable, registring it). Otherwise, increments the reference count. |
| 184 | */ | 172 | */ |
| 185 | void DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, LookupMode mode_, lua_State* errL_) | 173 | void DeepFactory::PushDeepProxy(DestState const L_, DeepPrelude* const prelude_, int const nuv_, LookupMode const mode_, lua_State* const errL_) |
| 186 | { | 174 | { |
| 187 | STACK_CHECK_START_REL(L_, 0); | 175 | STACK_CHECK_START_REL(L_, 0); |
| 188 | kDeepProxyCacheRegKey.getSubTableMode(L_, "v"); // L_: DPC | 176 | kDeepProxyCacheRegKey.getSubTableMode(L_, "v"); // L_: DPC |
| @@ -208,8 +196,8 @@ void DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, L | |||
| 208 | prelude_->refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data | 196 | prelude_->refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data |
| 209 | 197 | ||
| 210 | // Get/create metatable for 'factory' (in this state) | 198 | // Get/create metatable for 'factory' (in this state) |
| 211 | DeepFactory& factory = prelude_->factory; | 199 | DeepFactory& _factory = prelude_->factory; |
| 212 | lua_pushlightuserdata(L_, std::bit_cast<void*>(&factory)); // L_: DPC proxy factory | 200 | lua_pushlightuserdata(L_, std::bit_cast<void*>(&_factory)); // L_: DPC proxy _factory |
| 213 | LookupDeep(L_); // L_: DPC proxy metatable|nil | 201 | LookupDeep(L_); // L_: DPC proxy metatable|nil |
| 214 | 202 | ||
| 215 | if (lua_isnil(L_, -1)) { // No metatable yet. | 203 | if (lua_isnil(L_, -1)) { // No metatable yet. |
| @@ -217,7 +205,7 @@ void DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, L | |||
| 217 | int const _oldtop{ lua_gettop(L_) }; | 205 | int const _oldtop{ lua_gettop(L_) }; |
| 218 | // 1 - make one and register it | 206 | // 1 - make one and register it |
| 219 | if (mode_ != LookupMode::ToKeeper) { | 207 | if (mode_ != LookupMode::ToKeeper) { |
| 220 | factory.createMetatable(L_); // L_: DPC proxy metatable | 208 | _factory.createMetatable(L_); // L_: DPC proxy metatable |
| 221 | if (lua_gettop(L_) - _oldtop != 1 || !lua_istable(L_, -1)) { | 209 | if (lua_gettop(L_) - _oldtop != 1 || !lua_istable(L_, -1)) { |
| 222 | // factory didn't push exactly 1 value, or the value it pushed is not a table: ERROR! | 210 | // factory didn't push exactly 1 value, or the value it pushed is not a table: ERROR! |
| 223 | raise_luaL_error(errL_, "Bad DeepFactory::createMetatable overload: unexpected pushed value"); | 211 | raise_luaL_error(errL_, "Bad DeepFactory::createMetatable overload: unexpected pushed value"); |
| @@ -232,18 +220,18 @@ void DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, L | |||
| 232 | if (lua_isnil(L_, -1)) { | 220 | if (lua_isnil(L_, -1)) { |
| 233 | // Add our own '__gc' method | 221 | // Add our own '__gc' method |
| 234 | lua_pop(L_, 1); // L_: DPC proxy metatable | 222 | lua_pop(L_, 1); // L_: DPC proxy metatable |
| 235 | lua_pushcfunction(L_, deep_userdata_gc); // L_: DPC proxy metatable deep_userdata_gc | 223 | lua_pushcfunction(L_, DeepGC); // L_: DPC proxy metatable DeepGC |
| 236 | } else { | 224 | } else { |
| 237 | // Add our own '__gc' method wrapping the original | 225 | // Add our own '__gc' method wrapping the original |
| 238 | lua_pushcclosure(L_, deep_userdata_gc, 1); // L_: DPC proxy metatable deep_userdata_gc | 226 | lua_pushcclosure(L_, DeepGC, 1); // L_: DPC proxy metatable DeepGC |
| 239 | } | 227 | } |
| 240 | lua_setfield(L_, -2, "__gc"); // L_: DPC proxy metatable | 228 | lua_setfield(L_, -2, "__gc"); // L_: DPC proxy metatable |
| 241 | 229 | ||
| 242 | // Memorize for later rounds | 230 | // Memorize for later rounds |
| 243 | factory.storeDeepLookup(L_); | 231 | _factory.storeDeepLookup(L_); |
| 244 | 232 | ||
| 245 | // 2 - cause the target state to require the module that exported the factory | 233 | // 2 - cause the target state to require the module that exported the factory |
| 246 | if (std::string_view const _modname{ factory.moduleName() }; !_modname.empty()) { // we actually got a module name | 234 | if (std::string_view const _modname{ _factory.moduleName() }; !_modname.empty()) { // we actually got a module name |
| 247 | // L.registry._LOADED exists without having registered the 'package' library. | 235 | // L.registry._LOADED exists without having registered the 'package' library. |
| 248 | lua_getglobal(L_, "require"); // L_: DPC proxy metatable require() | 236 | lua_getglobal(L_, "require"); // L_: DPC proxy metatable require() |
| 249 | // check that the module is already loaded (or being loaded, we are happy either way) | 237 | // check that the module is already loaded (or being loaded, we are happy either way) |
| @@ -310,7 +298,7 @@ void DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, L | |||
| 310 | * | 298 | * |
| 311 | * Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()' | 299 | * Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()' |
| 312 | */ | 300 | */ |
| 313 | int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const | 301 | int DeepFactory::pushDeepUserdata(DestState const L_, int const nuv_) const |
| 314 | { | 302 | { |
| 315 | STACK_GROW(L_, 1); | 303 | STACK_GROW(L_, 1); |
| 316 | STACK_CHECK_START_REL(L_, 0); | 304 | STACK_CHECK_START_REL(L_, 0); |
| @@ -343,12 +331,38 @@ int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const | |||
| 343 | // ################################################################################################# | 331 | // ################################################################################################# |
| 344 | 332 | ||
| 345 | /* | 333 | /* |
| 334 | * Sets up [-1]<->[-2] two-way lookups, and ensures the lookup table exists. | ||
| 335 | * Pops the both values off the stack. | ||
| 336 | */ | ||
| 337 | void DeepFactory::storeDeepLookup(lua_State* const L_) const | ||
| 338 | { | ||
| 339 | // the deep metatable is at the top of the stack // L_: mt | ||
| 340 | STACK_GROW(L_, 3); | ||
| 341 | STACK_CHECK_START_REL(L_, 0); // L_: mt | ||
| 342 | std::ignore = kDeepLookupRegKey.getSubTable(L_, 0, 0); // L_: mt {} | ||
| 343 | lua_pushvalue(L_, -2); // L_: mt {} mt | ||
| 344 | lua_pushlightuserdata(L_, std::bit_cast<void*>(this)); // L_: mt {} mt factory | ||
| 345 | lua_rawset(L_, -3); // L_: mt {} | ||
| 346 | STACK_CHECK(L_, 1); | ||
| 347 | |||
| 348 | lua_pushlightuserdata(L_, std::bit_cast<void*>(this)); // L_: mt {} factory | ||
| 349 | lua_pushvalue(L_, -3); // L_: mt {} factory mt | ||
| 350 | lua_rawset(L_, -3); // L_: mt {} | ||
| 351 | STACK_CHECK(L_, 1); | ||
| 352 | |||
| 353 | lua_pop(L_, 1); // L_: mt | ||
| 354 | STACK_CHECK(L_, 0); | ||
| 355 | } | ||
| 356 | |||
| 357 | // ################################################################################################# | ||
| 358 | |||
| 359 | /* | ||
| 346 | * Access deep userdata through a proxy. | 360 | * Access deep userdata through a proxy. |
| 347 | * | 361 | * |
| 348 | * Reference count is not changed, and access to the deep userdata is not | 362 | * Reference count is not changed, and access to the deep userdata is not |
| 349 | * serialized. It is the module's responsibility to prevent conflicting usage. | 363 | * serialized. It is the module's responsibility to prevent conflicting usage. |
| 350 | */ | 364 | */ |
| 351 | DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const | 365 | DeepPrelude* DeepFactory::toDeep(lua_State* const L_, int const index_) const |
| 352 | { | 366 | { |
| 353 | STACK_CHECK_START_REL(L_, 0); | 367 | STACK_CHECK_START_REL(L_, 0); |
| 354 | // ensure it is actually a deep userdata we created | 368 | // ensure it is actually a deep userdata we created |
| @@ -63,21 +63,22 @@ class DeepFactory | |||
| 63 | DeepFactory& operator=(DeepFactory const&&) = delete; | 63 | DeepFactory& operator=(DeepFactory const&&) = delete; |
| 64 | 64 | ||
| 65 | private: | 65 | private: |
| 66 | void storeDeepLookup(lua_State* L_) const; | ||
| 67 | // NVI: private overrides | 66 | // NVI: private overrides |
| 68 | [[nodiscard]] virtual DeepPrelude* newDeepObjectInternal(lua_State* L_) const = 0; | 67 | virtual void createMetatable(lua_State* const L_) const = 0; |
| 69 | virtual void deleteDeepObjectInternal(lua_State* L_, DeepPrelude* o_) const = 0; | 68 | virtual void deleteDeepObjectInternal(lua_State* const L_, DeepPrelude* const o_) const = 0; |
| 70 | virtual void createMetatable(lua_State* L_) const = 0; | 69 | [[nodiscard]] virtual DeepPrelude* newDeepObjectInternal(lua_State* const L_) const = 0; |
| 71 | [[nodiscard]] virtual std::string_view moduleName() const = 0; | 70 | [[nodiscard]] virtual std::string_view moduleName() const = 0; |
| 72 | 71 | ||
| 72 | private: | ||
| 73 | void storeDeepLookup(lua_State* const L_) const; | ||
| 74 | |||
| 73 | public: | 75 | public: |
| 74 | // NVI: public interface | 76 | // NVI: public interface |
| 75 | [[nodiscard]] int pushDeepUserdata(DestState L_, int nuv_) const; | 77 | static void DeleteDeepObject(lua_State* const L_, DeepPrelude* const o_); |
| 76 | [[nodiscard]] DeepPrelude* toDeep(lua_State* L_, int index_) const; | 78 | [[nodiscard]] static DeepFactory* LookupFactory(lua_State* const L_, int const index_, LookupMode const mode_); |
| 77 | static void DeleteDeepObject(lua_State* L_, DeepPrelude* o_); | 79 | static void PushDeepProxy(DestState const L_, DeepPrelude* const o_, int const nuv_, LookupMode const mode_, lua_State* const errL_); |
| 78 | static void PushDeepProxy(DestState L_, DeepPrelude* o_, int nuv_, LookupMode mode_, lua_State* errL_); | 80 | [[nodiscard]] int pushDeepUserdata(DestState const L_, int const nuv_) const; |
| 81 | [[nodiscard]] DeepPrelude* toDeep(lua_State* const L_, int const index_) const; | ||
| 79 | }; | 82 | }; |
| 80 | 83 | ||
| 81 | // ################################################################################################# | 84 | // ################################################################################################# |
| 82 | |||
| 83 | [[nodiscard]] DeepFactory* LookupFactory(lua_State* L_, int index_, LookupMode mode_); | ||
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp index 044b197..eddb309 100644 --- a/src/intercopycontext.cpp +++ b/src/intercopycontext.cpp | |||
| @@ -723,7 +723,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const | |||
| 723 | // Returns false if not a deep userdata, else true (unless an error occured) | 723 | // Returns false if not a deep userdata, else true (unless an error occured) |
| 724 | [[nodiscard]] bool InterCopyContext::tryCopyDeep() const | 724 | [[nodiscard]] bool InterCopyContext::tryCopyDeep() const |
| 725 | { | 725 | { |
| 726 | DeepFactory* const _factory{ LookupFactory(L1, L1_i, mode) }; | 726 | DeepFactory* const _factory{ DeepFactory::LookupFactory(L1, L1_i, mode) }; |
| 727 | if (_factory == nullptr) { | 727 | if (_factory == nullptr) { |
| 728 | return false; // not a deep userdata | 728 | return false; // not a deep userdata |
| 729 | } | 729 | } |
