aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/deep.cpp182
-rw-r--r--src/deep.h21
-rw-r--r--src/intercopycontext.cpp2
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 };
60static constexpr RegistryUniqueKey kDeepProxyCacheRegKey{ 0xEBCD49AE1A3DD35Eull }; 60static constexpr RegistryUniqueKey kDeepProxyCacheRegKey{ 0xEBCD49AE1A3DD35Eull };
61 61
62// ################################################################################################# 62// #################################################################################################
63// #################################################################################################
64namespace {
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 */
68void 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). 126void DeepFactory::DeleteDeepObject(lua_State* const L_, DeepPrelude* const o_)
91static 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
137void 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 */
185void DeepFactory::PushDeepProxy(DestState L_, DeepPrelude* prelude_, int nuv_, LookupMode mode_, lua_State* errL_) 173void 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 */
313int DeepFactory::pushDeepUserdata(DestState L_, int nuv_) const 301int 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 */
337void 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 */
351DeepPrelude* DeepFactory::toDeep(lua_State* L_, int index_) const 365DeepPrelude* 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
diff --git a/src/deep.h b/src/deep.h
index 4b52b51..17d3491 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -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 }