aboutsummaryrefslogtreecommitdiff
path: root/src/deep.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/deep.cpp')
-rw-r--r--src/deep.cpp149
1 files changed, 59 insertions, 90 deletions
diff --git a/src/deep.cpp b/src/deep.cpp
index d0b8123..780c86e 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -47,11 +47,11 @@ THE SOFTWARE.
47/*---=== Deep userdata ===---*/ 47/*---=== Deep userdata ===---*/
48 48
49/* 49/*
50* 'registry[REGKEY]' is a two-way lookup table for 'idfunc's and those type's 50* 'registry[REGKEY]' is a two-way lookup table for 'factory's and those type's
51* metatables: 51* metatables:
52* 52*
53* metatable -> idfunc 53* metatable -> factory
54* idfunc -> metatable 54* factory -> metatable
55*/ 55*/
56// crc64/we of string "DEEP_LOOKUP_KEY" generated at http://www.nitrxgen.net/hashgen/ 56// crc64/we of string "DEEP_LOOKUP_KEY" generated at http://www.nitrxgen.net/hashgen/
57static constexpr UniqueKey DEEP_LOOKUP_KEY{ 0x9fb9b4f3f633d83dull }; 57static constexpr UniqueKey DEEP_LOOKUP_KEY{ 0x9fb9b4f3f633d83dull };
@@ -84,8 +84,8 @@ static void set_deep_lookup(lua_State* L)
84// ################################################################################################ 84// ################################################################################################
85 85
86/* 86/*
87* Pops the key (metatable or idfunc) off the stack, and replaces with the 87* Pops the key (metatable or factory) off the stack, and replaces with the
88* deep lookup value (idfunc/metatable/nil). 88* deep lookup value (factory/metatable/nil).
89*/ 89*/
90static void get_deep_lookup(lua_State* L) 90static void get_deep_lookup(lua_State* L)
91{ 91{
@@ -104,21 +104,21 @@ static void get_deep_lookup(lua_State* L)
104// ################################################################################################ 104// ################################################################################################
105 105
106/* 106/*
107* Return the registered ID function for 'index' (deep userdata proxy), 107* Return the registered factory for 'index' (deep userdata proxy),
108* or nullptr if 'index' is not a deep userdata proxy. 108* or nullptr if 'index' is not a deep userdata proxy.
109*/ 109*/
110[[nodiscard]] static inline luaG_IdFunction get_idfunc(lua_State* L, int index, LookupMode mode_) 110[[nodiscard]] static inline DeepFactory* get_factory(lua_State* L, int index, LookupMode mode_)
111{ 111{
112 // when looking inside a keeper, we are 100% sure the object is a deep userdata 112 // when looking inside a keeper, we are 100% sure the object is a deep userdata
113 if (mode_ == LookupMode::FromKeeper) 113 if (mode_ == LookupMode::FromKeeper)
114 { 114 {
115 DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L, index) }; 115 DeepPrelude* const proxy{ *lua_tofulluserdata<DeepPrelude*>(L, index) };
116 // we can (and must) cast and fetch the internally stored idfunc 116 // we can (and must) cast and fetch the internally stored factory
117 return (*proxy)->idfunc; 117 return &proxy->m_factory;
118 } 118 }
119 else 119 else
120 { 120 {
121 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/idfunc database 121 // essentially we are making sure that the metatable of the object we want to copy is stored in our metatable/factory database
122 // it is the only way to ensure that the userdata is indeed a deep userdata! 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 123 // of course, we could just trust the caller, but we won't
124 STACK_GROW( L, 1); 124 STACK_GROW( L, 1);
@@ -129,10 +129,10 @@ static void get_deep_lookup(lua_State* L)
129 return nullptr; // no metatable: can't be a deep userdata object! 129 return nullptr; // no metatable: can't be a deep userdata object!
130 } 130 }
131 131
132 // replace metatable with the idfunc pointer, if it is actually a deep userdata 132 // replace metatable with the factory pointer, if it is actually a deep userdata
133 get_deep_lookup( L); // deep ... idfunc|nil 133 get_deep_lookup( L); // deep ... factory|nil
134 134
135 luaG_IdFunction const ret{ *lua_tolightuserdata<luaG_IdFunction>(L, -1) }; // nullptr if not a userdata 135 DeepFactory* const ret{ lua_tolightuserdata<DeepFactory>(L, -1) }; // nullptr if not a userdata
136 lua_pop( L, 1); 136 lua_pop( L, 1);
137 STACK_CHECK( L, 0); 137 STACK_CHECK( L, 0);
138 return ret; 138 return ret;
@@ -141,14 +141,10 @@ static void get_deep_lookup(lua_State* L)
141 141
142// ################################################################################################ 142// ################################################################################################
143 143
144void free_deep_prelude(lua_State* L, DeepPrelude* prelude_) 144void DeepFactory::DeleteDeepObject(lua_State* L, DeepPrelude* o_)
145{ 145{
146 ASSERT_L(prelude_->idfunc);
147 STACK_CHECK_START_REL(L, 0); 146 STACK_CHECK_START_REL(L, 0);
148 // Call 'idfunc( "delete", deep_ptr )' to make deep cleanup 147 o_->m_factory.deleteDeepObjectInternal(L, o_);
149 lua_pushlightuserdata( L, prelude_);
150 prelude_->idfunc( L, DeepOp::Delete);
151 lua_pop(L, 1);
152 STACK_CHECK(L, 0); 148 STACK_CHECK(L, 0);
153} 149}
154 150
@@ -162,8 +158,8 @@ void free_deep_prelude(lua_State* L, DeepPrelude* prelude_)
162 */ 158 */
163[[nodiscard]] static int deep_userdata_gc(lua_State* L) 159[[nodiscard]] static int deep_userdata_gc(lua_State* L)
164{ 160{
165 DeepPrelude** const proxy{ lua_tofulluserdata<DeepPrelude*>(L, 1) }; 161 DeepPrelude* const* const proxy{ lua_tofulluserdata<DeepPrelude*>(L, 1) };
166 DeepPrelude* p = *proxy; 162 DeepPrelude* const p{ *proxy };
167 163
168 // can work without a universe if creating a deep userdata from some external C module when Lanes isn't loaded 164 // 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 165 // in that case, we are not multithreaded and locking isn't necessary anyway
@@ -178,17 +174,9 @@ void free_deep_prelude(lua_State* L, DeepPrelude* prelude_)
178 lua_insert( L, -2); // __gc self 174 lua_insert( L, -2); // __gc self
179 lua_call( L, 1, 0); // 175 lua_call( L, 1, 0); //
180 } 176 }
181 // 'idfunc' expects a clean stack to work on 177 // we don't really know what remains on the stack at that point (depending on us finding a __gc or not), but we don't care
182 lua_settop( L, 0); 178 DeepFactory::DeleteDeepObject(L, p);
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 } 179 }
191 *proxy = nullptr; // make sure we don't use it any more, just in case
192 return 0; 180 return 0;
193} 181}
194 182
@@ -196,14 +184,14 @@ void free_deep_prelude(lua_State* L, DeepPrelude* prelude_)
196 184
197/* 185/*
198 * Push a proxy userdata on the stack. 186 * 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 187 * returns nullptr if ok, else some error string related to bad factory behavior or module require problem
200 * (error cannot happen with mode_ == LookupMode::ToKeeper) 188 * (error cannot happen with mode_ == LookupMode::ToKeeper)
201 * 189 *
202 * Initializes necessary structures if it's the first time 'idfunc' is being 190 * Initializes necessary structures if it's the first time 'factory' is being
203 * used in this Lua state (metatable, registring it). Otherwise, increments the 191 * used in this Lua state (metatable, registring it). Otherwise, increments the
204 * reference count. 192 * reference count.
205 */ 193 */
206char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_) 194char const* DeepFactory::PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_)
207{ 195{
208 // Check if a proxy already exists 196 // Check if a proxy already exists
209 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC 197 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
@@ -228,24 +216,25 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
228 *proxy = prelude; 216 *proxy = prelude;
229 prelude->m_refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data 217 prelude->m_refcount.fetch_add(1, std::memory_order_relaxed); // one more proxy pointing to this deep data
230 218
231 // Get/create metatable for 'idfunc' (in this state) 219 // Get/create metatable for 'factory' (in this state)
232 lua_pushlightuserdata( L, std::bit_cast<void*>(prelude->idfunc)); // DPC proxy idfunc 220 DeepFactory& factory = prelude->m_factory;
221 lua_pushlightuserdata( L, std::bit_cast<void*>(&factory)); // DPC proxy factory
233 get_deep_lookup( L); // DPC proxy metatable? 222 get_deep_lookup( L); // DPC proxy metatable?
234 223
235 if( lua_isnil( L, -1)) // // No metatable yet. 224 if( lua_isnil( L, -1)) // // No metatable yet.
236 { 225 {
237 char const* modname;
238 int oldtop = lua_gettop( L); // DPC proxy nil 226 int oldtop = lua_gettop( L); // DPC proxy nil
239 lua_pop( L, 1); // DPC proxy 227 lua_pop( L, 1); // DPC proxy
240 // 1 - make one and register it 228 // 1 - make one and register it
241 if (mode_ != LookupMode::ToKeeper) 229 if (mode_ != LookupMode::ToKeeper)
242 { 230 {
243 (void) prelude->idfunc( L, DeepOp::Metatable); // DPC proxy metatable 231 factory.createMetatable(L); // DPC proxy metatable
244 if( lua_gettop( L) - oldtop != 0 || !lua_istable( L, -1)) 232 if (lua_gettop(L) - oldtop != 0 || !lua_istable(L, -1))
245 { 233 {
234 // factory didn't push exactly 1 value, or the value it pushed is not a table: ERROR!
246 lua_settop( L, oldtop); // DPC proxy X 235 lua_settop( L, oldtop); // DPC proxy X
247 lua_pop( L, 3); // 236 lua_pop( L, 3); //
248 return "Bad idfunc(eOP_metatable): unexpected pushed value"; 237 return "Bad DeepFactory::createMetatable overload: unexpected pushed value";
249 } 238 }
250 // if the metatable contains a __gc, we will call it from our own 239 // if the metatable contains a __gc, we will call it from our own
251 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc 240 lua_getfield( L, -1, "__gc"); // DPC proxy metatable __gc
@@ -271,22 +260,11 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
271 260
272 // Memorize for later rounds 261 // Memorize for later rounds
273 lua_pushvalue( L, -1); // DPC proxy metatable metatable 262 lua_pushvalue( L, -1); // DPC proxy metatable metatable
274 lua_pushlightuserdata( L, std::bit_cast<void*>(prelude->idfunc)); // DPC proxy metatable metatable idfunc 263 lua_pushlightuserdata(L, std::bit_cast<void*>(&factory)); // DPC proxy metatable metatable factory
275 set_deep_lookup( L); // DPC proxy metatable 264 set_deep_lookup( L); // DPC proxy metatable
276 265
277 // 2 - cause the target state to require the module that exported the idfunc 266 // 2 - cause the target state to require the module that exported the factory
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 267 if (char const* const modname{ factory.moduleName() }; modname) // we actually got a module name
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 { 268 {
291 // L.registry._LOADED exists without having registered the 'package' library. 269 // L.registry._LOADED exists without having registered the 'package' library.
292 lua_getglobal( L, "require"); // DPC proxy metatable require() 270 lua_getglobal( L, "require"); // DPC proxy metatable require()
@@ -309,7 +287,7 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
309 if( require_result != LUA_OK) 287 if( require_result != LUA_OK)
310 { 288 {
311 // failed, return the error message 289 // failed, return the error message
312 lua_pushfstring( L, "error while requiring '%s' identified by idfunc(eOP_module): ", modname); 290 lua_pushfstring( L, "error while requiring '%s' identified by DeepFactory::moduleName: ", modname);
313 lua_insert( L, -2); // DPC proxy metatable prefix error 291 lua_insert( L, -2); // DPC proxy metatable prefix error
314 lua_concat( L, 2); // DPC proxy metatable error 292 lua_concat( L, 2); // DPC proxy metatable error
315 return lua_tostring( L, -1); 293 return lua_tostring( L, -1);
@@ -323,7 +301,7 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
323 else // no L.registry._LOADED; can this ever happen? 301 else // no L.registry._LOADED; can this ever happen?
324 { 302 {
325 lua_pop( L, 6); // 303 lua_pop( L, 6); //
326 return "unexpected error while requiring a module identified by idfunc(eOP_module)"; 304 return "unexpected error while requiring a module identified by DeepFactory::moduleName";
327 } 305 }
328 } 306 }
329 else // a module name, but no require() function :-( 307 else // a module name, but no require() function :-(
@@ -334,7 +312,7 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
334 } 312 }
335 } 313 }
336 STACK_CHECK(L, 2); // DPC proxy metatable 314 STACK_CHECK(L, 2); // DPC proxy metatable
337 ASSERT_L(lua_type(L, -2) == LUA_TUSERDATA); 315 ASSERT_L(lua_type_as_enum(L, -2) == LuaType::USERDATA);
338 ASSERT_L(lua_istable( L, -1)); 316 ASSERT_L(lua_istable( L, -1));
339 lua_setmetatable( L, -2); // DPC proxy 317 lua_setmetatable( L, -2); // DPC proxy
340 318
@@ -343,7 +321,7 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
343 lua_pushvalue( L, -2); // DPC proxy deep proxy 321 lua_pushvalue( L, -2); // DPC proxy deep proxy
344 lua_rawset( L, -4); // DPC proxy 322 lua_rawset( L, -4); // DPC proxy
345 lua_remove( L, -2); // proxy 323 lua_remove( L, -2); // proxy
346 ASSERT_L(lua_type(L, -1) == LUA_TUSERDATA); 324 ASSERT_L(lua_type_as_enum(L, -1) == LuaType::USERDATA);
347 STACK_CHECK(L, 0); 325 STACK_CHECK(L, 0);
348 return nullptr; 326 return nullptr;
349} 327}
@@ -353,56 +331,47 @@ char const* push_deep_proxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode m
353/* 331/*
354* Create a deep userdata 332* Create a deep userdata
355* 333*
356* proxy_ud= deep_userdata( idfunc [, ...] ) 334* proxy_ud= deep_userdata( [...] )
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* 335*
363* lightuserdata = idfunc( DeepOp::New [, ...] ) -- creates a new deep data instance 336* Creates a deep userdata entry of the type defined by the factory.
364* void = idfunc( DeepOp::Delete, lightuserdata ) -- releases a deep data instance 337* Parameters found on the stack are left as is and passed on to DeepFactory::newDeepObjectInternal.
365* tbl = idfunc( DeepOp::Metatable ) -- gives metatable for userdata proxies
366* 338*
367* Reference counting and true userdata proxying are taken care of for the 339* Reference counting and true userdata proxying are taken care of for the actual data type.
368* actual data type.
369* 340*
370* Types using the deep userdata system (and only those!) can be passed between 341* Types using the deep userdata system (and only those!) can be passed between
371* separate Lua states via 'luaG_inter_move()'. 342* separate Lua states via 'luaG_inter_move()'.
372* 343*
373* Returns: 'proxy' userdata for accessing the deep data via 'luaG_todeep()' 344* Returns: 'proxy' userdata for accessing the deep data via 'DeepFactory::toDeep()'
374*/ 345*/
375int luaG_newdeepuserdata(Dest L, luaG_IdFunction idfunc, int nuv_) 346int DeepFactory::pushDeepUserdata(Dest L, int nuv_) const
376{ 347{
377 STACK_GROW( L, 1); 348 STACK_GROW( L, 1);
378 STACK_CHECK_START_REL(L, 0); 349 STACK_CHECK_START_REL(L, 0);
379 int const oldtop{ lua_gettop(L) }; 350 int const oldtop{ lua_gettop(L) };
380 DeepPrelude* const prelude{ static_cast<DeepPrelude*>(idfunc(L, DeepOp::New)) }; 351 DeepPrelude* const prelude{ newDeepObjectInternal(L) };
381 if (prelude == nullptr) 352 if (prelude == nullptr)
382 { 353 {
383 return luaL_error( L, "idfunc(DeepOp::New) failed to create deep userdata (out of memory)"); 354 return luaL_error( L, "DeepFactory::newDeepObjectInternal failed to create deep userdata (out of memory)");
384 } 355 }
385 356
386 if( prelude->magic != DEEP_VERSION) 357 if( prelude->m_magic != DEEP_VERSION)
387 { 358 {
388 // just in case, don't leak the newly allocated deep userdata object 359 // just in case, don't leak the newly allocated deep userdata object
389 lua_pushlightuserdata( L, prelude); 360 deleteDeepObjectInternal(L, prelude);
390 idfunc( L, DeepOp::Delete); 361 return luaL_error( L, "Bad Deep Factory: DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation");
391 return luaL_error( L, "Bad idfunc(DeepOp::New): DEEP_VERSION is incorrect, rebuild your implementation with the latest deep implementation");
392 } 362 }
393 363
394 ASSERT_L(prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'push_deep_proxy' will lift it to 1 364 ASSERT_L(prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'DeepFactory::PushDeepProxy' will lift it to 1
395 prelude->idfunc = idfunc; 365 ASSERT_L(&prelude->m_factory == this);
396 366
397 if( lua_gettop( L) - oldtop != 0) 367 if( lua_gettop( L) - oldtop != 0)
398 { 368 {
399 // just in case, don't leak the newly allocated deep userdata object 369 // just in case, don't leak the newly allocated deep userdata object
400 lua_pushlightuserdata( L, prelude); 370 deleteDeepObjectInternal(L, prelude);
401 idfunc( L, DeepOp::Delete); 371 return luaL_error(L, "Bad DeepFactory::newDeepObjectInternal overload: should not push anything on the stack");
402 return luaL_error( L, "Bad idfunc(DeepOp::New): should not push anything on the stack");
403 } 372 }
404 373
405 char const* const errmsg{ push_deep_proxy(L, prelude, nuv_, LookupMode::LaneBody) }; // proxy 374 char const* const errmsg{ DeepFactory::PushDeepProxy(L, prelude, nuv_, LookupMode::LaneBody) }; // proxy
406 if (errmsg != nullptr) 375 if (errmsg != nullptr)
407 { 376 {
408 return luaL_error( L, errmsg); 377 return luaL_error( L, errmsg);
@@ -419,11 +388,11 @@ int luaG_newdeepuserdata(Dest L, luaG_IdFunction idfunc, int nuv_)
419* Reference count is not changed, and access to the deep userdata is not 388* 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. 389* serialized. It is the module's responsibility to prevent conflicting usage.
421*/ 390*/
422DeepPrelude* luaG_todeep(lua_State* L, luaG_IdFunction idfunc, int index) 391DeepPrelude* DeepFactory::toDeep(lua_State* L, int index) const
423{ 392{
424 STACK_CHECK_START_REL(L, 0); 393 STACK_CHECK_START_REL(L, 0);
425 // ensure it is actually a deep userdata 394 // ensure it is actually a deep userdata we created
426 if (get_idfunc(L, index, LookupMode::LaneBody) != idfunc) 395 if (get_factory(L, index, LookupMode::LaneBody) != this)
427 { 396 {
428 return nullptr; // no metatable, or wrong kind 397 return nullptr; // no metatable, or wrong kind
429 } 398 }
@@ -444,8 +413,8 @@ DeepPrelude* luaG_todeep(lua_State* L, luaG_IdFunction idfunc, int index)
444 */ 413 */
445bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_) 414bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode mode_, char const* upName_)
446{ 415{
447 luaG_IdFunction const idfunc { get_idfunc(L, i, mode_) }; 416 DeepFactory* const factory { get_factory(L, i, mode_) };
448 if (idfunc == nullptr) 417 if (factory == nullptr)
449 { 418 {
450 return false; // not a deep userdata 419 return false; // not a deep userdata
451 } 420 }
@@ -463,7 +432,7 @@ bool copydeep(Universe* U, Dest L2, int L2_cache_i, Source L, int i, LookupMode
463 lua_pop( L, 1); // ... u [uv]* 432 lua_pop( L, 1); // ... u [uv]*
464 STACK_CHECK( L, nuv); 433 STACK_CHECK( L, nuv);
465 434
466 char const* errmsg{ push_deep_proxy(L2, *lua_tofulluserdata<DeepPrelude*>(L, i), nuv, mode_) }; // u 435 char const* errmsg{ DeepFactory::PushDeepProxy(L2, *lua_tofulluserdata<DeepPrelude*>(L, i), nuv, mode_) }; // u
467 436
468 // transfer all uservalues of the source in the destination 437 // transfer all uservalues of the source in the destination
469 { 438 {