aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBenoit Germain <benoit.germain@ubisoft.com>2024-04-16 18:06:02 +0200
committerBenoit Germain <benoit.germain@ubisoft.com>2024-04-16 18:06:02 +0200
commitbbb4f99918d00308b52af3289c29624bfeb0066b (patch)
treeaebfba19ae4bb2bddc2beaa18f80a64d254b4ae6 /src
parentd6f5a7795360e3c2a6fc2d424904b6daa1f2accd (diff)
downloadlanes-bbb4f99918d00308b52af3289c29624bfeb0066b.tar.gz
lanes-bbb4f99918d00308b52af3289c29624bfeb0066b.tar.bz2
lanes-bbb4f99918d00308b52af3289c29624bfeb0066b.zip
some dead code elimination and other trifles
Diffstat (limited to 'src')
-rw-r--r--src/cancel.cpp38
-rw-r--r--src/cancel.h12
-rw-r--r--src/compat.cpp39
-rw-r--r--src/deep.cpp56
-rw-r--r--src/deep.h2
-rw-r--r--src/keeper.cpp60
-rw-r--r--src/lanes.cpp329
-rw-r--r--src/lanes_private.h2
-rw-r--r--src/macros_and_utils.h4
-rw-r--r--src/state.cpp36
-rw-r--r--src/state.h4
-rw-r--r--src/threading.cpp20
-rw-r--r--src/threading.h18
-rw-r--r--src/tools.cpp669
-rw-r--r--src/tools.h48
-rw-r--r--src/uniquekey.h2
-rw-r--r--src/universe.cpp10
-rw-r--r--src/universe.h13
18 files changed, 634 insertions, 728 deletions
diff --git a/src/cancel.cpp b/src/cancel.cpp
index b3e52b6..fe1f3e6 100644
--- a/src/cancel.cpp
+++ b/src/cancel.cpp
@@ -39,8 +39,8 @@ THE SOFTWARE.
39#include "threading.h" 39#include "threading.h"
40#include "tools.h" 40#include "tools.h"
41 41
42// ################################################################################################ 42// #################################################################################################
43// ################################################################################################ 43// #################################################################################################
44 44
45/* 45/*
46* Check if the thread in question ('L') has been signalled for cancel. 46* Check if the thread in question ('L') has been signalled for cancel.
@@ -58,7 +58,7 @@ THE SOFTWARE.
58 return lane ? lane->cancel_request : CancelRequest::None; 58 return lane ? lane->cancel_request : CancelRequest::None;
59} 59}
60 60
61// ################################################################################################ 61// #################################################################################################
62 62
63//--- 63//---
64// bool = cancel_test() 64// bool = cancel_test()
@@ -66,15 +66,15 @@ THE SOFTWARE.
66// Available inside the global namespace of lanes 66// Available inside the global namespace of lanes
67// returns a boolean saying if a cancel request is pending 67// returns a boolean saying if a cancel request is pending
68// 68//
69LUAG_FUNC( cancel_test) 69LUAG_FUNC(cancel_test)
70{ 70{
71 CancelRequest test{ cancel_test(L) }; 71 CancelRequest test{ cancel_test(L) };
72 lua_pushboolean(L, test != CancelRequest::None); 72 lua_pushboolean(L, test != CancelRequest::None);
73 return 1; 73 return 1;
74} 74}
75 75
76// ################################################################################################ 76// #################################################################################################
77// ################################################################################################ 77// #################################################################################################
78 78
79[[nodiscard]] static void cancel_hook(lua_State* L, [[maybe_unused]] lua_Debug* ar) 79[[nodiscard]] static void cancel_hook(lua_State* L, [[maybe_unused]] lua_Debug* ar)
80{ 80{
@@ -86,8 +86,8 @@ LUAG_FUNC( cancel_test)
86 } 86 }
87} 87}
88 88
89// ################################################################################################ 89// #################################################################################################
90// ################################################################################################ 90// #################################################################################################
91 91
92//--- 92//---
93// = thread_cancel( lane_ud [,timeout_secs=0.0] [,wake_lindas_bool=false] ) 93// = thread_cancel( lane_ud [,timeout_secs=0.0] [,wake_lindas_bool=false] )
@@ -106,7 +106,7 @@ LUAG_FUNC( cancel_test)
106// false if the cancellation timed out, or a kill was needed. 106// false if the cancellation timed out, or a kill was needed.
107// 107//
108 108
109// ################################################################################################ 109// #################################################################################################
110 110
111[[nodiscard]] static CancelResult thread_cancel_soft(Lane* lane_, lua_Duration duration_, bool wake_lane_) 111[[nodiscard]] static CancelResult thread_cancel_soft(Lane* lane_, lua_Duration duration_, bool wake_lane_)
112{ 112{
@@ -124,7 +124,7 @@ LUAG_FUNC( cancel_test)
124 return lane_->waitForCompletion(duration_) ? CancelResult::Cancelled : CancelResult::Timeout; 124 return lane_->waitForCompletion(duration_) ? CancelResult::Cancelled : CancelResult::Timeout;
125} 125}
126 126
127// ################################################################################################ 127// #################################################################################################
128 128
129[[nodiscard]] static CancelResult thread_cancel_hard(Lane* lane_, lua_Duration duration_, bool wake_lane_) 129[[nodiscard]] static CancelResult thread_cancel_hard(Lane* lane_, lua_Duration duration_, bool wake_lane_)
130{ 130{
@@ -143,7 +143,7 @@ LUAG_FUNC( cancel_test)
143 return result; 143 return result;
144} 144}
145 145
146// ################################################################################################ 146// #################################################################################################
147 147
148CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hook_count_, lua_Duration duration_, bool wake_lane_) 148CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hook_count_, lua_Duration duration_, bool wake_lane_)
149{ 149{
@@ -169,8 +169,8 @@ CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hook_count_, lua_Durat
169 return thread_cancel_hard(lane_, duration_, wake_lane_); 169 return thread_cancel_hard(lane_, duration_, wake_lane_);
170} 170}
171 171
172// ################################################################################################ 172// #################################################################################################
173// ################################################################################################ 173// #################################################################################################
174 174
175CancelOp which_cancel_op(char const* op_string_) 175CancelOp which_cancel_op(char const* op_string_)
176{ 176{
@@ -202,7 +202,7 @@ CancelOp which_cancel_op(char const* op_string_)
202 return op; 202 return op;
203} 203}
204 204
205// ################################################################################################ 205// #################################################################################################
206 206
207[[nodiscard]] static CancelOp which_cancel_op(lua_State* L, int idx_) 207[[nodiscard]] static CancelOp which_cancel_op(lua_State* L, int idx_)
208{ 208{
@@ -220,12 +220,12 @@ CancelOp which_cancel_op(char const* op_string_)
220 return CancelOp::Hard; 220 return CancelOp::Hard;
221} 221}
222 222
223// ################################################################################################ 223// #################################################################################################
224 224
225// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lindas]) 225// bool[,reason] = lane_h:cancel( [mode, hookcount] [, timeout] [, wake_lindas])
226LUAG_FUNC(thread_cancel) 226LUAG_FUNC(thread_cancel)
227{ 227{
228 Lane* const lane{ lua_toLane(L, 1) }; 228 Lane* const lane{ ToLane(L, 1) };
229 CancelOp const op{ which_cancel_op(L, 2) }; // this removes the op string from the stack 229 CancelOp const op{ which_cancel_op(L, 2) }; // this removes the op string from the stack
230 230
231 int hook_count{ 0 }; 231 int hook_count{ 0 };
@@ -235,7 +235,7 @@ LUAG_FUNC(thread_cancel)
235 lua_remove(L, 2); // argument is processed, remove it 235 lua_remove(L, 2); // argument is processed, remove it
236 if (hook_count < 1) 236 if (hook_count < 1)
237 { 237 {
238 return luaL_error(L, "hook count cannot be < 1"); 238 return luaL_error(L, "hook count cannot be < 1"); // doesn't return
239 } 239 }
240 } 240 }
241 241
@@ -246,7 +246,7 @@ LUAG_FUNC(thread_cancel)
246 lua_remove(L, 2); // argument is processed, remove it 246 lua_remove(L, 2); // argument is processed, remove it
247 if (wait_timeout.count() < 0.0) 247 if (wait_timeout.count() < 0.0)
248 { 248 {
249 return luaL_error(L, "cancel timeout cannot be < 0"); 249 return luaL_error(L, "cancel timeout cannot be < 0"); // doesn't return
250 } 250 }
251 } 251 }
252 // we wake by default in "hard" mode (remember that hook is hard too), but this can be turned off if desired 252 // we wake by default in "hard" mode (remember that hook is hard too), but this can be turned off if desired
@@ -255,7 +255,7 @@ LUAG_FUNC(thread_cancel)
255 { 255 {
256 if (!lua_isboolean(L, 2)) 256 if (!lua_isboolean(L, 2))
257 { 257 {
258 return luaL_error(L, "wake_lindas parameter is not a boolean"); 258 return luaL_error(L, "wake_lindas parameter is not a boolean"); // doesn't return
259 } 259 }
260 wake_lane = lua_toboolean(L, 2); 260 wake_lane = lua_toboolean(L, 2);
261 lua_remove(L, 2); // argument is processed, remove it 261 lua_remove(L, 2); // argument is processed, remove it
diff --git a/src/cancel.h b/src/cancel.h
index 10a9804..8cff1c9 100644
--- a/src/cancel.h
+++ b/src/cancel.h
@@ -10,12 +10,10 @@ extern "C" {
10} 10}
11#endif // __cplusplus 11#endif // __cplusplus
12 12
13#include "uniquekey.h"
14#include "macros_and_utils.h" 13#include "macros_and_utils.h"
14#include "uniquekey.h"
15 15
16#include <chrono> 16// #################################################################################################
17
18// ################################################################################################
19 17
20class Lane; // forward 18class Lane; // forward
21 19
@@ -59,10 +57,10 @@ static constexpr UniqueKey CANCEL_ERROR{ 0xe97d41626cc97577ull, "lanes.cancel_er
59 raise_lua_error(L); // doesn't return 57 raise_lua_error(L); // doesn't return
60} 58}
61 59
62// ################################################################################################ 60// #################################################################################################
63// ################################################################################################ 61// #################################################################################################
64 62
65LUAG_FUNC(cancel_test); 63LUAG_FUNC(cancel_test);
66LUAG_FUNC(thread_cancel); 64LUAG_FUNC(thread_cancel);
67 65
68// ################################################################################################ 66// #################################################################################################
diff --git a/src/compat.cpp b/src/compat.cpp
index 73d0f6b..9f652a7 100644
--- a/src/compat.cpp
+++ b/src/compat.cpp
@@ -1,20 +1,17 @@
1/* 1// #################################################################################################
2 * ############################################################################################### 2// ###################################### Lua 5.1 / 5.2 / 5.3 ######################################
3 * ####################################### Lua 5.1/5.2/5.3 ####################################### 3// #################################################################################################
4 * ############################################################################################### 4
5 */
6#include "compat.h" 5#include "compat.h"
7#include "macros_and_utils.h" 6#include "macros_and_utils.h"
8 7
9/* 8// #################################################################################################
10** Copied from Lua 5.2 loadlib.c 9// #################################################################################################
11*/
12// ################################################################################################
13// ################################################################################################
14#if LUA_VERSION_NUM == 501 10#if LUA_VERSION_NUM == 501
15// ################################################################################################ 11// #################################################################################################
16// ################################################################################################ 12// #################################################################################################
17 13
14// Copied from Lua 5.2 loadlib.c
18static int luaL_getsubtable(lua_State* L, int idx, const char* fname) 15static int luaL_getsubtable(lua_State* L, int idx, const char* fname)
19{ 16{
20 lua_getfield(L, idx, fname); 17 lua_getfield(L, idx, fname);
@@ -31,7 +28,7 @@ static int luaL_getsubtable(lua_State* L, int idx, const char* fname)
31 } 28 }
32} 29}
33 30
34// ################################################################################################ 31// #################################################################################################
35 32
36void luaL_requiref(lua_State *L, const char *modname, lua_CFunction openf, int glb) 33void luaL_requiref(lua_State *L, const char *modname, lua_CFunction openf, int glb)
37{ 34{
@@ -50,11 +47,11 @@ void luaL_requiref(lua_State *L, const char *modname, lua_CFunction openf, int g
50} 47}
51#endif // LUA_VERSION_NUM 48#endif // LUA_VERSION_NUM
52 49
53// ################################################################################################ 50// #################################################################################################
54// ################################################################################################ 51// #################################################################################################
55#if LUA_VERSION_NUM < 504 52#if LUA_VERSION_NUM < 504
56// ################################################################################################ 53// #################################################################################################
57// ################################################################################################ 54// #################################################################################################
58 55
59void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue) 56void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue)
60{ 57{
@@ -62,13 +59,13 @@ void* lua_newuserdatauv( lua_State* L, size_t sz, int nuvalue)
62 return lua_newuserdata(L, sz); 59 return lua_newuserdata(L, sz);
63} 60}
64 61
65// ################################################################################################ 62// #################################################################################################
66 63
67// push on stack uservalue #n of full userdata at idx 64// push on stack uservalue #n of full userdata at idx
68int lua_getiuservalue(lua_State* L, int idx, int n) 65int lua_getiuservalue(lua_State* L, int idx, int n)
69{ 66{
70 // full userdata can have only 1 uservalue before 5.4 67 // full userdata can have only 1 uservalue before 5.4
71 if( n > 1) 68 if (n > 1)
72 { 69 {
73 lua_pushnil(L); 70 lua_pushnil(L);
74 return LUA_TNONE; 71 return LUA_TNONE;
@@ -91,13 +88,13 @@ int lua_getiuservalue(lua_State* L, int idx, int n)
91 return lua_type(L, -1); 88 return lua_type(L, -1);
92} 89}
93 90
94// ################################################################################################ 91// #################################################################################################
95 92
96// Pops a value from the stack and sets it as the new n-th user value associated to the full userdata at the given index. 93// Pops a value from the stack and sets it as the new n-th user value associated to the full userdata at the given index.
97// Returns 0 if the userdata does not have that value. 94// Returns 0 if the userdata does not have that value.
98int lua_setiuservalue(lua_State* L, int idx, int n) 95int lua_setiuservalue(lua_State* L, int idx, int n)
99{ 96{
100 if( n > 1 97 if (n > 1
101#if LUA_VERSION_NUM == 501 98#if LUA_VERSION_NUM == 501
102 || lua_type(L, -1) != LUA_TTABLE 99 || lua_type(L, -1) != LUA_TTABLE
103#endif 100#endif
diff --git a/src/deep.cpp b/src/deep.cpp
index 3eb38dc..3326f98 100644
--- a/src/deep.cpp
+++ b/src/deep.cpp
@@ -81,7 +81,7 @@ static void set_deep_lookup(lua_State* L)
81 STACK_CHECK( L, 0); 81 STACK_CHECK( L, 0);
82} 82}
83 83
84// ################################################################################################ 84// #################################################################################################
85 85
86/* 86/*
87* Pops the key (metatable or factory) off the stack, and replaces with the 87* Pops the key (metatable or factory) off the stack, and replaces with the
@@ -92,7 +92,7 @@ static void get_deep_lookup(lua_State* L)
92 STACK_GROW( L, 1); 92 STACK_GROW( L, 1);
93 STACK_CHECK_START_REL(L, 1); // a 93 STACK_CHECK_START_REL(L, 1); // a
94 DEEP_LOOKUP_KEY.pushValue(L); // a {} 94 DEEP_LOOKUP_KEY.pushValue(L); // a {}
95 if( !lua_isnil( L, -1)) 95 if (!lua_isnil( L, -1))
96 { 96 {
97 lua_insert( L, -2); // {} a 97 lua_insert( L, -2); // {} a
98 lua_rawget( L, -2); // {} b 98 lua_rawget( L, -2); // {} b
@@ -101,7 +101,7 @@ static void get_deep_lookup(lua_State* L)
101 STACK_CHECK( L, 1); 101 STACK_CHECK( L, 1);
102} 102}
103 103
104// ################################################################################################ 104// #################################################################################################
105 105
106/* 106/*
107* Return the registered factory for 'index' (deep userdata proxy), 107* Return the registered factory for 'index' (deep userdata proxy),
@@ -124,7 +124,7 @@ static void get_deep_lookup(lua_State* L)
124 STACK_GROW( L, 1); 124 STACK_GROW( L, 1);
125 STACK_CHECK_START_REL(L, 0); 125 STACK_CHECK_START_REL(L, 0);
126 126
127 if( !lua_getmetatable( L, index)) // deep ... metatable? 127 if (!lua_getmetatable( L, index)) // deep ... metatable?
128 { 128 {
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 }
@@ -139,7 +139,7 @@ static void get_deep_lookup(lua_State* L)
139 } 139 }
140} 140}
141 141
142// ################################################################################################ 142// #################################################################################################
143 143
144void DeepFactory::DeleteDeepObject(lua_State* L, DeepPrelude* o_) 144void DeepFactory::DeleteDeepObject(lua_State* L, DeepPrelude* o_)
145{ 145{
@@ -148,7 +148,7 @@ void DeepFactory::DeleteDeepObject(lua_State* L, DeepPrelude* o_)
148 STACK_CHECK(L, 0); 148 STACK_CHECK(L, 0);
149} 149}
150 150
151// ################################################################################################ 151// #################################################################################################
152 152
153/* 153/*
154 * void= mt.__gc( proxy_ud ) 154 * void= mt.__gc( proxy_ud )
@@ -169,7 +169,7 @@ void DeepFactory::DeleteDeepObject(lua_State* L, DeepPrelude* o_)
169 { 169 {
170 // retrieve wrapped __gc 170 // retrieve wrapped __gc
171 lua_pushvalue( L, lua_upvalueindex( 1)); // self __gc? 171 lua_pushvalue( L, lua_upvalueindex( 1)); // self __gc?
172 if( !lua_isnil( L, -1)) 172 if (!lua_isnil( L, -1))
173 { 173 {
174 lua_insert( L, -2); // __gc self 174 lua_insert( L, -2); // __gc self
175 lua_call( L, 1, 0); // 175 lua_call( L, 1, 0); //
@@ -180,7 +180,7 @@ void DeepFactory::DeleteDeepObject(lua_State* L, DeepPrelude* o_)
180 return 0; 180 return 0;
181} 181}
182 182
183// ################################################################################################ 183// #################################################################################################
184 184
185/* 185/*
186 * Push a proxy userdata on the stack. 186 * Push a proxy userdata on the stack.
@@ -194,25 +194,25 @@ void DeepFactory::DeleteDeepObject(lua_State* L, DeepPrelude* o_)
194char const* DeepFactory::PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_) 194char const* DeepFactory::PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, LookupMode mode_)
195{ 195{
196 // Check if a proxy already exists 196 // Check if a proxy already exists
197 push_registry_subtable_mode( L, DEEP_PROXY_CACHE_KEY, "v"); // DPC 197 push_registry_subtable_mode(L, DEEP_PROXY_CACHE_KEY, "v"); // DPC
198 lua_pushlightuserdata( L, prelude); // DPC deep 198 lua_pushlightuserdata(L, prelude); // DPC deep
199 lua_rawget( L, -2); // DPC proxy 199 lua_rawget(L, -2); // DPC proxy
200 if ( !lua_isnil( L, -1)) 200 if (!lua_isnil(L, -1))
201 { 201 {
202 lua_remove( L, -2); // proxy 202 lua_remove(L, -2); // proxy
203 return nullptr; 203 return nullptr;
204 } 204 }
205 else 205 else
206 { 206 {
207 lua_pop( L, 1); // DPC 207 lua_pop(L, 1); // DPC
208 } 208 }
209 209
210 STACK_GROW( L, 7); 210 STACK_GROW(L, 7);
211 STACK_CHECK_START_REL(L, 0); 211 STACK_CHECK_START_REL(L, 0);
212 212
213 // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4) 213 // a new full userdata, fitted with the specified number of uservalue slots (always 1 for Lua < 5.4)
214 DeepPrelude** proxy = (DeepPrelude**) lua_newuserdatauv(L, sizeof(DeepPrelude*), nuv_); // DPC proxy 214 DeepPrelude** const proxy{ lua_newuserdatauv<DeepPrelude*>(L, nuv_) }; // DPC proxy
215 ASSERT_L( proxy); 215 ASSERT_L(proxy);
216 *proxy = prelude; 216 *proxy = prelude;
217 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
218 218
@@ -221,7 +221,7 @@ char const* DeepFactory::PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, L
221 lua_pushlightuserdata( L, std::bit_cast<void*>(&factory)); // DPC proxy factory 221 lua_pushlightuserdata( L, std::bit_cast<void*>(&factory)); // DPC proxy factory
222 get_deep_lookup( L); // DPC proxy metatable? 222 get_deep_lookup( L); // DPC proxy metatable?
223 223
224 if( lua_isnil( L, -1)) // // No metatable yet. 224 if (lua_isnil( L, -1)) // // No metatable yet.
225 { 225 {
226 lua_pop(L, 1); // DPC proxy 226 lua_pop(L, 1); // DPC proxy
227 int const oldtop{ lua_gettop(L) }; 227 int const oldtop{ lua_gettop(L) };
@@ -245,7 +245,7 @@ char const* DeepFactory::PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, L
245 lua_newtable( L); // DPC proxy metatable 245 lua_newtable( L); // DPC proxy metatable
246 lua_pushnil( L); // DPC proxy metatable nil 246 lua_pushnil( L); // DPC proxy metatable nil
247 } 247 }
248 if( lua_isnil( L, -1)) 248 if (lua_isnil( L, -1))
249 { 249 {
250 // Add our own '__gc' method 250 // Add our own '__gc' method
251 lua_pop( L, 1); // DPC proxy metatable 251 lua_pop( L, 1); // DPC proxy metatable
@@ -269,22 +269,22 @@ char const* DeepFactory::PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, L
269 // L.registry._LOADED exists without having registered the 'package' library. 269 // L.registry._LOADED exists without having registered the 'package' library.
270 lua_getglobal( L, "require"); // DPC proxy metatable require() 270 lua_getglobal( L, "require"); // DPC proxy metatable require()
271 // check that the module is already loaded (or being loaded, we are happy either way) 271 // check that the module is already loaded (or being loaded, we are happy either way)
272 if( lua_isfunction( L, -1)) 272 if (lua_isfunction( L, -1))
273 { 273 {
274 lua_pushstring( L, modname); // DPC proxy metatable require() "module" 274 lua_pushstring( L, modname); // DPC proxy metatable require() "module"
275 lua_getfield( L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // DPC proxy metatable require() "module" _R._LOADED 275 lua_getfield( L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); // DPC proxy metatable require() "module" _R._LOADED
276 if( lua_istable( L, -1)) 276 if (lua_istable( L, -1))
277 { 277 {
278 lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module" 278 lua_pushvalue( L, -2); // DPC proxy metatable require() "module" _R._LOADED "module"
279 lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module 279 lua_rawget( L, -2); // DPC proxy metatable require() "module" _R._LOADED module
280 int const alreadyloaded = lua_toboolean( L, -1); 280 int const alreadyloaded = lua_toboolean( L, -1);
281 if( !alreadyloaded) // not loaded 281 if (!alreadyloaded) // not loaded
282 { 282 {
283 int require_result; 283 int require_result;
284 lua_pop( L, 2); // DPC proxy metatable require() "module" 284 lua_pop( L, 2); // DPC proxy metatable require() "module"
285 // require "modname" 285 // require "modname"
286 require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error? 286 require_result = lua_pcall( L, 1, 0, 0); // DPC proxy metatable error?
287 if( require_result != LUA_OK) 287 if (require_result != LUA_OK)
288 { 288 {
289 // failed, return the error message 289 // failed, return the error message
290 lua_pushfstring( L, "error while requiring '%s' identified by DeepFactory::moduleName: ", modname); 290 lua_pushfstring( L, "error while requiring '%s' identified by DeepFactory::moduleName: ", modname);
@@ -326,7 +326,7 @@ char const* DeepFactory::PushDeepProxy(Dest L, DeepPrelude* prelude, int nuv_, L
326 return nullptr; 326 return nullptr;
327} 327}
328 328
329// ################################################################################################ 329// #################################################################################################
330 330
331/* 331/*
332* Create a deep userdata 332* Create a deep userdata
@@ -354,7 +354,7 @@ int DeepFactory::pushDeepUserdata(Dest L, int nuv_) const
354 return luaL_error( L, "DeepFactory::newDeepObjectInternal failed to create deep userdata (out of memory)"); 354 return luaL_error( L, "DeepFactory::newDeepObjectInternal failed to create deep userdata (out of memory)");
355 } 355 }
356 356
357 if( prelude->m_magic != DEEP_VERSION) 357 if (prelude->m_magic != DEEP_VERSION)
358 { 358 {
359 // 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
360 deleteDeepObjectInternal(L, prelude); 360 deleteDeepObjectInternal(L, prelude);
@@ -364,7 +364,7 @@ int DeepFactory::pushDeepUserdata(Dest L, int nuv_) const
364 ASSERT_L(prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'DeepFactory::PushDeepProxy' will lift it to 1 364 ASSERT_L(prelude->m_refcount.load(std::memory_order_relaxed) == 0); // 'DeepFactory::PushDeepProxy' will lift it to 1
365 ASSERT_L(&prelude->m_factory == this); 365 ASSERT_L(&prelude->m_factory == this);
366 366
367 if( lua_gettop( L) - oldtop != 0) 367 if (lua_gettop( L) - oldtop != 0)
368 { 368 {
369 // 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
370 deleteDeepObjectInternal(L, prelude); 370 deleteDeepObjectInternal(L, prelude);
@@ -380,7 +380,7 @@ int DeepFactory::pushDeepUserdata(Dest L, int nuv_) const
380 return 1; 380 return 1;
381} 381}
382 382
383// ################################################################################################ 383// #################################################################################################
384 384
385/* 385/*
386* Access deep userdata through a proxy. 386* Access deep userdata through a proxy.
@@ -402,7 +402,7 @@ DeepPrelude* DeepFactory::toDeep(lua_State* L, int index) const
402 return *proxy; 402 return *proxy;
403} 403}
404 404
405// ################################################################################################ 405// #################################################################################################
406 406
407/* 407/*
408 * Copy deep userdata between two separate Lua states (from L to L2) 408 * Copy deep userdata between two separate Lua states (from L to L2)
diff --git a/src/deep.h b/src/deep.h
index 27aed8f..3256e6e 100644
--- a/src/deep.h
+++ b/src/deep.h
@@ -28,7 +28,7 @@ enum class LookupMode
28 FromKeeper // send a function from a keeper state to a lane 28 FromKeeper // send a function from a keeper state to a lane
29}; 29};
30 30
31// ################################################################################################ 31// #################################################################################################
32 32
33// xxh64 of string "DEEP_VERSION_3" generated at https://www.pelock.com/products/hash-calculator 33// xxh64 of string "DEEP_VERSION_3" generated at https://www.pelock.com/products/hash-calculator
34static constexpr UniqueKey DEEP_VERSION{ 0xB2CC0FD9C0AE9674ull, "DEEP_VERSION_3" }; 34static constexpr UniqueKey DEEP_VERSION{ 0xB2CC0FD9C0AE9674ull, "DEEP_VERSION_3" };
diff --git a/src/keeper.cpp b/src/keeper.cpp
index 3e26d5e..5d94944 100644
--- a/src/keeper.cpp
+++ b/src/keeper.cpp
@@ -48,9 +48,9 @@
48#include <algorithm> 48#include <algorithm>
49#include <cassert> 49#include <cassert>
50 50
51// ################################################################################### 51// #################################################################################################
52// Keeper implementation 52// Keeper implementation
53// ################################################################################### 53// #################################################################################################
54 54
55class keeper_fifo 55class keeper_fifo
56{ 56{
@@ -74,7 +74,7 @@ class keeper_fifo
74 74
75static constexpr int CONTENTS_TABLE{ 1 }; 75static constexpr int CONTENTS_TABLE{ 1 };
76 76
77// ################################################################################################## 77// #################################################################################################
78 78
79// replaces the fifo ud by its uservalue on the stack 79// replaces the fifo ud by its uservalue on the stack
80[[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L, int idx_) 80[[nodiscard]] static keeper_fifo* prepare_fifo_access(lua_State* L, int idx_)
@@ -91,7 +91,7 @@ static constexpr int CONTENTS_TABLE{ 1 };
91 return fifo; 91 return fifo;
92} 92}
93 93
94// ################################################################################################## 94// #################################################################################################
95 95
96// in: nothing 96// in: nothing
97// out: { first = 1, count = 0, limit = -1} 97// out: { first = 1, count = 0, limit = -1}
@@ -107,7 +107,7 @@ static constexpr int CONTENTS_TABLE{ 1 };
107 return fifo; 107 return fifo;
108} 108}
109 109
110// ################################################################################################## 110// #################################################################################################
111 111
112// in: expect fifo ... on top of the stack 112// in: expect fifo ... on top of the stack
113// out: nothing, removes all pushed values from the stack 113// out: nothing, removes all pushed values from the stack
@@ -124,7 +124,7 @@ static void fifo_push(lua_State* L, keeper_fifo* fifo_, int count_)
124 fifo_->count += count_; 124 fifo_->count += count_;
125} 125}
126 126
127// ################################################################################################## 127// #################################################################################################
128 128
129// in: fifo 129// in: fifo
130// out: ...|nothing 130// out: ...|nothing
@@ -140,7 +140,7 @@ static void fifo_peek(lua_State* L, keeper_fifo* fifo_, int count_)
140 } 140 }
141} 141}
142 142
143// ################################################################################################## 143// #################################################################################################
144 144
145// in: fifo 145// in: fifo
146// out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number) 146// out: remove the fifo from the stack, push as many items as required on the stack (function assumes they exist in sufficient number)
@@ -177,7 +177,7 @@ static void fifo_pop( lua_State* L, keeper_fifo* fifo_, int count_)
177 } 177 }
178} 178}
179 179
180// ################################################################################################## 180// #################################################################################################
181 181
182// in: linda_ud expected at stack slot idx 182// in: linda_ud expected at stack slot idx
183// out: fifos[ud] 183// out: fifos[ud]
@@ -205,7 +205,7 @@ static void push_table(lua_State* L, int idx_)
205 STACK_CHECK(L, 1); 205 STACK_CHECK(L, 1);
206} 206}
207 207
208// ################################################################################################## 208// #################################################################################################
209 209
210int keeper_push_linda_storage(Universe* U, Dest L, void* ptr_, uintptr_t magic_) 210int keeper_push_linda_storage(Universe* U, Dest L, void* ptr_, uintptr_t magic_)
211{ 211{
@@ -258,7 +258,7 @@ int keeper_push_linda_storage(Universe* U, Dest L, void* ptr_, uintptr_t magic_)
258 return 1; 258 return 1;
259} 259}
260 260
261// ################################################################################################## 261// #################################################################################################
262 262
263// in: linda_ud 263// in: linda_ud
264int keepercall_clear(lua_State* L) 264int keepercall_clear(lua_State* L)
@@ -274,7 +274,7 @@ int keepercall_clear(lua_State* L)
274 return 0; 274 return 0;
275} 275}
276 276
277// ################################################################################################## 277// #################################################################################################
278 278
279// in: linda_ud, key, ... 279// in: linda_ud, key, ...
280// out: true|false 280// out: true|false
@@ -285,7 +285,7 @@ int keepercall_send(lua_State* L)
285 // get the fifo associated to this key in this linda, create it if it doesn't exist 285 // get the fifo associated to this key in this linda, create it if it doesn't exist
286 lua_pushvalue(L, 2); // ud key ... fifos key 286 lua_pushvalue(L, 2); // ud key ... fifos key
287 lua_rawget(L, -2); // ud key ... fifos fifo 287 lua_rawget(L, -2); // ud key ... fifos fifo
288 if( lua_isnil(L, -1)) 288 if (lua_isnil(L, -1))
289 { 289 {
290 lua_pop(L, 1); // ud key ... fifos 290 lua_pop(L, 1); // ud key ... fifos
291 std::ignore = fifo_new(L); // ud key ... fifos fifo 291 std::ignore = fifo_new(L); // ud key ... fifos fifo
@@ -311,7 +311,7 @@ int keepercall_send(lua_State* L)
311 return 1; 311 return 1;
312} 312}
313 313
314// ################################################################################################## 314// #################################################################################################
315 315
316// in: linda_ud, key [, key]? 316// in: linda_ud, key [, key]?
317// out: (key, val) or nothing 317// out: (key, val) or nothing
@@ -347,13 +347,13 @@ int keepercall_receive(lua_State* L)
347 return 0; 347 return 0;
348} 348}
349 349
350// ################################################################################################## 350// #################################################################################################
351 351
352// in: linda_ud key mincount [maxcount] 352// in: linda_ud key mincount [maxcount]
353int keepercall_receive_batched(lua_State* L) 353int keepercall_receive_batched(lua_State* L)
354{ 354{
355 int const min_count{ static_cast<int>(lua_tointeger(L, 3)) }; 355 int const min_count{ static_cast<int>(lua_tointeger(L, 3)) };
356 if( min_count > 0) 356 if (min_count > 0)
357 { 357 {
358 int const max_count{ static_cast<int>(luaL_optinteger(L, 4, min_count)) }; 358 int const max_count{ static_cast<int>(luaL_optinteger(L, 4, min_count)) };
359 lua_settop(L, 2); // ud key 359 lua_settop(L, 2); // ud key
@@ -364,7 +364,7 @@ int keepercall_receive_batched(lua_State* L)
364 lua_rawget(L, 2); // key fifos fifo 364 lua_rawget(L, 2); // key fifos fifo
365 lua_remove(L, 2); // key fifo 365 lua_remove(L, 2); // key fifo
366 keeper_fifo* const fifo{ prepare_fifo_access(L, 2) }; // key fifotbl 366 keeper_fifo* const fifo{ prepare_fifo_access(L, 2) }; // key fifotbl
367 if( fifo != nullptr && fifo->count >= min_count) 367 if (fifo != nullptr && fifo->count >= min_count)
368 { 368 {
369 fifo_pop(L, fifo, std::min( max_count, fifo->count)); // key ... 369 fifo_pop(L, fifo, std::min( max_count, fifo->count)); // key ...
370 } 370 }
@@ -380,7 +380,7 @@ int keepercall_receive_batched(lua_State* L)
380 } 380 }
381} 381}
382 382
383// ################################################################################################## 383// #################################################################################################
384 384
385// in: linda_ud key n 385// in: linda_ud key n
386// out: true or nil 386// out: true or nil
@@ -403,7 +403,7 @@ int keepercall_limit(lua_State* L)
403 lua_settop(L, 0); 403 lua_settop(L, 0);
404 // return true if we decide that blocked threads waiting to write on that key should be awakened 404 // return true if we decide that blocked threads waiting to write on that key should be awakened
405 // this is the case if we detect the key was full but it is no longer the case 405 // this is the case if we detect the key was full but it is no longer the case
406 if( 406 if (
407 ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit 407 ((fifo->limit >= 0) && (fifo->count >= fifo->limit)) // the key was full if limited and count exceeded the previous limit
408 && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit 408 && ((limit < 0) || (fifo->count < limit)) // the key is not full if unlimited or count is lower than the new limit
409 ) 409 )
@@ -416,7 +416,7 @@ int keepercall_limit(lua_State* L)
416 return lua_gettop(L); 416 return lua_gettop(L);
417} 417}
418 418
419// ################################################################################################## 419// #################################################################################################
420 420
421// in: linda_ud key [[val] ...] 421// in: linda_ud key [[val] ...]
422//out: true if the linda was full but it's no longer the case, else nothing 422//out: true if the linda was full but it's no longer the case, else nothing
@@ -462,7 +462,7 @@ int keepercall_set(lua_State* L)
462 lua_pushvalue(L, 2); // fifos key [val [, ...]] key 462 lua_pushvalue(L, 2); // fifos key [val [, ...]] key
463 lua_rawget(L, 1); // fifos key [val [, ...]] fifo|nil 463 lua_rawget(L, 1); // fifos key [val [, ...]] fifo|nil
464 keeper_fifo* fifo{ keeper_fifo::getPtr(L, -1) }; 464 keeper_fifo* fifo{ keeper_fifo::getPtr(L, -1) };
465 if( fifo == nullptr) // can be nullptr if we store a value at a new key 465 if (fifo == nullptr) // can be nullptr if we store a value at a new key
466 { // fifos key [val [, ...]] nil 466 { // fifos key [val [, ...]] nil
467 // no need to wake writers in that case, because a writer can't wait on an inexistent key 467 // no need to wake writers in that case, because a writer can't wait on an inexistent key
468 lua_pop(L, 1); // fifos key [val [, ...]] 468 lua_pop(L, 1); // fifos key [val [, ...]]
@@ -489,7 +489,7 @@ int keepercall_set(lua_State* L)
489 return should_wake_writers ? (lua_pushboolean(L, 1), 1) : 0; 489 return should_wake_writers ? (lua_pushboolean(L, 1), 1) : 0;
490} 490}
491 491
492// ################################################################################################## 492// #################################################################################################
493 493
494// in: linda_ud key [count] 494// in: linda_ud key [count]
495// out: at most <count> values 495// out: at most <count> values
@@ -517,7 +517,7 @@ int keepercall_get(lua_State* L)
517 return 0; 517 return 0;
518} 518}
519 519
520// ################################################################################################## 520// #################################################################################################
521 521
522// in: linda_ud [, key [, ...]] 522// in: linda_ud [, key [, ...]]
523int keepercall_count(lua_State* L) 523int keepercall_count(lua_State* L)
@@ -586,9 +586,9 @@ int keepercall_count(lua_State* L)
586 return 1; 586 return 1;
587} 587}
588 588
589//################################################################################### 589// #################################################################################################
590// Keeper API, accessed from linda methods 590// Keeper API, accessed from linda methods
591//################################################################################### 591// #################################################################################################
592 592
593/*---=== Keeper states ===--- 593/*---=== Keeper states ===---
594*/ 594*/
@@ -636,7 +636,7 @@ void close_keepers(Universe* U)
636 } 636 }
637} 637}
638 638
639// ################################################################################################## 639// #################################################################################################
640 640
641/* 641/*
642 * Initialize keeper states 642 * Initialize keeper states
@@ -746,7 +746,7 @@ void init_keepers(Universe* U, lua_State* L)
746 STACK_CHECK(L, 0); 746 STACK_CHECK(L, 0);
747} 747}
748 748
749// ################################################################################################## 749// #################################################################################################
750 750
751// should be called only when inside a keeper_acquire/keeper_release pair (see Linda::ProtectedCall) 751// should be called only when inside a keeper_acquire/keeper_release pair (see Linda::ProtectedCall)
752Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_) 752Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_)
@@ -760,7 +760,7 @@ Keeper* which_keeper(Keepers* keepers_, uintptr_t magic_)
760 return nullptr; 760 return nullptr;
761} 761}
762 762
763// ################################################################################################## 763// #################################################################################################
764 764
765Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_) 765Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_)
766{ 766{
@@ -784,7 +784,7 @@ Keeper* keeper_acquire(Keepers* keepers_, uintptr_t magic_)
784 return nullptr; 784 return nullptr;
785} 785}
786 786
787// ################################################################################################## 787// #################################################################################################
788 788
789void keeper_release(Keeper* K) 789void keeper_release(Keeper* K)
790{ 790{
@@ -795,7 +795,7 @@ void keeper_release(Keeper* K)
795 } 795 }
796} 796}
797 797
798// ################################################################################################## 798// #################################################################################################
799 799
800void keeper_toggle_nil_sentinels(lua_State* L, int val_i_, LookupMode const mode_) 800void keeper_toggle_nil_sentinels(lua_State* L, int val_i_, LookupMode const mode_)
801{ 801{
@@ -821,7 +821,7 @@ void keeper_toggle_nil_sentinels(lua_State* L, int val_i_, LookupMode const mode
821 } 821 }
822} 822}
823 823
824// ################################################################################################## 824// #################################################################################################
825 825
826/* 826/*
827* Call a function ('func_name') in the keeper state, and pass on the returned 827* Call a function ('func_name') in the keeper state, and pass on the returned
diff --git a/src/lanes.cpp b/src/lanes.cpp
index 17d4f67..3aa3365 100644
--- a/src/lanes.cpp
+++ b/src/lanes.cpp
@@ -101,8 +101,63 @@ THE SOFTWARE.
101 101
102#include <atomic> 102#include <atomic>
103 103
104// forwarding (will do things better later) 104// #################################################################################################
105static void tracking_add(Lane* lane_); 105
106#if HAVE_LANE_TRACKING()
107
108// The chain is ended by '(Lane*)(-1)', not nullptr:
109// 'tracking_first -> ... -> ... -> (-1)'
110#define TRACKING_END ((Lane *)(-1))
111
112/*
113 * Add the lane to tracking chain; the ones still running at the end of the
114 * whole process will be cancelled.
115 */
116static void tracking_add(Lane* lane_)
117{
118 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
119 assert(lane_->tracking_next == nullptr);
120
121 lane_->tracking_next = lane_->U->tracking_first;
122 lane_->U->tracking_first = lane_;
123}
124
125// #################################################################################################
126
127/*
128 * A free-running lane has ended; remove it from tracking chain
129 */
130[[nodiscard]] static bool tracking_remove(Lane* lane_)
131{
132 bool found{ false };
133 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
134 // Make sure (within the MUTEX) that we actually are in the chain
135 // still (at process exit they will remove us from chain and then
136 // cancel/kill).
137 //
138 if (lane_->tracking_next != nullptr)
139 {
140 Lane** ref = (Lane**) &lane_->U->tracking_first;
141
142 while( *ref != TRACKING_END)
143 {
144 if (*ref == lane_)
145 {
146 *ref = lane_->tracking_next;
147 lane_->tracking_next = nullptr;
148 found = true;
149 break;
150 }
151 ref = (Lane**) &((*ref)->tracking_next);
152 }
153 assert( found);
154 }
155 return found;
156}
157
158#endif // HAVE_LANE_TRACKING()
159
160// #################################################################################################
106 161
107Lane::Lane(Universe* U_, lua_State* L_) 162Lane::Lane(Universe* U_, lua_State* L_)
108: U{ U_ } 163: U{ U_ }
@@ -180,114 +235,44 @@ static constexpr UniqueKey FINALIZER_REGKEY{ 0x188fccb8bf348e09ull };
180 235
181// ################################################################################################# 236// #################################################################################################
182 237
183/* 238Lane::~Lane()
184* Push a table stored in registry onto Lua stack.
185*
186* If there is no existing table, create one if 'create' is true.
187*
188* Returns: true if a table was pushed
189* false if no table found, not created, and nothing pushed
190*/
191[[nodiscard]] static bool push_registry_table(lua_State* L, UniqueKey key, bool create)
192{ 239{
193 STACK_GROW(L, 3); 240 // Clean up after a (finished) thread
194 STACK_CHECK_START_REL(L, 0); 241 //
195 242#if HAVE_LANE_TRACKING()
196 key.pushValue(L); // ? 243 if (U->tracking_first != nullptr)
197 if (lua_isnil(L, -1)) // nil?
198 { 244 {
199 lua_pop(L, 1); // 245 // Lane was cleaned up, no need to handle at process termination
200 STACK_CHECK(L, 0); 246 std::ignore = tracking_remove(this);
201
202 if (!create)
203 {
204 return false;
205 }
206
207 lua_newtable(L); // t
208 key.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); });
209 } 247 }
210 STACK_CHECK(L, 1); 248#endif // HAVE_LANE_TRACKING()
211 return true; // table pushed
212} 249}
213 250
214// ################################################################################################# 251// #################################################################################################
252// ########################################## Finalizer ############################################
253// #################################################################################################
215 254
216#if HAVE_LANE_TRACKING()
217 255
218// The chain is ended by '(Lane*)(-1)', not nullptr: 256// Push the finalizers table on the stack.
219// 'tracking_first -> ... -> ... -> (-1)' 257// If there is no existing table, create ti.
220#define TRACKING_END ((Lane *)(-1)) 258static void push_finalizers_table(lua_State* L)
221
222/*
223 * Add the lane to tracking chain; the ones still running at the end of the
224 * whole process will be cancelled.
225 */
226static void tracking_add(Lane* lane_)
227{ 259{
228 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs }; 260 STACK_GROW(L, 3);
229 assert(lane_->tracking_next == nullptr); 261 STACK_CHECK_START_REL(L, 0);
230
231 lane_->tracking_next = lane_->U->tracking_first;
232 lane_->U->tracking_first = lane_;
233}
234
235// #################################################################################################
236 262
237/* 263 FINALIZER_REGKEY.pushValue(L); // ?
238 * A free-running lane has ended; remove it from tracking chain 264 if (lua_isnil(L, -1)) // nil?
239 */
240[[nodiscard]] static bool tracking_remove(Lane* lane_)
241{
242 bool found{ false };
243 std::lock_guard<std::mutex> guard{ lane_->U->tracking_cs };
244 // Make sure (within the MUTEX) that we actually are in the chain
245 // still (at process exit they will remove us from chain and then
246 // cancel/kill).
247 //
248 if (lane_->tracking_next != nullptr)
249 { 265 {
250 Lane** ref = (Lane**) &lane_->U->tracking_first; 266 lua_pop(L, 1); //
251 267 // store a newly created table in the registry, but leave it on the stack too
252 while( *ref != TRACKING_END) 268 lua_newtable(L); // t
253 { 269 FINALIZER_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); }); // t
254 if (*ref == lane_)
255 {
256 *ref = lane_->tracking_next;
257 lane_->tracking_next = nullptr;
258 found = true;
259 break;
260 }
261 ref = (Lane**) &((*ref)->tracking_next);
262 }
263 assert( found);
264 } 270 }
265 return found; 271 STACK_CHECK(L, 1);
266} 272}
267 273
268#endif // HAVE_LANE_TRACKING()
269
270// ################################################################################################# 274// #################################################################################################
271 275
272Lane::~Lane()
273{
274 // Clean up after a (finished) thread
275 //
276#if HAVE_LANE_TRACKING()
277 if (U->tracking_first != nullptr)
278 {
279 // Lane was cleaned up, no need to handle at process termination
280 std::ignore = tracking_remove(this);
281 }
282#endif // HAVE_LANE_TRACKING()
283}
284
285/*
286 * ###############################################################################################
287 * ########################################## Finalizer ##########################################
288 * ###############################################################################################
289 */
290
291//--- 276//---
292// void= finalizer( finalizer_func ) 277// void= finalizer( finalizer_func )
293// 278//
@@ -296,12 +281,12 @@ Lane::~Lane()
296// Add a function that will be called when exiting the lane, either via 281// Add a function that will be called when exiting the lane, either via
297// normal return or an error. 282// normal return or an error.
298// 283//
299LUAG_FUNC( set_finalizer) 284LUAG_FUNC(set_finalizer)
300{ 285{
301 luaL_argcheck(L, lua_isfunction(L, 1), 1, "finalizer should be a function"); 286 luaL_argcheck(L, lua_isfunction(L, 1), 1, "finalizer should be a function");
302 luaL_argcheck(L, lua_gettop( L) == 1, 1, "too many arguments"); 287 luaL_argcheck(L, lua_gettop( L) == 1, 1, "too many arguments");
303 // Get the current finalizer table (if any), create one if it doesn't exist 288 // Get the current finalizer table (if any), create one if it doesn't exist
304 std::ignore = push_registry_table(L, FINALIZER_REGKEY, true); // finalizer {finalisers} 289 push_finalizers_table(L); // finalizer {finalisers}
305 STACK_GROW(L, 2); 290 STACK_GROW(L, 2);
306 lua_pushinteger(L, lua_rawlen(L, -1) + 1); // finalizer {finalisers} idx 291 lua_pushinteger(L, lua_rawlen(L, -1) + 1); // finalizer {finalisers} idx
307 lua_pushvalue(L, 1); // finalizer {finalisers} idx finalizer 292 lua_pushvalue(L, 1); // finalizer {finalisers} idx finalizer
@@ -310,7 +295,44 @@ LUAG_FUNC( set_finalizer)
310 return 0; 295 return 0;
311} 296}
312 297
298// #################################################################################################
299
300static void push_stack_trace(lua_State* L, int rc_, int stk_base_)
301{
302 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
303 switch(rc_)
304 {
305 case LUA_OK: // no error, body return values are on the stack
306 break;
313 307
308 case LUA_ERRRUN: // cancellation or a runtime error
309#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler
310 {
311 STACK_CHECK_START_REL(L, 0);
312 // fetch the call stack table from the registry where the handler stored it
313 STACK_GROW(L, 1);
314 // yields nil if no stack was generated (in case of cancellation for example)
315 STACKTRACE_REGKEY.pushValue(L); // err trace|nil
316 STACK_CHECK(L, 1);
317
318 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed
319 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
320 ASSERT_L(lua_type(L, 1 + stk_base_) == (CANCEL_ERROR.equals(L, stk_base_) ? LUA_TNIL : LUA_TTABLE));
321 // Just leaving the stack trace table on the stack is enough to get it through to the master.
322 break;
323 }
324#endif // fall through if not ERROR_FULL_STACK
325
326 case LUA_ERRMEM: // memory allocation error (handler not called)
327 case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
328 default:
329 // we should have a single value which is either a string (the error message) or CANCEL_ERROR
330 ASSERT_L((lua_gettop(L) == stk_base_) && ((lua_type(L, stk_base_) == LUA_TSTRING) || CANCEL_ERROR.equals(L, stk_base_)));
331 break;
332 }
333}
334
335// #################################################################################################
314//--- 336//---
315// Run finalizers - if any - with the given parameters 337// Run finalizers - if any - with the given parameters
316// 338//
@@ -324,43 +346,37 @@ LUAG_FUNC( set_finalizer)
324// 346//
325// TBD: should we add stack trace on failing finalizer, wouldn't be hard.. 347// TBD: should we add stack trace on failing finalizer, wouldn't be hard..
326// 348//
327static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
328 349
329[[nodiscard]] static int run_finalizers(lua_State* L, int lua_rc) 350[[nodiscard]] static int run_finalizers(lua_State* L, int lua_rc_)
330{ 351{
331 int finalizers_index; 352 FINALIZER_REGKEY.pushValue(L); // ... finalizers?
332 int n; 353 if (lua_isnil(L, -1))
333 int err_handler_index = 0;
334 int rc = LUA_OK; // ...
335 if (!push_registry_table(L, FINALIZER_REGKEY, false)) // ... finalizers?
336 { 354 {
355 lua_pop(L, 1);
337 return 0; // no finalizers 356 return 0; // no finalizers
338 } 357 }
339 358
340 STACK_GROW(L, 5); 359 STACK_GROW(L, 5);
341 360
342 finalizers_index = lua_gettop( L); 361 int const finalizers_index{ lua_gettop(L) };
343 362 int const err_handler_index{ ERROR_FULL_STACK ? (lua_pushcfunction(L, lane_error), lua_gettop(L)) : 0 };
344#if ERROR_FULL_STACK
345 lua_pushcfunction(L, lane_error); // ... finalizers lane_error
346 err_handler_index = lua_gettop( L);
347#endif // ERROR_FULL_STACK
348 363
349 for( n = (int) lua_rawlen(L, finalizers_index); n > 0; -- n) 364 int rc{ LUA_OK };
365 for (int n = static_cast<int>(lua_rawlen(L, finalizers_index)); n > 0; --n)
350 { 366 {
351 int args = 0; 367 int args = 0;
352 lua_pushinteger(L, n); // ... finalizers lane_error n 368 lua_pushinteger(L, n); // ... finalizers lane_error n
353 lua_rawget(L, finalizers_index); // ... finalizers lane_error finalizer 369 lua_rawget(L, finalizers_index); // ... finalizers lane_error finalizer
354 ASSERT_L( lua_isfunction(L, -1)); 370 ASSERT_L(lua_isfunction(L, -1));
355 if (lua_rc != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack 371 if (lua_rc_ != LUA_OK) // we have an error message and an optional stack trace at the bottom of the stack
356 { 372 {
357 ASSERT_L( finalizers_index == 2 || finalizers_index == 3); 373 ASSERT_L( finalizers_index == 2 || finalizers_index == 3);
358 //char const* err_msg = lua_tostring(L, 1); 374 //char const* err_msg = lua_tostring(L, 1);
359 lua_pushvalue(L, 1); // ... finalizers lane_error finalizer err_msg 375 lua_pushvalue(L, 1); // ... finalizers lane_error finalizer err_msg
360 // note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM 376 // note we don't always have a stack trace for example when CANCEL_ERROR, or when we got an error that doesn't call our handler, such as LUA_ERRMEM
361 if (finalizers_index == 3) 377 if (finalizers_index == 3)
362 { 378 {
363 lua_pushvalue(L, 2); // ... finalizers lane_error finalizer err_msg stack_trace 379 lua_pushvalue(L, 2); // ... finalizers lane_error finalizer err_msg stack_trace
364 } 380 }
365 args = finalizers_index - 1; 381 args = finalizers_index - 1;
366 } 382 }
@@ -369,21 +385,21 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
369 rc = lua_pcall(L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2? 385 rc = lua_pcall(L, args, 0, err_handler_index); // ... finalizers lane_error err_msg2?
370 if (rc != LUA_OK) 386 if (rc != LUA_OK)
371 { 387 {
372 push_stack_trace(L, rc, lua_gettop( L)); 388 push_stack_trace(L, rc, lua_gettop(L));
373 // If one finalizer fails, don't run the others. Return this 389 // If one finalizer fails, don't run the others. Return this
374 // as the 'real' error, replacing what we could have had (or not) 390 // as the 'real' error, replacing what we could have had (or not)
375 // from the actual code. 391 // from the actual code.
376 break; 392 break;
377 } 393 }
378 // no error, proceed to next finalizer // ... finalizers lane_error 394 // no error, proceed to next finalizer // ... finalizers lane_error
379 } 395 }
380 396
381 if (rc != LUA_OK) 397 if (rc != LUA_OK)
382 { 398 {
383 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack 399 // ERROR_FULL_STACK accounts for the presence of lane_error on the stack
384 int nb_err_slots = lua_gettop( L) - finalizers_index - ERROR_FULL_STACK; 400 int const nb_err_slots{ lua_gettop(L) - finalizers_index - ERROR_FULL_STACK };
385 // a finalizer generated an error, this is what we leave of the stack 401 // a finalizer generated an error, this is what we leave of the stack
386 for( n = nb_err_slots; n > 0; -- n) 402 for (int n = nb_err_slots; n > 0; --n)
387 { 403 {
388 lua_replace(L, n); 404 lua_replace(L, n);
389 } 405 }
@@ -399,9 +415,9 @@ static void push_stack_trace( lua_State* L, int rc_, int stk_base_);
399} 415}
400 416
401/* 417/*
402 * ############################################################################################### 418 * ################################################################################################
403 * ########################################### Threads ########################################### 419 * ########################################### Threads ############################################
404 * ############################################################################################### 420 * ################################################################################################
405 */ 421 */
406 422
407// 423//
@@ -425,7 +441,7 @@ static void selfdestruct_add(Lane* lane_)
425 lane_->U->selfdestruct_first = lane_; 441 lane_->U->selfdestruct_first = lane_;
426} 442}
427 443
428// ############################################################################################### 444// #################################################################################################
429 445
430/* 446/*
431 * A free-running lane has ended; remove it from selfdestruct chain 447 * A free-running lane has ended; remove it from selfdestruct chain
@@ -460,7 +476,7 @@ static void selfdestruct_add(Lane* lane_)
460 return found; 476 return found;
461} 477}
462 478
463// ############################################################################################### 479// #################################################################################################
464 480
465/* 481/*
466* Process end; cancel any still free-running threads 482* Process end; cancel any still free-running threads
@@ -567,7 +583,7 @@ static void selfdestruct_add(Lane* lane_)
567 return 0; 583 return 0;
568} 584}
569 585
570// ############################################################################################### 586// #################################################################################################
571 587
572//--- 588//---
573// = _single( [cores_uint=1] ) 589// = _single( [cores_uint=1] )
@@ -597,7 +613,7 @@ LUAG_FUNC( set_singlethreaded)
597#endif 613#endif
598} 614}
599 615
600// ############################################################################################### 616// #################################################################################################
601 617
602/* 618/*
603* str= lane_error( error_val|str ) 619* str= lane_error( error_val|str )
@@ -710,41 +726,6 @@ LUAG_FUNC( set_error_reporting)
710} 726}
711#endif // ERROR_FULL_STACK 727#endif // ERROR_FULL_STACK
712 728
713static void push_stack_trace( lua_State* L, int rc_, int stk_base_)
714{
715 // Lua 5.1 error handler is limited to one return value; it stored the stack trace in the registry
716 switch( rc_)
717 {
718 case LUA_OK: // no error, body return values are on the stack
719 break;
720
721 case LUA_ERRRUN: // cancellation or a runtime error
722#if ERROR_FULL_STACK // when ERROR_FULL_STACK, we installed a handler
723 {
724 STACK_CHECK_START_REL(L, 0);
725 // fetch the call stack table from the registry where the handler stored it
726 STACK_GROW(L, 1);
727 // yields nil if no stack was generated (in case of cancellation for example)
728 STACKTRACE_REGKEY.pushValue(L); // err trace|nil
729 STACK_CHECK(L, 1);
730
731 // For cancellation the error message is CANCEL_ERROR, and a stack trace isn't placed
732 // For other errors, the message can be whatever was thrown, and we should have a stack trace table
733 ASSERT_L(lua_type(L, 1 + stk_base_) == (CANCEL_ERROR.equals(L, stk_base_) ? LUA_TNIL : LUA_TTABLE));
734 // Just leaving the stack trace table on the stack is enough to get it through to the master.
735 break;
736 }
737#endif // fall through if not ERROR_FULL_STACK
738
739 case LUA_ERRMEM: // memory allocation error (handler not called)
740 case LUA_ERRERR: // error while running the error handler (if any, for example an out-of-memory condition)
741 default:
742 // we should have a single value which is either a string (the error message) or CANCEL_ERROR
743 ASSERT_L((lua_gettop(L) == stk_base_) && ((lua_type(L, stk_base_) == LUA_TSTRING) || CANCEL_ERROR.equals(L, stk_base_)));
744 break;
745 }
746}
747
748// ################################################################################################# 729// #################################################################################################
749 730
750LUAG_FUNC(set_debug_threadname) 731LUAG_FUNC(set_debug_threadname)
@@ -772,7 +753,7 @@ LUAG_FUNC(set_debug_threadname)
772 753
773LUAG_FUNC(get_debug_threadname) 754LUAG_FUNC(get_debug_threadname)
774{ 755{
775 Lane* const lane{ lua_toLane(L, 1) }; 756 Lane* const lane{ ToLane(L, 1) };
776 luaL_argcheck(L, lua_gettop(L) == 1, 2, "too many arguments"); 757 luaL_argcheck(L, lua_gettop(L) == 1, 2, "too many arguments");
777 lua_pushstring(L, lane->debug_name); 758 lua_pushstring(L, lane->debug_name);
778 return 1; 759 return 1;
@@ -828,8 +809,7 @@ static struct errcode_name s_errcodes[] =
828}; 809};
829static char const* get_errcode_name( int _code) 810static char const* get_errcode_name( int _code)
830{ 811{
831 int i; 812 for (int i{ 0 }; i < 7; ++i)
832 for( i = 0; i < 7; ++ i)
833 { 813 {
834 if (s_errcodes[i].code == _code) 814 if (s_errcodes[i].code == _code)
835 { 815 {
@@ -891,7 +871,6 @@ static void lane_main(Lane* lane)
891 push_stack_trace(L, rc, 1); // retvals|error [trace] 871 push_stack_trace(L, rc, 1); // retvals|error [trace]
892 872
893 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name(rc), CANCEL_ERROR.equals(L, 1) ? "cancelled" : lua_typename(L, lua_type(L, 1)))); 873 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "Lane %p body: %s (%s)\n" INDENT_END, L, get_errcode_name(rc), CANCEL_ERROR.equals(L, 1) ? "cancelled" : lua_typename(L, lua_type(L, 1))));
894 // STACK_DUMP(L);
895 // Call finalizers, if the script has set them up. 874 // Call finalizers, if the script has set them up.
896 // 875 //
897 int rc2{ run_finalizers(L, rc) }; 876 int rc2{ run_finalizers(L, rc) };
@@ -1295,7 +1274,7 @@ LUAG_FUNC(lane_new)
1295[[nodiscard]] static int lane_gc(lua_State* L) 1274[[nodiscard]] static int lane_gc(lua_State* L)
1296{ 1275{
1297 bool have_gc_cb{ false }; 1276 bool have_gc_cb{ false };
1298 Lane* const lane{ lua_toLane(L, 1) }; // ud 1277 Lane* const lane{ ToLane(L, 1) }; // ud
1299 1278
1300 // if there a gc callback? 1279 // if there a gc callback?
1301 lua_getiuservalue(L, 1, 1); // ud uservalue 1280 lua_getiuservalue(L, 1, 1); // ud uservalue
@@ -1393,7 +1372,7 @@ void push_thread_status(lua_State* L, Lane* lane_)
1393// 1372//
1394LUAG_FUNC(thread_join) 1373LUAG_FUNC(thread_join)
1395{ 1374{
1396 Lane* const lane{ lua_toLane(L, 1) }; 1375 Lane* const lane{ ToLane(L, 1) };
1397 lua_Duration const duration{ luaL_optnumber(L, 2, -1.0) }; 1376 lua_Duration const duration{ luaL_optnumber(L, 2, -1.0) };
1398 lua_State* const L2{ lane->L }; 1377 lua_State* const L2{ lane->L };
1399 1378
@@ -1474,7 +1453,7 @@ LUAG_FUNC(thread_index)
1474 static constexpr int UD{ 1 }; 1453 static constexpr int UD{ 1 };
1475 static constexpr int KEY{ 2 }; 1454 static constexpr int KEY{ 2 };
1476 static constexpr int USR{ 3 }; 1455 static constexpr int USR{ 3 };
1477 Lane* const lane{ lua_toLane(L, UD) }; 1456 Lane* const lane{ ToLane(L, UD) };
1478 ASSERT_L(lua_gettop(L) == 2); 1457 ASSERT_L(lua_gettop(L) == 2);
1479 1458
1480 STACK_GROW(L, 8); // up to 8 positions are needed in case of error propagation 1459 STACK_GROW(L, 8); // up to 8 positions are needed in case of error propagation
@@ -1647,11 +1626,9 @@ LUAG_FUNC(threads)
1647} 1626}
1648#endif // HAVE_LANE_TRACKING() 1627#endif // HAVE_LANE_TRACKING()
1649 1628
1650/* 1629// #################################################################################################
1651 * ############################################################################################### 1630// ######################################## Timer support ##########################################
1652 * ######################################## Timer support ######################################## 1631// #################################################################################################
1653 * ###############################################################################################
1654 */
1655 1632
1656/* 1633/*
1657* secs = now_secs() 1634* secs = now_secs()
@@ -1722,11 +1699,9 @@ LUAG_FUNC(wakeup_conv)
1722 return 1; 1699 return 1;
1723} 1700}
1724 1701
1725/* 1702// #################################################################################################
1726 * ############################################################################################### 1703// ######################################## Module linkage #########################################
1727 * ######################################## Module linkage ####################################### 1704// #################################################################################################
1728 * ###############################################################################################
1729 */
1730 1705
1731extern int LG_linda(lua_State* L); 1706extern int LG_linda(lua_State* L);
1732static struct luaL_Reg const lanes_functions[] = 1707static struct luaL_Reg const lanes_functions[] =
diff --git a/src/lanes_private.h b/src/lanes_private.h
index 18e55fd..5e6160d 100644
--- a/src/lanes_private.h
+++ b/src/lanes_private.h
@@ -98,7 +98,7 @@ static constexpr UniqueKey LANE_POINTER_REGKEY{ 0xB3022205633743BCull }; // used
98// 'Lane' are malloc/free'd and the handle only carries a pointer. 98// 'Lane' are malloc/free'd and the handle only carries a pointer.
99// This is not deep userdata since the handle's not portable among lanes. 99// This is not deep userdata since the handle's not portable among lanes.
100// 100//
101[[nodiscard]] inline Lane* lua_toLane(lua_State* L, int i_) 101[[nodiscard]] inline Lane* ToLane(lua_State* L, int i_)
102{ 102{
103 return *(static_cast<Lane**>(luaL_checkudata(L, i_, "Lane"))); 103 return *(static_cast<Lane**>(luaL_checkudata(L, i_, "Lane")));
104} 104}
diff --git a/src/macros_and_utils.h b/src/macros_and_utils.h
index 073e940..c01363a 100644
--- a/src/macros_and_utils.h
+++ b/src/macros_and_utils.h
@@ -35,7 +35,6 @@ using namespace std::chrono_literals;
35#ifdef NDEBUG 35#ifdef NDEBUG
36 36
37#define _ASSERT_L(lua,c) //nothing 37#define _ASSERT_L(lua,c) //nothing
38#define STACK_DUMP(L) //nothing
39 38
40#define STACK_CHECK_START_REL(L, offset_) 39#define STACK_CHECK_START_REL(L, offset_)
41#define STACK_CHECK_START_ABS(L, offset_) 40#define STACK_CHECK_START_ABS(L, offset_)
@@ -45,8 +44,7 @@ using namespace std::chrono_literals;
45 44
46#else // NDEBUG 45#else // NDEBUG
47 46
48#define _ASSERT_L(L, cond_) if( (cond_) == 0) { (void) luaL_error(L, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #cond_);} 47#define _ASSERT_L(L, cond_) if ((cond_) == 0) { (void) luaL_error(L, "ASSERT failed: %s:%d '%s'", __FILE__, __LINE__, #cond_);}
49#define STACK_DUMP(L) luaG_dump(L)
50 48
51class StackChecker 49class StackChecker
52{ 50{
diff --git a/src/state.cpp b/src/state.cpp
index 1ba5a77..9ad237d 100644
--- a/src/state.cpp
+++ b/src/state.cpp
@@ -37,7 +37,7 @@ THE SOFTWARE.
37#include "tools.h" 37#include "tools.h"
38#include "universe.h" 38#include "universe.h"
39 39
40// ################################################################################################ 40// #################################################################################################
41 41
42/*---=== Serialize require ===--- 42/*---=== Serialize require ===---
43*/ 43*/
@@ -71,7 +71,7 @@ THE SOFTWARE.
71 71
72 // the required module (or an error message) is left on the stack as returned value by original require function 72 // the required module (or an error message) is left on the stack as returned value by original require function
73 73
74 if( rc != LUA_OK) // LUA_ERRRUN / LUA_ERRMEM ? 74 if (rc != LUA_OK) // LUA_ERRRUN / LUA_ERRMEM ?
75 { 75 {
76 raise_lua_error(L); 76 raise_lua_error(L);
77 } 77 }
@@ -108,7 +108,7 @@ void serialize_require(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L)
108 STACK_CHECK(L, 0); 108 STACK_CHECK(L, 0);
109} 109}
110 110
111// ################################################################################################ 111// #################################################################################################
112 112
113/*---=== luaG_newstate ===---*/ 113/*---=== luaG_newstate ===---*/
114 114
@@ -161,10 +161,9 @@ static luaL_Reg const libs[] =
161 161
162static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L, char const* name_, size_t len_) 162static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L, char const* name_, size_t len_)
163{ 163{
164 int i; 164 for (int i{ 0 }; libs[i].name; ++i)
165 for( i = 0; libs[i].name; ++ i)
166 { 165 {
167 if( strncmp( name_, libs[i].name, len_) == 0) 166 if (strncmp( name_, libs[i].name, len_) == 0)
168 { 167 {
169 lua_CFunction libfunc = libs[i].func; 168 lua_CFunction libfunc = libs[i].func;
170 name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_ 169 name_ = libs[i].name; // note that the provided name_ doesn't necessarily ends with '\0', hence len_
@@ -176,7 +175,7 @@ static void open1lib(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L, char const
176 // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack) 175 // open the library as if through require(), and create a global as well if necessary (the library table is left on the stack)
177 luaL_requiref( L, name_, libfunc, !isLanesCore); 176 luaL_requiref( L, name_, libfunc, !isLanesCore);
178 // lanes.core doesn't declare a global, so scan it here and now 177 // lanes.core doesn't declare a global, so scan it here and now
179 if( isLanesCore == true) 178 if (isLanesCore == true)
180 { 179 {
181 populate_func_lookup_table( L, -1, name_); 180 populate_func_lookup_table( L, -1, name_);
182 } 181 }
@@ -220,7 +219,7 @@ void initialize_on_state_create( Universe* U, lua_State* L)
220{ 219{
221 STACK_CHECK_START_REL(L, 1); // settings 220 STACK_CHECK_START_REL(L, 1); // settings
222 lua_getfield(L, -1, "on_state_create"); // settings on_state_create|nil 221 lua_getfield(L, -1, "on_state_create"); // settings on_state_create|nil
223 if( !lua_isnil(L, -1)) 222 if (!lua_isnil(L, -1))
224 { 223 {
225 // store C function pointer in an internal variable 224 // store C function pointer in an internal variable
226 U->on_state_create_func = lua_tocfunction(L, -1); // settings on_state_create 225 U->on_state_create_func = lua_tocfunction(L, -1); // settings on_state_create
@@ -371,7 +370,7 @@ lua_State* luaG_newstate(Universe* U, Source from_, char const* libs_)
371 { 370 {
372 // special "*" case (mainly to help with LuaJIT compatibility) 371 // special "*" case (mainly to help with LuaJIT compatibility)
373 // as we are called from luaopen_lanes_core() already, and that would deadlock 372 // as we are called from luaopen_lanes_core() already, and that would deadlock
374 if( libs_[0] == '*' && libs_[1] == 0) 373 if (libs_[0] == '*' && libs_[1] == 0)
375 { 374 {
376 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END)); 375 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "opening ALL standard libraries\n" INDENT_END));
377 luaL_openlibs( L); 376 luaL_openlibs( L);
@@ -396,26 +395,25 @@ lua_State* luaG_newstate(Universe* U, Source from_, char const* libs_)
396 STACK_CHECK(L, 0); 395 STACK_CHECK(L, 0);
397 396
398 // scan all libraries, open them one by one 397 // scan all libraries, open them one by one
399 if( libs_) 398 if (libs_)
400 { 399 {
401 char const* p; 400 unsigned int len{ 0 };
402 unsigned int len = 0; 401 for (char const* p{ libs_ }; *p; p += len)
403 for( p = libs_; *p; p += len)
404 { 402 {
405 // skip delimiters ('.' can be part of name for "lanes.core") 403 // skip delimiters ('.' can be part of name for "lanes.core")
406 while( *p && !isalnum( *p) && *p != '.') 404 while( *p && !isalnum(*p) && *p != '.')
407 ++ p; 405 ++ p;
408 // skip name 406 // skip name
409 len = 0; 407 len = 0;
410 while( isalnum( p[len]) || p[len] == '.') 408 while( isalnum(p[len]) || p[len] == '.')
411 ++ len; 409 ++ len;
412 // open library 410 // open library
413 open1lib( DEBUGSPEW_PARAM_COMMA( U) L, p, len); 411 open1lib(DEBUGSPEW_PARAM_COMMA( U) L, p, len);
414 } 412 }
415 } 413 }
416 lua_gc( L, LUA_GCRESTART, 0); 414 lua_gc(L, LUA_GCRESTART, 0);
417 415
418 serialize_require( DEBUGSPEW_PARAM_COMMA( U) L); 416 serialize_require(DEBUGSPEW_PARAM_COMMA( U) L);
419 417
420 // call this after the base libraries are loaded and GC is restarted 418 // call this after the base libraries are loaded and GC is restarted
421 // will raise an error in from_ in case of problem 419 // will raise an error in from_ in case of problem
@@ -423,7 +421,7 @@ lua_State* luaG_newstate(Universe* U, Source from_, char const* libs_)
423 421
424 STACK_CHECK(L, 0); 422 STACK_CHECK(L, 0);
425 // after all this, register everything we find in our name<->function database 423 // after all this, register everything we find in our name<->function database
426 lua_pushglobaltable( L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack 424 lua_pushglobaltable(L); // Lua 5.2 no longer has LUA_GLOBALSINDEX: we must push globals table on the stack
427 STACK_CHECK(L, 1); 425 STACK_CHECK(L, 1);
428 populate_func_lookup_table(L, -1, nullptr); 426 populate_func_lookup_table(L, -1, nullptr);
429 427
diff --git a/src/state.h b/src/state.h
index e1c311a..059e163 100644
--- a/src/state.h
+++ b/src/state.h
@@ -8,12 +8,12 @@ class Universe;
8 8
9void serialize_require(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L); 9void serialize_require(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L);
10 10
11// ################################################################################################ 11// #################################################################################################
12 12
13[[nodiscard]] lua_State* create_state(Universe* U, lua_State* from_); 13[[nodiscard]] lua_State* create_state(Universe* U, lua_State* from_);
14[[nodiscard]] lua_State* luaG_newstate(Universe* U, Source _from, char const* libs); 14[[nodiscard]] lua_State* luaG_newstate(Universe* U, Source _from, char const* libs);
15 15
16// ################################################################################################ 16// #################################################################################################
17 17
18void initialize_on_state_create(Universe* U, lua_State* L); 18void initialize_on_state_create(Universe* U, lua_State* L);
19void call_on_state_create(Universe* U, lua_State* L, lua_State* from_, LookupMode mode_); 19void call_on_state_create(Universe* U, lua_State* L, lua_State* from_, LookupMode mode_);
diff --git a/src/threading.cpp b/src/threading.cpp
index 259693a..c7928c1 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -105,8 +105,8 @@ THE SOFTWARE.
105 105
106/*---=== Threading ===---*/ 106/*---=== Threading ===---*/
107 107
108// ################################################################################################## 108// #################################################################################################
109// ################################################################################################## 109// #################################################################################################
110#if THREADAPI == THREADAPI_WINDOWS 110#if THREADAPI == THREADAPI_WINDOWS
111 111
112static int const gs_prio_remap[] = 112static int const gs_prio_remap[] =
@@ -120,7 +120,7 @@ static int const gs_prio_remap[] =
120 THREAD_PRIORITY_TIME_CRITICAL 120 THREAD_PRIORITY_TIME_CRITICAL
121}; 121};
122 122
123// ############################################################################################### 123// #################################################################################################
124 124
125void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_) 125void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_)
126{ 126{
@@ -131,7 +131,7 @@ void THREAD_SET_PRIORITY(int prio_, [[maybe_unused]] bool sudo_)
131 } 131 }
132} 132}
133 133
134// ############################################################################################### 134// #################################################################################################
135 135
136void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_) 136void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] bool sudo_)
137{ 137{
@@ -142,7 +142,7 @@ void JTHREAD_SET_PRIORITY(std::jthread& thread_, int prio_, [[maybe_unused]] boo
142 } 142 }
143} 143}
144 144
145// ############################################################################################### 145// #################################################################################################
146 146
147void THREAD_SET_AFFINITY(unsigned int aff) 147void THREAD_SET_AFFINITY(unsigned int aff)
148{ 148{
@@ -152,7 +152,7 @@ void THREAD_SET_AFFINITY(unsigned int aff)
152 } 152 }
153} 153}
154 154
155// ############################################################################################### 155// #################################################################################################
156 156
157#if !defined __GNUC__ 157#if !defined __GNUC__
158//see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx 158//see http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
@@ -187,11 +187,11 @@ void THREAD_SETNAME(char const* _name)
187#endif // !__GNUC__ 187#endif // !__GNUC__
188} 188}
189 189
190// ################################################################################################## 190// #################################################################################################
191// ################################################################################################## 191// #################################################################################################
192#else // THREADAPI == THREADAPI_PTHREAD 192#else // THREADAPI == THREADAPI_PTHREAD
193// ################################################################################################## 193// #################################################################################################
194// ################################################################################################## 194// #################################################################################################
195 195
196// PThread (Linux, OS X, ...) 196// PThread (Linux, OS X, ...)
197// 197//
diff --git a/src/threading.h b/src/threading.h
index fc35730..e13932b 100644
--- a/src/threading.h
+++ b/src/threading.h
@@ -7,7 +7,7 @@
7#define THREADAPI_WINDOWS 1 7#define THREADAPI_WINDOWS 1
8#define THREADAPI_PTHREAD 2 8#define THREADAPI_PTHREAD 2
9 9
10#if( defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) 10#if (defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC))
11//#pragma message ( "THREADAPI_WINDOWS" ) 11//#pragma message ( "THREADAPI_WINDOWS" )
12#define THREADAPI THREADAPI_WINDOWS 12#define THREADAPI THREADAPI_WINDOWS
13#else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) 13#else // (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC)
@@ -17,8 +17,8 @@
17 17
18static constexpr int THREAD_PRIO_DEFAULT{ -999 }; 18static constexpr int THREAD_PRIO_DEFAULT{ -999 };
19 19
20// ################################################################################################## 20// #################################################################################################
21// ################################################################################################## 21// #################################################################################################
22#if THREADAPI == THREADAPI_WINDOWS 22#if THREADAPI == THREADAPI_WINDOWS
23 23
24#if defined(PLATFORM_XBOX) 24#if defined(PLATFORM_XBOX)
@@ -43,11 +43,11 @@ static constexpr int THREAD_PRIO_DEFAULT{ -999 };
43static constexpr int THREAD_PRIO_MIN{ -3 }; 43static constexpr int THREAD_PRIO_MIN{ -3 };
44static constexpr int THREAD_PRIO_MAX{ +3 }; 44static constexpr int THREAD_PRIO_MAX{ +3 };
45 45
46// ################################################################################################## 46// #################################################################################################
47// ################################################################################################## 47// #################################################################################################
48#else // THREADAPI == THREADAPI_PTHREAD 48#else // THREADAPI == THREADAPI_PTHREAD
49// ################################################################################################## 49// #################################################################################################
50// ################################################################################################## 50// #################################################################################################
51 51
52// PThread (Linux, OS X, ...) 52// PThread (Linux, OS X, ...)
53 53
@@ -65,8 +65,8 @@ static constexpr int THREAD_PRIO_MIN{ -3 };
65static constexpr int THREAD_PRIO_MAX{ +3 }; 65static constexpr int THREAD_PRIO_MAX{ +3 };
66 66
67#endif // THREADAPI == THREADAPI_PTHREAD 67#endif // THREADAPI == THREADAPI_PTHREAD
68// ################################################################################################## 68// #################################################################################################
69// ################################################################################################## 69// #################################################################################################
70 70
71void THREAD_SETNAME(char const* _name); 71void THREAD_SETNAME(char const* _name);
72void THREAD_SET_PRIORITY(int prio_, bool sudo_); 72void THREAD_SET_PRIORITY(int prio_, bool sudo_);
diff --git a/src/tools.cpp b/src/tools.cpp
index ad706be..cd25eda 100644
--- a/src/tools.cpp
+++ b/src/tools.cpp
@@ -35,13 +35,12 @@ THE SOFTWARE.
35 35
36#include "universe.h" 36#include "universe.h"
37 37
38// functions implemented in deep.c
39extern void push_registry_subtable( lua_State* L, UniqueKey key_);
40
41DEBUGSPEW_CODE(char const* const DebugSpewIndentScope::debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+"); 38DEBUGSPEW_CODE(char const* const DebugSpewIndentScope::debugspew_indent = "----+----!----+----!----+----!----+----!----+----!----+----!----+----!----+");
42 39
40// crc64/we of string "LOOKUPCACHE_REGKEY" generated at http://www.nitrxgen.net/hashgen/
41static constexpr UniqueKey LOOKUPCACHE_REGKEY{ 0x837a68dfc6fcb716ull };
43 42
44// ################################################################################################ 43// #################################################################################################
45 44
46/* 45/*
47 * 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
@@ -76,7 +75,7 @@ void push_registry_subtable_mode( lua_State* L, UniqueKey key_, const char* mode
76 ASSERT_L(lua_istable(L, -1)); 75 ASSERT_L(lua_istable(L, -1));
77} 76}
78 77
79// ################################################################################################ 78// #################################################################################################
80 79
81/* 80/*
82 * Push a registry subtable (keyed by unique 'key_') onto the stack. 81 * Push a registry subtable (keyed by unique 'key_') onto the stack.
@@ -87,60 +86,7 @@ void push_registry_subtable( lua_State* L, UniqueKey key_)
87 push_registry_subtable_mode(L, key_, nullptr); 86 push_registry_subtable_mode(L, key_, nullptr);
88} 87}
89 88
90// ################################################################################################ 89// #################################################################################################
91
92/*---=== luaG_dump ===---*/
93#ifdef _DEBUG
94void luaG_dump( lua_State* L)
95{
96 int top = lua_gettop( L);
97 int i;
98
99 fprintf( stderr, "\n\tDEBUG STACK:\n");
100
101 if( top == 0)
102 fprintf( stderr, "\t(none)\n");
103
104 for( i = 1; i <= top; ++ i)
105 {
106 LuaType type{ lua_type_as_enum(L, i) };
107
108 fprintf( stderr, "\t[%d]= (%s) ", i, lua_typename( L, type));
109
110 // Print item contents here...
111 //
112 // Note: this requires 'tostring()' to be defined. If it is NOT,
113 // enable it for more debugging.
114 //
115 STACK_CHECK_START_REL(L, 0);
116 STACK_GROW( L, 2);
117
118 lua_getglobal( L, "tostring");
119 //
120 // [-1]: tostring function, or nil
121
122 if( !lua_isfunction( L, -1))
123 {
124 fprintf( stderr, "('tostring' not available)");
125 }
126 else
127 {
128 lua_pushvalue( L, i);
129 lua_call( L, 1 /*args*/, 1 /*retvals*/);
130
131 // Don't trust the string contents
132 //
133 fprintf( stderr, "%s", lua_tostring( L, -1));
134 }
135 lua_pop( L, 1);
136 STACK_CHECK( L, 0);
137 fprintf( stderr, "\n");
138 }
139 fprintf( stderr, "\n");
140}
141#endif // _DEBUG
142
143// ################################################################################################
144 90
145// same as PUC-Lua l_alloc 91// same as PUC-Lua l_alloc
146extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_) 92extern "C" [[nodiscard]] static void* libc_lua_Alloc([[maybe_unused]] void* ud, [[maybe_unused]] void* ptr_, [[maybe_unused]] size_t osize_, size_t nsize_)
@@ -231,7 +177,7 @@ void initialize_allocator_function(Universe* U, lua_State* L)
231 STACK_CHECK(L, 1); 177 STACK_CHECK(L, 1);
232} 178}
233 179
234// ################################################################################################ 180// #################################################################################################
235 181
236[[nodiscard]] static int dummy_writer(lua_State* L, void const* p, size_t sz, void* ud) 182[[nodiscard]] static int dummy_writer(lua_State* L, void const* p, size_t sz, void* ud)
237{ 183{
@@ -239,7 +185,6 @@ void initialize_allocator_function(Universe* U, lua_State* L)
239 return 666; 185 return 666;
240} 186}
241 187
242
243/* 188/*
244 * differentiation between C, bytecode and JIT-fast functions 189 * differentiation between C, bytecode and JIT-fast functions
245 * 190 *
@@ -262,25 +207,25 @@ enum FuncSubType
262 FST_FastJIT 207 FST_FastJIT
263} ; 208} ;
264 209
265FuncSubType luaG_getfuncsubtype( lua_State *L, int _i) 210FuncSubType luaG_getfuncsubtype(lua_State* L, int _i)
266{ 211{
267 if( lua_tocfunction( L, _i)) 212 if (lua_tocfunction(L, _i)) // nullptr for LuaJIT-fast && bytecode functions
268 { 213 {
269 return FST_Native; 214 return FST_Native;
270 } 215 }
271 { 216 {
272 int mustpush = 0, dumpres; 217 int mustpush{ 0 };
273 if( lua_absindex( L, _i) != lua_gettop( L)) 218 if (lua_absindex(L, _i) != lua_gettop(L))
274 { 219 {
275 lua_pushvalue( L, _i); 220 lua_pushvalue(L, _i);
276 mustpush = 1; 221 mustpush = 1;
277 } 222 }
278 // the provided writer fails with code 666 223 // the provided writer fails with code 666
279 // therefore, anytime we get 666, this means that lua_dump() attempted a dump 224 // therefore, anytime we get 666, this means that lua_dump() attempted a dump
280 // all other cases mean this is either a C or LuaJIT-fast function 225 // all other cases mean this is either a C or LuaJIT-fast function
281 dumpres = lua504_dump(L, dummy_writer, nullptr, 0); 226 int const dumpres{ lua504_dump(L, dummy_writer, nullptr, 0) };
282 lua_pop( L, mustpush); 227 lua_pop(L, mustpush);
283 if( dumpres == 666) 228 if (dumpres == 666)
284 { 229 {
285 return FST_Bytecode; 230 return FST_Bytecode;
286 } 231 }
@@ -290,40 +235,28 @@ FuncSubType luaG_getfuncsubtype( lua_State *L, int _i)
290 235
291// ################################################################################################# 236// #################################################################################################
292 237
293[[nodiscard]] static lua_CFunction luaG_tocfunction(lua_State* L, int _i, FuncSubType* _out)
294{
295 lua_CFunction p = lua_tocfunction( L, _i);
296 *_out = luaG_getfuncsubtype( L, _i);
297 return p;
298}
299
300// crc64/we of string "LOOKUPCACHE_REGKEY" generated at http://www.nitrxgen.net/hashgen/
301static constexpr UniqueKey LOOKUPCACHE_REGKEY{ 0x837a68dfc6fcb716ull };
302
303// #################################################################################################
304
305// inspired from tconcat() in ltablib.c 238// inspired from tconcat() in ltablib.c
306[[nodiscard]] static char const* luaG_pushFQN(lua_State* L, int t, int last, size_t* length) 239[[nodiscard]] static char const* luaG_pushFQN(lua_State* L, int t, int last, size_t* length)
307{ 240{
308 int i = 1;
309 luaL_Buffer b; 241 luaL_Buffer b;
310 STACK_CHECK_START_REL(L, 0); 242 STACK_CHECK_START_REL(L, 0);
311 // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... 243 // Lua 5.4 pushes &b as light userdata on the stack. be aware of it...
312 luaL_buffinit( L, &b); // ... {} ... &b? 244 luaL_buffinit(L, &b); // ... {} ... &b?
313 for( ; i < last; ++ i) 245 int i = 1;
246 for (; i < last; ++i)
314 { 247 {
315 lua_rawgeti( L, t, i); 248 lua_rawgeti( L, t, i);
316 luaL_addvalue( &b); 249 luaL_addvalue( &b);
317 luaL_addlstring(&b, "/", 1); 250 luaL_addlstring(&b, "/", 1);
318 } 251 }
319 if( i == last) // add last value (if interval was not empty) 252 if (i == last) // add last value (if interval was not empty)
320 { 253 {
321 lua_rawgeti( L, t, i); 254 lua_rawgeti(L, t, i);
322 luaL_addvalue( &b); 255 luaL_addvalue(&b);
323 } 256 }
324 // &b is popped at that point (-> replaced by the result) 257 // &b is popped at that point (-> replaced by the result)
325 luaL_pushresult( &b); // ... {} ... "<result>" 258 luaL_pushresult(&b); // ... {} ... "<result>"
326 STACK_CHECK( L, 1); 259 STACK_CHECK(L, 1);
327 return lua_tolstring( L, -1, length); 260 return lua_tolstring( L, -1, length);
328} 261}
329 262
@@ -337,12 +270,12 @@ static constexpr UniqueKey LOOKUPCACHE_REGKEY{ 0x837a68dfc6fcb716ull };
337 * if we already had an entry of type [o] = ..., replace the name if the new one is shorter 270 * if we already had an entry of type [o] = ..., replace the name if the new one is shorter
338 * pops the processed object from the stack 271 * pops the processed object from the stack
339 */ 272 */
340static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L, int _ctx_base, int _depth) 273static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L, int _ctx_base, int _depth)
341{ 274{
342 // slot 1 in the stack contains the table that receives everything we found 275 // slot 1 in the stack contains the table that receives everything we found
343 int const dest = _ctx_base; 276 int const dest{ _ctx_base };
344 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i 277 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i
345 int const fqn = _ctx_base + 1; 278 int const fqn{ _ctx_base + 1 };
346 279
347 size_t prevNameLength, newNameLength; 280 size_t prevNameLength, newNameLength;
348 char const* prevName; 281 char const* prevName;
@@ -352,16 +285,16 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L
352 285
353 STACK_CHECK_START_REL(L, 0); 286 STACK_CHECK_START_REL(L, 0);
354 // first, raise an error if the function is already known 287 // first, raise an error if the function is already known
355 lua_pushvalue( L, -1); // ... {bfc} k o o 288 lua_pushvalue(L, -1); // ... {bfc} k o o
356 lua_rawget( L, dest); // ... {bfc} k o name? 289 lua_rawget(L, dest); // ... {bfc} k o name?
357 prevName = lua_tolstring( L, -1, &prevNameLength); // nullptr if we got nil (first encounter of this object) 290 prevName = lua_tolstring( L, -1, &prevNameLength); // nullptr if we got nil (first encounter of this object)
358 // push name in fqn stack (note that concatenation will crash if name is a not string or a number) 291 // push name in fqn stack (note that concatenation will crash if name is a not string or a number)
359 lua_pushvalue( L, -3); // ... {bfc} k o name? k 292 lua_pushvalue(L, -3); // ... {bfc} k o name? k
360 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER || lua_type( L, -1) == LUA_TSTRING); 293 ASSERT_L(lua_type(L, -1) == LUA_TNUMBER || lua_type(L, -1) == LUA_TSTRING);
361 ++ _depth; 294 ++_depth;
362 lua_rawseti( L, fqn, _depth); // ... {bfc} k o name? 295 lua_rawseti(L, fqn, _depth); // ... {bfc} k o name?
363 // generate name 296 // generate name
364 DEBUGSPEW_OR_NOT(newName, std::ignore) = luaG_pushFQN(L, fqn, _depth, &newNameLength);// ... {bfc} k o name? "f.q.n" 297 DEBUGSPEW_OR_NOT(newName, std::ignore) = luaG_pushFQN(L, fqn, _depth, &newNameLength); // ... {bfc} k o name? "f.q.n"
365 // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order 298 // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order
366 // on different VMs even when the tables are populated the exact same way. 299 // on different VMs even when the tables are populated the exact same way.
367 // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), 300 // When Lua is built with compatibility options (such as LUA_COMPAT_ALL),
@@ -375,36 +308,36 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L
375 { 308 {
376 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -3)), newName, prevName)); 309 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s' remained named '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -3)), newName, prevName));
377 // the previous name is 'smaller' than the one we just generated: keep it! 310 // the previous name is 'smaller' than the one we just generated: keep it!
378 lua_pop( L, 3); // ... {bfc} k 311 lua_pop(L, 3); // ... {bfc} k
379 } 312 }
380 else 313 else
381 { 314 {
382 // the name we generated is either the first one, or a better fit for our purposes 315 // the name we generated is either the first one, or a better fit for our purposes
383 if( prevName) 316 if (prevName)
384 { 317 {
385 // clear the previous name for the database to avoid clutter 318 // clear the previous name for the database to avoid clutter
386 lua_insert( L, -2); // ... {bfc} k o "f.q.n" prevName 319 lua_insert(L, -2); // ... {bfc} k o "f.q.n" prevName
387 // t[prevName] = nil 320 // t[prevName] = nil
388 lua_pushnil( L); // ... {bfc} k o "f.q.n" prevName nil 321 lua_pushnil(L); // ... {bfc} k o "f.q.n" prevName nil
389 lua_rawset( L, dest); // ... {bfc} k o "f.q.n" 322 lua_rawset(L, dest); // ... {bfc} k o "f.q.n"
390 } 323 }
391 else 324 else
392 { 325 {
393 lua_remove( L, -2); // ... {bfc} k o "f.q.n" 326 lua_remove(L, -2); // ... {bfc} k o "f.q.n"
394 } 327 }
395 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename( L, lua_type( L, -2)), newName)); 328 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%s '%s'\n" INDENT_END, lua_typename(L, lua_type( L, -2)), newName));
396 // prepare the stack for database feed 329 // prepare the stack for database feed
397 lua_pushvalue( L, -1); // ... {bfc} k o "f.q.n" "f.q.n" 330 lua_pushvalue(L, -1); // ... {bfc} k o "f.q.n" "f.q.n"
398 lua_pushvalue( L, -3); // ... {bfc} k o "f.q.n" "f.q.n" o 331 lua_pushvalue(L, -3); // ... {bfc} k o "f.q.n" "f.q.n" o
399 ASSERT_L( lua_rawequal( L, -1, -4)); 332 ASSERT_L(lua_rawequal(L, -1, -4));
400 ASSERT_L( lua_rawequal( L, -2, -3)); 333 ASSERT_L(lua_rawequal(L, -2, -3));
401 // t["f.q.n"] = o 334 // t["f.q.n"] = o
402 lua_rawset( L, dest); // ... {bfc} k o "f.q.n" 335 lua_rawset(L, dest); // ... {bfc} k o "f.q.n"
403 // t[o] = "f.q.n" 336 // t[o] = "f.q.n"
404 lua_rawset( L, dest); // ... {bfc} k 337 lua_rawset(L, dest); // ... {bfc} k
405 // remove table name from fqn stack 338 // remove table name from fqn stack
406 lua_pushnil( L); // ... {bfc} k nil 339 lua_pushnil(L); // ... {bfc} k nil
407 lua_rawseti( L, fqn, _depth); // ... {bfc} k 340 lua_rawseti(L, fqn, _depth); // ... {bfc} k
408 } 341 }
409 -- _depth; 342 -- _depth;
410 STACK_CHECK(L, -1); 343 STACK_CHECK(L, -1);
@@ -414,122 +347,121 @@ static void update_lookup_entry(DEBUGSPEW_PARAM_COMMA( Universe* U) lua_State* L
414 347
415static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L, int _ctx_base, int _i, int _depth) 348static void populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(Universe* U) lua_State* L, int _ctx_base, int _i, int _depth)
416{ 349{
417 lua_Integer visit_count;
418 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i 350 // slot 2 contains a table that, when concatenated, produces the fully qualified name of scanned elements in the table provided at slot _i
419 int const fqn = _ctx_base + 1; 351 int const fqn = _ctx_base + 1;
420 // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops 352 // slot 3 contains a cache that stores all already visited tables to avoid infinite recursion loops
421 int const cache = _ctx_base + 2; 353 int const cache = _ctx_base + 2;
422 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search) 354 // we need to remember subtables to process them after functions encountered at the current depth (breadth-first search)
423 int const breadth_first_cache = lua_gettop( L) + 1; 355 int const breadth_first_cache = lua_gettop(L) + 1;
424 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END)); 356 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "populate_func_lookup_table_recur()\n" INDENT_END));
425 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); 357 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
426 358
427 STACK_GROW( L, 6); 359 STACK_GROW(L, 6);
428 // slot _i contains a table where we search for functions (or a full userdata with a metatable) 360 // slot _i contains a table where we search for functions (or a full userdata with a metatable)
429 STACK_CHECK_START_REL(L, 0); // ... {_i} 361 STACK_CHECK_START_REL(L, 0); // ... {_i}
430 362
431 // if object is a userdata, replace it by its metatable 363 // if object is a userdata, replace it by its metatable
432 if( lua_type( L, _i) == LUA_TUSERDATA) 364 if (lua_type(L, _i) == LUA_TUSERDATA)
433 { 365 {
434 lua_getmetatable( L, _i); // ... {_i} mt 366 lua_getmetatable(L, _i); // ... {_i} mt
435 lua_replace( L, _i); // ... {_i} 367 lua_replace(L, _i); // ... {_i}
436 } 368 }
437 369
438 // if table is already visited, we are done 370 // if table is already visited, we are done
439 lua_pushvalue( L, _i); // ... {_i} {} 371 lua_pushvalue(L, _i); // ... {_i} {}
440 lua_rawget( L, cache); // ... {_i} nil|n 372 lua_rawget(L, cache); // ... {_i} nil|n
441 visit_count = lua_tointeger( L, -1); // 0 if nil, else n 373 lua_Integer visit_count{ lua_tointeger(L, -1) }; // 0 if nil, else n
442 lua_pop( L, 1); // ... {_i} 374 lua_pop(L, 1); // ... {_i}
443 STACK_CHECK( L, 0); 375 STACK_CHECK(L, 0);
444 if( visit_count > 0) 376 if (visit_count > 0)
445 { 377 {
446 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END)); 378 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "already visited\n" INDENT_END));
447 return; 379 return;
448 } 380 }
449 381
450 // remember we visited this table (1-visit count) 382 // remember we visited this table (1-visit count)
451 lua_pushvalue( L, _i); // ... {_i} {} 383 lua_pushvalue(L, _i); // ... {_i} {}
452 lua_pushinteger( L, visit_count + 1); // ... {_i} {} 1 384 lua_pushinteger(L, visit_count + 1); // ... {_i} {} 1
453 lua_rawset( L, cache); // ... {_i} 385 lua_rawset(L, cache); // ... {_i}
454 STACK_CHECK( L, 0); 386 STACK_CHECK(L, 0);
455 387
456 // this table is at breadth_first_cache index 388 // this table is at breadth_first_cache index
457 lua_newtable( L); // ... {_i} {bfc} 389 lua_newtable(L); // ... {_i} {bfc}
458 ASSERT_L( lua_gettop( L) == breadth_first_cache); 390 ASSERT_L( lua_gettop(L) == breadth_first_cache);
459 // iterate over all entries in the processed table 391 // iterate over all entries in the processed table
460 lua_pushnil( L); // ... {_i} {bfc} nil 392 lua_pushnil(L); // ... {_i} {bfc} nil
461 while( lua_next( L, _i) != 0) // ... {_i} {bfc} k v 393 while( lua_next(L, _i) != 0) // ... {_i} {bfc} k v
462 { 394 {
463 // just for debug, not actually needed 395 // just for debug, not actually needed
464 //char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string"; 396 //char const* key = (lua_type(L, -2) == LUA_TSTRING) ? lua_tostring(L, -2) : "not a string";
465 // subtable: process it recursively 397 // subtable: process it recursively
466 if( lua_istable( L, -1)) // ... {_i} {bfc} k {} 398 if (lua_istable(L, -1)) // ... {_i} {bfc} k {}
467 { 399 {
468 // increment visit count to make sure we will actually scan it at this recursive level 400 // increment visit count to make sure we will actually scan it at this recursive level
469 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} 401 lua_pushvalue(L, -1); // ... {_i} {bfc} k {} {}
470 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} {} 402 lua_pushvalue(L, -1); // ... {_i} {bfc} k {} {} {}
471 lua_rawget( L, cache); // ... {_i} {bfc} k {} {} n? 403 lua_rawget(L, cache); // ... {_i} {bfc} k {} {} n?
472 visit_count = lua_tointeger( L, -1) + 1; // 1 if we got nil, else n+1 404 visit_count = lua_tointeger(L, -1) + 1; // 1 if we got nil, else n+1
473 lua_pop( L, 1); // ... {_i} {bfc} k {} {} 405 lua_pop(L, 1); // ... {_i} {bfc} k {} {}
474 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n 406 lua_pushinteger(L, visit_count); // ... {_i} {bfc} k {} {} n
475 lua_rawset( L, cache); // ... {_i} {bfc} k {} 407 lua_rawset(L, cache); // ... {_i} {bfc} k {}
476 // store the table in the breadth-first cache 408 // store the table in the breadth-first cache
477 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k 409 lua_pushvalue(L, -2); // ... {_i} {bfc} k {} k
478 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k {} 410 lua_pushvalue(L, -2); // ... {_i} {bfc} k {} k {}
479 lua_rawset( L, breadth_first_cache); // ... {_i} {bfc} k {} 411 lua_rawset(L, breadth_first_cache); // ... {_i} {bfc} k {}
480 // generate a name, and if we already had one name, keep whichever is the shorter 412 // generate a name, and if we already had one name, keep whichever is the shorter
481 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, _depth); // ... {_i} {bfc} k 413 update_lookup_entry( DEBUGSPEW_PARAM_COMMA(U) L, _ctx_base, _depth); // ... {_i} {bfc} k
482 } 414 }
483 else if( lua_isfunction( L, -1) && (luaG_getfuncsubtype( L, -1) != FST_Bytecode)) // ... {_i} {bfc} k func 415 else if (lua_isfunction(L, -1) && (luaG_getfuncsubtype(L, -1) != FST_Bytecode)) // ... {_i} {bfc} k func
484 { 416 {
485 // generate a name, and if we already had one name, keep whichever is the shorter 417 // generate a name, and if we already had one name, keep whichever is the shorter
486 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, _depth); // ... {_i} {bfc} k 418 update_lookup_entry( DEBUGSPEW_PARAM_COMMA(U) L, _ctx_base, _depth); // ... {_i} {bfc} k
487 } 419 }
488 else 420 else
489 { 421 {
490 lua_pop( L, 1); // ... {_i} {bfc} k 422 lua_pop(L, 1); // ... {_i} {bfc} k
491 } 423 }
492 STACK_CHECK( L, 2); 424 STACK_CHECK(L, 2);
493 } 425 }
494 // now process the tables we encountered at that depth 426 // now process the tables we encountered at that depth
495 ++ _depth; 427 ++ _depth;
496 lua_pushnil( L); // ... {_i} {bfc} nil 428 lua_pushnil(L); // ... {_i} {bfc} nil
497 while( lua_next( L, breadth_first_cache) != 0) // ... {_i} {bfc} k {} 429 while (lua_next(L, breadth_first_cache) != 0) // ... {_i} {bfc} k {}
498 { 430 {
499 DEBUGSPEW_CODE( char const* key = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : "not a string"); 431 DEBUGSPEW_CODE(char const* key = (lua_type(L, -2) == LUA_TSTRING) ? lua_tostring(L, -2) : "not a string");
500 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key)); 432 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "table '%s'\n" INDENT_END, key));
501 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); 433 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
502 // un-visit this table in case we do need to process it 434 // un-visit this table in case we do need to process it
503 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} 435 lua_pushvalue(L, -1); // ... {_i} {bfc} k {} {}
504 lua_rawget( L, cache); // ... {_i} {bfc} k {} n 436 lua_rawget(L, cache); // ... {_i} {bfc} k {} n
505 ASSERT_L( lua_type( L, -1) == LUA_TNUMBER); 437 ASSERT_L(lua_type(L, -1) == LUA_TNUMBER);
506 visit_count = lua_tointeger( L, -1) - 1; 438 visit_count = lua_tointeger(L, -1) - 1;
507 lua_pop( L, 1); // ... {_i} {bfc} k {} 439 lua_pop(L, 1); // ... {_i} {bfc} k {}
508 lua_pushvalue( L, -1); // ... {_i} {bfc} k {} {} 440 lua_pushvalue(L, -1); // ... {_i} {bfc} k {} {}
509 if( visit_count > 0) 441 if (visit_count > 0)
510 { 442 {
511 lua_pushinteger( L, visit_count); // ... {_i} {bfc} k {} {} n 443 lua_pushinteger(L, visit_count); // ... {_i} {bfc} k {} {} n
512 } 444 }
513 else 445 else
514 { 446 {
515 lua_pushnil( L); // ... {_i} {bfc} k {} {} nil 447 lua_pushnil(L); // ... {_i} {bfc} k {} {} nil
516 } 448 }
517 lua_rawset( L, cache); // ... {_i} {bfc} k {} 449 lua_rawset(L, cache); // ... {_i} {bfc} k {}
518 // push table name in fqn stack (note that concatenation will crash if name is a not string!) 450 // push table name in fqn stack (note that concatenation will crash if name is a not string!)
519 lua_pushvalue( L, -2); // ... {_i} {bfc} k {} k 451 lua_pushvalue(L, -2); // ... {_i} {bfc} k {} k
520 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} k {} 452 lua_rawseti(L, fqn, _depth); // ... {_i} {bfc} k {}
521 populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, _ctx_base, lua_gettop( L), _depth); 453 populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U) L, _ctx_base, lua_gettop(L), _depth);
522 lua_pop( L, 1); // ... {_i} {bfc} k 454 lua_pop(L, 1); // ... {_i} {bfc} k
523 STACK_CHECK( L, 2); 455 STACK_CHECK(L, 2);
524 } 456 }
525 // remove table name from fqn stack 457 // remove table name from fqn stack
526 lua_pushnil( L); // ... {_i} {bfc} nil 458 lua_pushnil(L); // ... {_i} {bfc} nil
527 lua_rawseti( L, fqn, _depth); // ... {_i} {bfc} 459 lua_rawseti(L, fqn, _depth); // ... {_i} {bfc}
528 -- _depth; 460 -- _depth;
529 // we are done with our cache 461 // we are done with our cache
530 lua_pop( L, 1); // ... {_i} 462 lua_pop(L, 1); // ... {_i}
531 STACK_CHECK( L, 0); 463 STACK_CHECK(L, 0);
532 // we are done // ... {_i} {bfc} 464 // we are done // ... {_i} {bfc}
533} 465}
534 466
535// ################################################################################################# 467// #################################################################################################
@@ -542,59 +474,59 @@ void populate_func_lookup_table(lua_State* L, int i_, char const* name_)
542 int const ctx_base = lua_gettop(L) + 1; 474 int const ctx_base = lua_gettop(L) + 1;
543 int const in_base = lua_absindex(L, i_); 475 int const in_base = lua_absindex(L, i_);
544 int start_depth = 0; 476 int start_depth = 0;
545 DEBUGSPEW_CODE( Universe* U = universe_get( L)); 477 DEBUGSPEW_CODE(Universe* U = universe_get(L));
546 DEBUGSPEW_CODE( fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "nullptr")); 478 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "%p: populate_func_lookup_table('%s')\n" INDENT_END, L, name_ ? name_ : "nullptr"));
547 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U }); 479 DEBUGSPEW_CODE(DebugSpewIndentScope scope{ U });
548 STACK_GROW(L, 3); 480 STACK_GROW(L, 3);
549 STACK_CHECK_START_REL(L, 0); 481 STACK_CHECK_START_REL(L, 0);
550 LOOKUP_REGKEY.pushValue(L); // {} 482 LOOKUP_REGKEY.pushValue(L); // {}
551 STACK_CHECK( L, 1); 483 STACK_CHECK(L, 1);
552 ASSERT_L( lua_istable( L, -1)); 484 ASSERT_L(lua_istable(L, -1));
553 if( lua_type( L, in_base) == LUA_TFUNCTION) // for example when a module is a simple function 485 if (lua_type(L, in_base) == LUA_TFUNCTION) // for example when a module is a simple function
554 { 486 {
555 name_ = name_ ? name_ : "nullptr"; 487 name_ = name_ ? name_ : "nullptr";
556 lua_pushvalue( L, in_base); // {} f 488 lua_pushvalue(L, in_base); // {} f
557 lua_pushstring( L, name_); // {} f _name 489 lua_pushstring(L, name_); // {} f _name
558 lua_rawset( L, -3); // {} 490 lua_rawset(L, -3); // {}
559 lua_pushstring( L, name_); // {} _name 491 lua_pushstring(L, name_); // {} _name
560 lua_pushvalue( L, in_base); // {} _name f 492 lua_pushvalue(L, in_base); // {} _name f
561 lua_rawset( L, -3); // {} 493 lua_rawset(L, -3); // {}
562 lua_pop( L, 1); // 494 lua_pop(L, 1); //
563 } 495 }
564 else if( lua_type( L, in_base) == LUA_TTABLE) 496 else if (lua_type(L, in_base) == LUA_TTABLE)
565 { 497 {
566 lua_newtable(L); // {} {fqn} 498 lua_newtable(L); // {} {fqn}
567 if( name_) 499 if (name_)
568 { 500 {
569 STACK_CHECK( L, 2); 501 STACK_CHECK(L, 2);
570 lua_pushstring( L, name_); // {} {fqn} "name" 502 lua_pushstring(L, name_); // {} {fqn} "name"
571 // generate a name, and if we already had one name, keep whichever is the shorter 503 // generate a name, and if we already had one name, keep whichever is the shorter
572 lua_pushvalue( L, in_base); // {} {fqn} "name" t 504 lua_pushvalue(L, in_base); // {} {fqn} "name" t
573 update_lookup_entry( DEBUGSPEW_PARAM_COMMA( U) L, ctx_base, start_depth); // {} {fqn} "name" 505 update_lookup_entry(DEBUGSPEW_PARAM_COMMA(U) L, ctx_base, start_depth); // {} {fqn} "name"
574 // don't forget to store the name at the bottom of the fqn stack 506 // don't forget to store the name at the bottom of the fqn stack
575 ++ start_depth; 507 ++ start_depth;
576 lua_rawseti( L, -2, start_depth); // {} {fqn} 508 lua_rawseti(L, -2, start_depth); // {} {fqn}
577 STACK_CHECK( L, 2); 509 STACK_CHECK(L, 2);
578 } 510 }
579 // retrieve the cache, create it if we haven't done it yet 511 // retrieve the cache, create it if we haven't done it yet
580 LOOKUPCACHE_REGKEY.pushValue(L); // {} {fqn} {cache}? 512 LOOKUPCACHE_REGKEY.pushValue(L); // {} {fqn} {cache}?
581 if( lua_isnil( L, -1)) 513 if (lua_isnil(L, -1))
582 { 514 {
583 lua_pop( L, 1); // {} {fqn} 515 lua_pop(L, 1); // {} {fqn}
584 lua_newtable( L); // {} {fqn} {cache} 516 lua_newtable(L); // {} {fqn} {cache}
585 LOOKUPCACHE_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); }); 517 LOOKUPCACHE_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); });
586 STACK_CHECK( L, 3); 518 STACK_CHECK(L, 3);
587 } 519 }
588 // process everything we find in that table, filling in lookup data for all functions and tables we see there 520 // process everything we find in that table, filling in lookup data for all functions and tables we see there
589 populate_func_lookup_table_recur( DEBUGSPEW_PARAM_COMMA( U) L, ctx_base, in_base, start_depth); 521 populate_func_lookup_table_recur(DEBUGSPEW_PARAM_COMMA(U) L, ctx_base, in_base, start_depth);
590 lua_pop( L, 3); 522 lua_pop(L, 3);
591 } 523 }
592 else 524 else
593 { 525 {
594 lua_pop( L, 1); // 526 lua_pop(L, 1); //
595 (void) luaL_error( L, "unsupported module type %s", lua_typename( L, lua_type( L, in_base))); 527 luaL_error(L, "unsupported module type %s", lua_typename(L, lua_type(L, in_base))); // doesn't return
596 } 528 }
597 STACK_CHECK( L, 0); 529 STACK_CHECK(L, 0);
598} 530}
599 531
600// ################################################################################################# 532// #################################################################################################
@@ -609,38 +541,34 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
609*/ 541*/
610[[nodiscard]] static lua_Integer get_mt_id(Universe* U, lua_State* L, int i) 542[[nodiscard]] static lua_Integer get_mt_id(Universe* U, lua_State* L, int i)
611{ 543{
612 lua_Integer id; 544 i = lua_absindex(L, i);
613 545
614 i = lua_absindex( L, i); 546 STACK_GROW(L, 3);
615
616 STACK_GROW( L, 3);
617 547
618 STACK_CHECK_START_REL(L, 0); 548 STACK_CHECK_START_REL(L, 0);
619 push_registry_subtable( L, REG_MTID); // ... _R[REG_MTID] 549 push_registry_subtable(L, REG_MTID); // ... _R[REG_MTID]
620 lua_pushvalue( L, i); // ... _R[REG_MTID] {mt} 550 lua_pushvalue(L, i); // ... _R[REG_MTID] {mt}
621 lua_rawget( L, -2); // ... _R[REG_MTID] mtk? 551 lua_rawget(L, -2); // ... _R[REG_MTID] mtk?
622 552
623 id = lua_tointeger( L, -1); // 0 for nil 553 lua_Integer id{ lua_tointeger(L, -1) }; // 0 for nil
624 lua_pop( L, 1); // ... _R[REG_MTID] 554 lua_pop(L, 1); // ... _R[REG_MTID]
625 STACK_CHECK( L, 1); 555 STACK_CHECK(L, 1);
626 556
627 if( id == 0) 557 if (id == 0)
628 { 558 {
629 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);
630 560
631 /* Create two-way references: id_uint <-> table 561 // Create two-way references: id_uint <-> table
632 */ 562 lua_pushvalue(L, i); // ... _R[REG_MTID] {mt}
633 lua_pushvalue( L, i); // ... _R[REG_MTID] {mt} 563 lua_pushinteger(L, id); // ... _R[REG_MTID] {mt} id
634 lua_pushinteger( L, id); // ... _R[REG_MTID] {mt} id 564 lua_rawset(L, -3); // ... _R[REG_MTID]
635 lua_rawset( L, -3); // ... _R[REG_MTID]
636 565
637 lua_pushinteger( L, id); // ... _R[REG_MTID] id 566 lua_pushinteger(L, id); // ... _R[REG_MTID] id
638 lua_pushvalue( L, i); // ... _R[REG_MTID] id {mt} 567 lua_pushvalue(L, i); // ... _R[REG_MTID] id {mt}
639 lua_rawset( L, -3); // ... _R[REG_MTID] 568 lua_rawset(L, -3); // ... _R[REG_MTID]
640 } 569 }
641 lua_pop( L, 1); // ... 570 lua_pop(L, 1); // ...
642 571 STACK_CHECK(L, 0);
643 STACK_CHECK( L, 0);
644 572
645 return id; 573 return id;
646} 574}
@@ -684,7 +612,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
684 if (mode_ == LookupMode::FromKeeper) 612 if (mode_ == LookupMode::FromKeeper)
685 { 613 {
686 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!
687 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)
688 { 616 {
689 lua_getupvalue( L, i, 1); // ... v ... "f.q.n" 617 lua_getupvalue( L, i, 1); // ... v ... "f.q.n"
690 } 618 }
@@ -722,7 +650,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
722 typewhat = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : luaL_typename( L, -2); 650 typewhat = (lua_type( L, -2) == LUA_TSTRING) ? lua_tostring( L, -2) : luaL_typename( L, -2);
723 // second return value can be nil if the table was not found 651 // second return value can be nil if the table was not found
724 // probable reason: the function was removed from the source Lua state before Lanes was required. 652 // probable reason: the function was removed from the source Lua state before Lanes was required.
725 if( lua_isnil( L, -1)) 653 if (lua_isnil( L, -1))
726 { 654 {
727 gotchaA = " referenced by"; 655 gotchaA = " referenced by";
728 gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)"; 656 gotchaB = "\n(did you remove it from the source Lua state before requiring Lanes?)";
@@ -744,22 +672,20 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
744 672
745// ################################################################################################# 673// #################################################################################################
746 674
747/* 675// Push a looked-up table, or nothing if we found nothing
748 * Push a looked-up table, or nothing if we found nothing 676[[nodiscard]] bool InterCopyContext::lookup_table() const
749 */
750[[nodiscard]] static bool lookup_table(Dest L2, Source L1, SourceIndex i_, LookupMode mode_, char const* upName_)
751{ 677{
752 // get the name of the table we want to send 678 // get the name of the table we want to send
753 size_t len; 679 size_t len;
754 char const* fqn = find_lookup_name(L1, i_, mode_, upName_, &len); 680 char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len);
755 if (nullptr == fqn) // name not found, it is some user-created table 681 if (nullptr == fqn) // name not found, it is some user-created table
756 { 682 {
757 return false; 683 return false;
758 } 684 }
759 // push the equivalent table in the destination's stack, retrieved from the lookup table 685 // push the equivalent table in the destination's stack, retrieved from the lookup table
760 STACK_CHECK_START_REL(L2, 0); // L // L2 686 STACK_CHECK_START_REL(L2, 0); // L // L2
761 STACK_GROW( L2, 3); // up to 3 slots are necessary on error 687 STACK_GROW(L2, 3); // up to 3 slots are necessary on error
762 switch( mode_) 688 switch (mode)
763 { 689 {
764 default: // shouldn't happen, in theory... 690 default: // shouldn't happen, in theory...
765 luaL_error(L1, "internal error: unknown lookup mode"); // doesn't return 691 luaL_error(L1, "internal error: unknown lookup mode"); // doesn't return
@@ -780,7 +706,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
780 lua_rawget(L2, -2); // {} t 706 lua_rawget(L2, -2); // {} t
781 // we accept destination lookup failures in the case of transfering the Lanes body function (this will result in the source table being cloned instead) 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)
782 // but not when we extract something out of a keeper, as there is nothing to clone! 708 // but not when we extract something out of a keeper, as there is nothing to clone!
783 if (lua_isnil(L2, -1) && mode_ == LookupMode::LaneBody) 709 if (lua_isnil(L2, -1) && mode == LookupMode::LaneBody)
784 { 710 {
785 lua_pop(L2, 2); // 711 lua_pop(L2, 2); //
786 STACK_CHECK(L2, 0); 712 STACK_CHECK(L2, 0);
@@ -797,7 +723,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
797 lua_pop(L2, 1); // {} t 723 lua_pop(L2, 1); // {} t
798 // 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 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
799 luaL_error( 725 luaL_error(
800 (mode_ == LookupMode::FromKeeper) ? L2 : L1 726 (mode == LookupMode::FromKeeper) ? L2 : L1
801 , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database." 727 , "INTERNAL ERROR IN %s: table '%s' not found in %s destination transfer database."
802 , from ? from : "main" 728 , from ? from : "main"
803 , fqn 729 , fqn
@@ -808,7 +734,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
808 lua_remove(L2, -2); // t 734 lua_remove(L2, -2); // t
809 break; 735 break;
810 } 736 }
811 STACK_CHECK( L2, 1); 737 STACK_CHECK(L2, 1);
812 return true; 738 return true;
813} 739}
814 740
@@ -877,7 +803,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
877 lua_pushvalue(L, -1); // o "r" {c} {fqn} ... {?} {?} 803 lua_pushvalue(L, -1); // o "r" {c} {fqn} ... {?} {?}
878 lua_rawget(L, cache); // o "r" {c} {fqn} ... {?} nil/1 804 lua_rawget(L, cache); // o "r" {c} {fqn} ... {?} nil/1
879 // if table is already visited, we are done 805 // if table is already visited, we are done
880 if( !lua_isnil(L, -1)) 806 if (!lua_isnil(L, -1))
881 { 807 {
882 lua_pop(L, 1); // o "r" {c} {fqn} ... {?} 808 lua_pop(L, 1); // o "r" {c} {fqn} ... {?}
883 return shortest_; 809 return shortest_;
@@ -902,7 +828,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
902 { 828 {
903 STACK_CHECK(L, 2); 829 STACK_CHECK(L, 2);
904 // update shortest name 830 // update shortest name
905 if( depth_ < shortest_) 831 if (depth_ < shortest_)
906 { 832 {
907 shortest_ = depth_; 833 shortest_ = depth_;
908 std::ignore = luaG_pushFQN(L, fqn, depth_, nullptr); // o "r" {c} {fqn} ... {?} k v "fqn" 834 std::ignore = luaG_pushFQN(L, fqn, depth_, nullptr); // o "r" {c} {fqn} ... {?} k v "fqn"
@@ -924,7 +850,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
924 // search in the table's metatable too 850 // search in the table's metatable too
925 if (lua_getmetatable(L, -1)) // o "r" {c} {fqn} ... {?} k {} {mt} 851 if (lua_getmetatable(L, -1)) // o "r" {c} {fqn} ... {?} k {} {mt}
926 { 852 {
927 if( lua_istable(L, -1)) 853 if (lua_istable(L, -1))
928 { 854 {
929 ++ depth_; 855 ++ depth_;
930 lua_pushliteral(L, "__metatable"); // o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" 856 lua_pushliteral(L, "__metatable"); // o "r" {c} {fqn} ... {?} k {} {mt} "__metatable"
@@ -966,7 +892,7 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
966 int uvi = 1; 892 int uvi = 1;
967 while (lua_getiuservalue(L, -1, uvi) != LUA_TNONE) // o "r" {c} {fqn} ... {?} k U {u} 893 while (lua_getiuservalue(L, -1, uvi) != LUA_TNONE) // o "r" {c} {fqn} ... {?} k U {u}
968 { 894 {
969 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
970 { 896 {
971 ++ depth_; 897 ++ depth_;
972 lua_pushliteral(L, "uservalue"); // o "r" {c} {fqn} ... {?} k v {u} "uservalue" 898 lua_pushliteral(L, "uservalue"); // o "r" {c} {fqn} ... {?} k v {u} "uservalue"
@@ -1010,13 +936,13 @@ static constexpr UniqueKey REG_MTID{ 0x2e68f9b4751584dcull };
1010int luaG_nameof( lua_State* L) 936int luaG_nameof( lua_State* L)
1011{ 937{
1012 int what = lua_gettop( L); 938 int what = lua_gettop( L);
1013 if( what > 1) 939 if (what > 1)
1014 { 940 {
1015 luaL_argerror( L, what, "too many arguments."); 941 luaL_argerror( L, what, "too many arguments.");
1016 } 942 }
1017 943
1018 // nil, boolean, light userdata, number and string aren't identifiable 944 // nil, boolean, light userdata, number and string aren't identifiable
1019 if( lua_type( L, 1) < LUA_TTABLE) 945 if (lua_type( L, 1) < LUA_TTABLE)
1020 { 946 {
1021 lua_pushstring( L, luaL_typename( L, 1)); // o "type" 947 lua_pushstring( L, luaL_typename( L, 1)); // o "type"
1022 lua_insert( L, -2); // "type" o 948 lua_insert( L, -2); // "type" o
@@ -1036,7 +962,7 @@ int luaG_nameof( lua_State* L)
1036 // this is where we start the search 962 // this is where we start the search
1037 lua_pushglobaltable( L); // o nil {c} {fqn} _G 963 lua_pushglobaltable( L); // o nil {c} {fqn} _G
1038 (void) discover_object_name_recur( L, 6666, 1); 964 (void) discover_object_name_recur( L, 6666, 1);
1039 if( lua_isnil( L, 2)) // try again with registry, just in case... 965 if (lua_isnil( L, 2)) // try again with registry, just in case...
1040 { 966 {
1041 lua_pop( L, 1); // o nil {c} {fqn} 967 lua_pop( L, 1); // o nil {c} {fqn}
1042 lua_pushliteral( L, "_R"); // o nil {c} {fqn} "_R" 968 lua_pushliteral( L, "_R"); // o nil {c} {fqn} "_R"
@@ -1056,56 +982,56 @@ int luaG_nameof( lua_State* L)
1056/* 982/*
1057 * Push a looked-up native/LuaJIT function. 983 * Push a looked-up native/LuaJIT function.
1058 */ 984 */
1059static void lookup_native_func(lua_State* L2, lua_State* L, int i, LookupMode mode_, char const* upName_) 985void InterCopyContext::lookup_native_func() const
1060{ 986{
1061 // get the name of the function we want to send 987 // get the name of the function we want to send
1062 size_t len; 988 size_t len;
1063 char const* fqn = find_lookup_name( L, i, mode_, upName_, &len); 989 char const* fqn = find_lookup_name(L1, L1_i, mode, name, &len);
1064 // push the equivalent function in the destination's stack, retrieved from the lookup table 990 // push the equivalent function in the destination's stack, retrieved from the lookup table
1065 STACK_CHECK_START_REL(L2, 0); // L // L2 991 STACK_CHECK_START_REL(L2, 0); // L1 // L2
1066 STACK_GROW( L2, 3); // up to 3 slots are necessary on error 992 STACK_GROW(L2, 3); // up to 3 slots are necessary on error
1067 switch( mode_) 993 switch (mode)
1068 { 994 {
1069 default: // shouldn't happen, in theory... 995 default: // shouldn't happen, in theory...
1070 (void) luaL_error( L, "internal error: unknown lookup mode"); 996 luaL_error(L1, "internal error: unknown lookup mode"); // doesn't return
1071 return; 997 break;
1072 998
1073 case LookupMode::ToKeeper: 999 case LookupMode::ToKeeper:
1074 // push a sentinel closure that holds the lookup name as upvalue 1000 // push a sentinel closure that holds the lookup name as upvalue
1075 lua_pushlstring( L2, fqn, len); // "f.q.n" 1001 lua_pushlstring(L2, fqn, len); // "f.q.n"
1076 lua_pushcclosure( L2, func_lookup_sentinel, 1); // f 1002 lua_pushcclosure(L2, func_lookup_sentinel, 1); // f
1077 break; 1003 break;
1078 1004
1079 case LookupMode::LaneBody: 1005 case LookupMode::LaneBody:
1080 case LookupMode::FromKeeper: 1006 case LookupMode::FromKeeper:
1081 LOOKUP_REGKEY.pushValue(L2); // {} 1007 LOOKUP_REGKEY.pushValue(L2); // {}
1082 STACK_CHECK( L2, 1); 1008 STACK_CHECK(L2, 1);
1083 ASSERT_L( lua_istable( L2, -1)); 1009 _ASSERT_L(L1, lua_istable(L2, -1));
1084 lua_pushlstring( L2, fqn, len); // {} "f.q.n" 1010 lua_pushlstring(L2, fqn, len); // {} "f.q.n"
1085 lua_rawget( L2, -2); // {} f 1011 lua_rawget(L2, -2); // {} f
1086 // nil means we don't know how to transfer stuff: user should do something 1012 // nil means we don't know how to transfer stuff: user should do something
1087 // anything other than function or table should not happen! 1013 // anything other than function or table should not happen!
1088 if( !lua_isfunction( L2, -1) && !lua_istable( L2, -1)) 1014 if (!lua_isfunction( L2, -1) && !lua_istable( L2, -1))
1089 { 1015 {
1090 char const* from, * to; 1016 char const* from, * to;
1091 lua_getglobal( L, "decoda_name"); // ... f ... decoda_name 1017 lua_getglobal(L1, "decoda_name"); // ... f ... decoda_name
1092 from = lua_tostring( L, -1); 1018 from = lua_tostring(L1, -1);
1093 lua_pop( L, 1); // ... f ... 1019 lua_pop(L1, 1); // ... f ...
1094 lua_getglobal( L2, "decoda_name"); // {} f decoda_name 1020 lua_getglobal(L2, "decoda_name"); // {} f decoda_name
1095 to = lua_tostring( L2, -1); 1021 to = lua_tostring(L2, -1);
1096 lua_pop( L2, 1); // {} f 1022 lua_pop(L2, 1); // {} f
1097 // 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 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
1098 (void) luaL_error( 1024 (void) luaL_error(
1099 (mode_ == LookupMode::FromKeeper) ? L2 : L 1025 (mode == LookupMode::FromKeeper) ? L2 : L1
1100 , "%s%s: function '%s' not found in %s destination transfer database." 1026 , "%s%s: function '%s' not found in %s destination transfer database."
1101 , lua_isnil( L2, -1) ? "" : "INTERNAL ERROR IN " 1027 , lua_isnil(L2, -1) ? "" : "INTERNAL ERROR IN "
1102 , from ? from : "main" 1028 , from ? from : "main"
1103 , fqn 1029 , fqn
1104 , to ? to : "main" 1030 , to ? to : "main"
1105 ); 1031 );
1106 return; 1032 return;
1107 } 1033 }
1108 lua_remove( L2, -2); // f 1034 lua_remove(L2, -2); // f
1109 break; 1035 break;
1110 1036
1111 /* keep it in case I need it someday, who knows... 1037 /* keep it in case I need it someday, who knows...
@@ -1115,7 +1041,7 @@ static void lookup_native_func(lua_State* L2, lua_State* L, int i, LookupMode mo
1115 char const* upname; 1041 char const* upname;
1116 lua_CFunction f = lua_tocfunction( L, i); 1042 lua_CFunction f = lua_tocfunction( L, i);
1117 // copy upvalues 1043 // copy upvalues
1118 for( n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n) 1044 for (n = 0; (upname = lua_getupvalue( L, i, 1 + n)) != nullptr; ++ n)
1119 { 1045 {
1120 luaG_inter_move( U, L, L2, 1, mode_); // [up[,up ...]] 1046 luaG_inter_move( U, L, L2, 1, mode_); // [up[,up ...]]
1121 } 1047 }
@@ -1166,7 +1092,7 @@ static char const* vt_names[] =
1166[[nodiscard]] static int buf_writer(lua_State* L, void const* b, size_t size, void* ud) 1092[[nodiscard]] static int buf_writer(lua_State* L, void const* b, size_t size, void* ud)
1167{ 1093{
1168 luaL_Buffer* B = (luaL_Buffer*) ud; 1094 luaL_Buffer* B = (luaL_Buffer*) ud;
1169 if( !B->L) 1095 if (!B->L)
1170 { 1096 {
1171 luaL_buffinit( L, B); 1097 luaL_buffinit( L, B);
1172 } 1098 }
@@ -1278,7 +1204,7 @@ void InterCopyContext::copy_func() const
1278 { // ... _G up[n] 1204 { // ... _G up[n]
1279 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END, n, c.name)); 1205 DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "UPNAME[%d]: %s -> " INDENT_END, n, c.name));
1280#if LUA_VERSION_NUM >= 502 1206#if LUA_VERSION_NUM >= 502
1281 if( lua_rawequal(L1, -1, -2)) // is the upvalue equal to the global table? 1207 if (lua_rawequal(L1, -1, -2)) // is the upvalue equal to the global table?
1282 { 1208 {
1283 DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n")); 1209 DEBUGSPEW_CODE(fprintf(stderr, "pushing destination global scope\n"));
1284 lua_pushglobaltable(L2); // ... {cache} ... function <upvalues> 1210 lua_pushglobaltable(L2); // ... {cache} ... function <upvalues>
@@ -1331,8 +1257,7 @@ void InterCopyContext::copy_func() const
1331 */ 1257 */
1332void InterCopyContext::copy_cached_func() const 1258void InterCopyContext::copy_cached_func() const
1333{ 1259{
1334 FuncSubType funcSubType; 1260 FuncSubType const funcSubType{ luaG_getfuncsubtype(L1, L1_i) };
1335 std::ignore = luaG_tocfunction(L1, L1_i, &funcSubType); // nullptr for LuaJIT-fast && bytecode functions
1336 if (funcSubType == FST_Bytecode) 1261 if (funcSubType == FST_Bytecode)
1337 { 1262 {
1338 void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i)); 1263 void* const aspointer = const_cast<void*>(lua_topointer(L1, L1_i));
@@ -1376,7 +1301,7 @@ void InterCopyContext::copy_cached_func() const
1376 } 1301 }
1377 else // function is native/LuaJIT: no need to cache 1302 else // function is native/LuaJIT: no need to cache
1378 { 1303 {
1379 lookup_native_func(L2, L1, L1_i, mode, name); // ... {cache} ... function 1304 lookup_native_func(); // ... {cache} ... function
1380 // if the function was in fact a lookup sentinel, we can either get a function or a table here 1305 // if the function was in fact a lookup sentinel, we can either get a function or a table here
1381 _ASSERT_L(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1)); 1306 _ASSERT_L(L1, lua_isfunction(L2, -1) || lua_istable(L2, -1));
1382 } 1307 }
@@ -1440,7 +1365,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const
1440 SourceIndex const val_i{ lua_gettop(L1) }; 1365 SourceIndex const val_i{ lua_gettop(L1) };
1441 SourceIndex const key_i{ val_i - 1 }; 1366 SourceIndex const key_i{ val_i - 1 };
1442 1367
1443 // Only basic key types are copied over; others ignored 1368 // For the key, only basic key types are copied over. others ignored
1444 InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name }; 1369 InterCopyContext c{ U, L2, L1, L2_cache_i, key_i, VT::KEY, mode, name };
1445 if (!c.inter_copy_one()) 1370 if (!c.inter_copy_one())
1446 { 1371 {
@@ -1451,7 +1376,7 @@ void InterCopyContext::inter_copy_keyvaluepair() const
1451 } 1376 }
1452 1377
1453 char* valPath{ nullptr }; 1378 char* valPath{ nullptr };
1454 if( U->verboseErrors) 1379 if (U->verboseErrors)
1455 { 1380 {
1456 // for debug purposes, let's try to build a useful name 1381 // for debug purposes, let's try to build a useful name
1457 if (lua_type(L1, key_i) == LUA_TSTRING) 1382 if (lua_type(L1, key_i) == LUA_TSTRING)
@@ -1483,20 +1408,17 @@ void InterCopyContext::inter_copy_keyvaluepair() const
1483 valPath = (char*) alloca(strlen(name) + 16 + 5); 1408 valPath = (char*) alloca(strlen(name) + 16 + 5);
1484 sprintf(valPath, "%s[U:%p]", name, key); 1409 sprintf(valPath, "%s[U:%p]", name, key);
1485 } 1410 }
1486 else if( lua_type( L1, key_i) == LUA_TBOOLEAN) 1411 else if (lua_type( L1, key_i) == LUA_TBOOLEAN)
1487 { 1412 {
1488 int const key{ lua_toboolean(L1, key_i) }; 1413 int const key{ lua_toboolean(L1, key_i) };
1489 valPath = (char*) alloca(strlen(name) + 8); 1414 valPath = (char*) alloca(strlen(name) + 8);
1490 sprintf(valPath, "%s[%s]", name, key ? "true" : "false"); 1415 sprintf(valPath, "%s[%s]", name, key ? "true" : "false");
1491 } 1416 }
1492 } 1417 }
1493 /*
1494 * Contents of metatables are copied with cache checking;
1495 * important to detect loops.
1496 */
1497 c.L1_i = SourceIndex{ val_i }; 1418 c.L1_i = SourceIndex{ val_i };
1498 c.name = valPath ? valPath : name; 1419 // Contents of metatables are copied with cache checking. important to detect loops.
1499 c.vt = VT::NORMAL; 1420 c.vt = VT::NORMAL;
1421 c.name = valPath ? valPath : name;
1500 if (c.inter_copy_one()) 1422 if (c.inter_copy_one())
1501 { 1423 {
1502 _ASSERT_L(L1, lua_istable( L2, -3)); 1424 _ASSERT_L(L1, lua_istable( L2, -3));
@@ -1539,7 +1461,7 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1539 STACK_CHECK(L2, 0); 1461 STACK_CHECK(L2, 0);
1540 1462
1541 // no metatable? -> not clonable 1463 // no metatable? -> not clonable
1542 if( !lua_getmetatable(L1, L1_i)) // ... mt? 1464 if (!lua_getmetatable(L1, L1_i)) // ... mt?
1543 { 1465 {
1544 STACK_CHECK(L1, 0); 1466 STACK_CHECK(L1, 0);
1545 return false; 1467 return false;
@@ -1547,7 +1469,7 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1547 1469
1548 // no __lanesclone? -> not clonable 1470 // no __lanesclone? -> not clonable
1549 lua_getfield(L1, -1, "__lanesclone"); // ... mt __lanesclone? 1471 lua_getfield(L1, -1, "__lanesclone"); // ... mt __lanesclone?
1550 if( lua_isnil(L1, -1)) 1472 if (lua_isnil(L1, -1))
1551 { 1473 {
1552 lua_pop(L1, 2); // ... 1474 lua_pop(L1, 2); // ...
1553 STACK_CHECK(L1, 0); 1475 STACK_CHECK(L1, 0);
@@ -1565,12 +1487,12 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1565 lua_pop(L1, 1); // ... mt __lanesclone [uv]+ 1487 lua_pop(L1, 1); // ... mt __lanesclone [uv]+
1566 -- uvi; 1488 -- uvi;
1567 // create the clone userdata with the required number of uservalue slots 1489 // create the clone userdata with the required number of uservalue slots
1568 void* clone = lua_newuserdatauv(L2, userdata_size, uvi); // ... u 1490 void* const clone{ lua_newuserdatauv(L2, userdata_size, uvi) }; // ... u
1569 // copy the metatable in the target state, and give it to the clone we put there 1491 // copy the metatable in the target state, and give it to the clone we put there
1570 InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name }; 1492 InterCopyContext c{ U, L2, L1, L2_cache_i, SourceIndex{ mt }, VT::NORMAL, mode, name };
1571 if (c.inter_copy_one()) // ... u mt|sentinel 1493 if (c.inter_copy_one()) // ... u mt|sentinel
1572 { 1494 {
1573 if( LookupMode::ToKeeper == mode) // ... u sentinel 1495 if (LookupMode::ToKeeper == mode) // ... u sentinel
1574 { 1496 {
1575 _ASSERT_L(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel); 1497 _ASSERT_L(L1, lua_tocfunction(L2, -1) == table_lookup_sentinel);
1576 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn 1498 // we want to create a new closure with a 'clone sentinel' function, where the upvalues are the userdata and the metatable fqn
@@ -1645,7 +1567,7 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1645 } 1567 }
1646 1568
1647 // try clonable userdata first 1569 // try clonable userdata first
1648 if( copyclone()) 1570 if (copyclone())
1649 { 1571 {
1650 STACK_CHECK(L1, 0); 1572 STACK_CHECK(L1, 0);
1651 STACK_CHECK(L2, 1); 1573 STACK_CHECK(L2, 1);
@@ -1714,8 +1636,8 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1714 } 1636 }
1715 lua_pop(L2, 1); // ... 1637 lua_pop(L2, 1); // ...
1716 1638
1717 // this function has 2 upvalues: the fqn of its metatable, and the userdata itself 1639 // userdata_clone_sentinel has 2 upvalues: the fqn of its metatable, and the userdata itself
1718 bool const found{ lookup_table(L2, L1, L1_i, mode, name) }; // ... mt? 1640 bool const found{ lookup_table() }; // ... mt?
1719 if (!found) 1641 if (!found)
1720 { 1642 {
1721 STACK_CHECK(L2, 0); 1643 STACK_CHECK(L2, 0);
@@ -1802,7 +1724,7 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1802 * 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?) 1724 * 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?)
1803 * 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 1725 * 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
1804 */ 1726 */
1805 if (lookup_table(L2, L1, L1_i, mode, name)) 1727 if (lookup_table())
1806 { 1728 {
1807 _ASSERT_L(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 1729 _ASSERT_L(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
1808 return true; 1730 return true;
@@ -1849,6 +1771,71 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1849 1771
1850// ################################################################################################# 1772// #################################################################################################
1851 1773
1774[[nodiscard]] bool InterCopyContext::inter_copy_boolean() const
1775{
1776 int const v{ lua_toboolean(L1, L1_i) };
1777 DEBUGSPEW_CODE(fprintf(stderr, "%s\n", v ? "true" : "false"));
1778 lua_pushboolean(L2, v);
1779 return true;
1780}
1781
1782// #################################################################################################
1783
1784[[nodiscard]] bool InterCopyContext::inter_copy_lightuserdata() const
1785{
1786 void* const p{ lua_touserdata(L1, L1_i) };
1787 DEBUGSPEW_CODE(fprintf(stderr, "%p\n", p));
1788 lua_pushlightuserdata(L2, p);
1789 return true;
1790}
1791
1792// #################################################################################################
1793
1794[[nodiscard]] bool InterCopyContext::inter_copy_nil() const
1795{
1796 if (vt == VT::KEY)
1797 {
1798 return false;
1799 }
1800 lua_pushnil(L2);
1801 return true;
1802}
1803
1804// #################################################################################################
1805
1806[[nodiscard]] bool InterCopyContext::inter_copy_number() const
1807{
1808 /* LNUM patch support (keeping integer accuracy) */
1809#if defined LUA_LNUM || LUA_VERSION_NUM >= 503
1810 if (lua_isinteger(L1, L1_i))
1811 {
1812 lua_Integer const v{ lua_tointeger(L1, L1_i) };
1813 DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v));
1814 lua_pushinteger(L2, v);
1815 }
1816 else
1817#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
1818 {
1819 lua_Number const v{ lua_tonumber(L1, L1_i) };
1820 DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", v));
1821 lua_pushnumber(L2, v);
1822 }
1823 return true;
1824}
1825
1826// #################################################################################################
1827
1828[[nodiscard]] bool InterCopyContext::inter_copy_string() const
1829{
1830 size_t len;
1831 char const* const s{ lua_tolstring(L1, L1_i, &len) };
1832 DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", s));
1833 lua_pushlstring(L2, s, len);
1834 return true;
1835}
1836
1837// #################################################################################################
1838
1852/* 1839/*
1853* Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove 1840* Copies a value from 'L1' state (at index 'i') to 'L2' state. Does not remove
1854* the original value. 1841* the original value.
@@ -1878,7 +1865,7 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1878 if (lua_getmetatable(L1, L1_i)) // ... mt 1865 if (lua_getmetatable(L1, L1_i)) // ... mt
1879 { 1866 {
1880 lua_getfield(L1, -1, "__lanesignore"); // ... mt ignore? 1867 lua_getfield(L1, -1, "__lanesignore"); // ... mt ignore?
1881 if( lua_isboolean(L1, -1) && lua_toboolean(L1, -1)) 1868 if (lua_isboolean(L1, -1) && lua_toboolean(L1, -1))
1882 { 1869 {
1883 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END)); 1870 DEBUGSPEW_CODE(fprintf( stderr, INDENT_BEGIN "__lanesignore -> LUA_TNIL\n" INDENT_END));
1884 val_type = LuaType::NIL; 1871 val_type = LuaType::NIL;
@@ -1892,81 +1879,21 @@ static constexpr UniqueKey CLONABLES_CACHE_KEY{ 0xD04EE018B3DEE8F5ull };
1892 bool ret{ true }; 1879 bool ret{ true };
1893 switch (val_type) 1880 switch (val_type)
1894 { 1881 {
1895 /* Basic types allowed both as values, and as table keys */ 1882 // Basic types allowed both as values, and as table keys
1896 1883 case LuaType::BOOLEAN: ret = inter_copy_boolean(); break;
1897 case LuaType::BOOLEAN: 1884 case LuaType::NUMBER: ret = inter_copy_number(); break;
1898 { 1885 case LuaType::STRING: ret = inter_copy_string(); break;
1899 int const v{ lua_toboolean(L1, L1_i) }; 1886 case LuaType::LIGHTUSERDATA: ret = inter_copy_lightuserdata();break;
1900 DEBUGSPEW_CODE( fprintf(stderr, "%s\n", v ? "true" : "false")); 1887
1901 lua_pushboolean(L2, v); 1888 // The following types are not allowed as table keys
1902 } 1889 case LuaType::USERDATA: ret = inter_copy_userdata(); break;
1903 break; 1890 case LuaType::NIL: ret = inter_copy_nil(); break;
1904 1891 case LuaType::FUNCTION: ret = inter_copy_function(); break;
1905 case LuaType::NUMBER: 1892 case LuaType::TABLE: ret = inter_copy_table(); break;
1906 /* LNUM patch support (keeping integer accuracy) */ 1893
1907#if defined LUA_LNUM || LUA_VERSION_NUM >= 503 1894 // The following types cannot be copied
1908 if( lua_isinteger(L1, L1_i)) 1895 case LuaType::CDATA: [[fallthrough]];
1909 { 1896 case LuaType::THREAD: ret = false; break;
1910 lua_Integer const v{ lua_tointeger(L1, L1_i) };
1911 DEBUGSPEW_CODE(fprintf(stderr, LUA_INTEGER_FMT "\n", v));
1912 lua_pushinteger(L2, v);
1913 break;
1914 }
1915 else
1916#endif // defined LUA_LNUM || LUA_VERSION_NUM >= 503
1917 {
1918 lua_Number const v{ lua_tonumber(L1, L1_i) };
1919 DEBUGSPEW_CODE(fprintf(stderr, LUA_NUMBER_FMT "\n", v));
1920 lua_pushnumber(L2, v);
1921 }
1922 break;
1923
1924 case LuaType::STRING:
1925 {
1926 size_t len;
1927 char const* const s{ lua_tolstring(L1, L1_i, &len) };
1928 DEBUGSPEW_CODE(fprintf(stderr, "'%s'\n", s));
1929 lua_pushlstring(L2, s, len);
1930 }
1931 break;
1932
1933 case LuaType::LIGHTUSERDATA:
1934 {
1935 void* const p{ lua_touserdata(L1, L1_i) };
1936 DEBUGSPEW_CODE(fprintf(stderr, "%p\n", p));
1937 lua_pushlightuserdata(L2, p);
1938 }
1939 break;
1940
1941 /* The following types are not allowed as table keys */
1942
1943 case LuaType::USERDATA:
1944 ret = inter_copy_userdata();
1945 break;
1946
1947 case LuaType::NIL:
1948 if (vt == VT::KEY)
1949 {
1950 ret = false;
1951 break;
1952 }
1953 lua_pushnil( L2);
1954 break;
1955
1956 case LuaType::FUNCTION:
1957 ret = inter_copy_function();
1958 break;
1959
1960 case LuaType::TABLE:
1961 ret = inter_copy_table();
1962 break;
1963
1964 /* The following types cannot be copied */
1965
1966 case LuaType::CDATA:
1967 case LuaType::THREAD:
1968 ret = false;
1969 break;
1970 } 1897 }
1971 1898
1972 STACK_CHECK(L2, ret ? 1 : 0); 1899 STACK_CHECK(L2, ret ? 1 : 0);
diff --git a/src/tools.h b/src/tools.h
index 06646d3..ddf5c67 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -6,13 +6,7 @@
6// forwards 6// forwards
7class Universe; 7class Universe;
8 8
9// ################################################################################################ 9// #################################################################################################
10
11#ifdef _DEBUG
12void luaG_dump(lua_State* L);
13#endif // _DEBUG
14
15// ################################################################################################
16 10
17void push_registry_subtable_mode(lua_State* L, UniqueKey key_, const char* mode_); 11void push_registry_subtable_mode(lua_State* L, UniqueKey key_, const char* mode_);
18void push_registry_subtable(lua_State* L, UniqueKey key_); 12void push_registry_subtable(lua_State* L, UniqueKey key_);
@@ -31,12 +25,13 @@ enum class InterCopyResult
31 Error 25 Error
32}; 26};
33 27
34// ################################################################################################ 28// #################################################################################################
35 29
36using CacheIndex = Unique<int>; 30using CacheIndex = Unique<int>;
37using SourceIndex = Unique<int>; 31using SourceIndex = Unique<int>;
38struct InterCopyContext 32class InterCopyContext
39{ 33{
34 public:
40 35
41 Universe* const U; 36 Universe* const U;
42 Dest const L2; 37 Dest const L2;
@@ -47,35 +42,50 @@ struct InterCopyContext
47 LookupMode const mode; 42 LookupMode const mode;
48 char const* name; // that one can change when we reuse the context 43 char const* name; // that one can change when we reuse the context
49 44
50 [[nodiscard]] bool inter_copy_one() const;
51
52 private: 45 private:
53 46
54 [[nodiscard]] bool inter_copy_userdata() const; 47 // for use in copy_cached_func
55 [[nodiscard]] bool inter_copy_function() const;
56 [[nodiscard]] bool inter_copy_table() const;
57 [[nodiscard]] bool copyclone() const;
58 [[nodiscard]] bool copydeep() const;
59 [[nodiscard]] bool push_cached_metatable() const;
60 void copy_func() const; 48 void copy_func() const;
49 void lookup_native_func() const;
50
51 // for use in inter_copy_function
61 void copy_cached_func() const; 52 void copy_cached_func() const;
53 [[nodiscard]] bool lookup_table() const;
54
55 // for use in inter_copy_table
62 void inter_copy_keyvaluepair() const; 56 void inter_copy_keyvaluepair() const;
57 [[nodiscard]] bool push_cached_metatable() const;
58
59 // for use in inter_copy_userdata
60 [[nodiscard]] bool copyclone() const;
61 [[nodiscard]] bool copydeep() const;
62
63 // copying a single Lua stack item
64 [[nodiscard]] bool inter_copy_boolean() const;
65 [[nodiscard]] bool inter_copy_function() const;
66 [[nodiscard]] bool inter_copy_lightuserdata() const;
67 [[nodiscard]] bool inter_copy_nil() const;
68 [[nodiscard]] bool inter_copy_number() const;
69 [[nodiscard]] bool inter_copy_string() const;
70 [[nodiscard]] bool inter_copy_table() const;
71 [[nodiscard]] bool inter_copy_userdata() const;
63 72
64 public: 73 public:
65 74
75 [[nodiscard]] bool inter_copy_one() const;
66 [[nodiscard]] InterCopyResult inter_copy_package() const; 76 [[nodiscard]] InterCopyResult inter_copy_package() const;
67 [[nodiscard]] InterCopyResult inter_copy(int n_) const; 77 [[nodiscard]] InterCopyResult inter_copy(int n_) const;
68 [[nodiscard]] InterCopyResult inter_move(int n_) const; 78 [[nodiscard]] InterCopyResult inter_move(int n_) const;
69}; 79};
70 80
71// ################################################################################################ 81// #################################################################################################
72 82
73[[nodiscard]] int luaG_nameof(lua_State* L); 83[[nodiscard]] int luaG_nameof(lua_State* L);
74 84
75void populate_func_lookup_table(lua_State* L, int _i, char const* _name); 85void populate_func_lookup_table(lua_State* L, int _i, char const* _name);
76void initialize_allocator_function(Universe* U, lua_State* L); 86void initialize_allocator_function(Universe* U, lua_State* L);
77 87
78// ################################################################################################ 88// #################################################################################################
79 89
80// crc64/we of string "CONFIG_REGKEY" generated at http://www.nitrxgen.net/hashgen/ 90// crc64/we of string "CONFIG_REGKEY" generated at http://www.nitrxgen.net/hashgen/
81static constexpr UniqueKey CONFIG_REGKEY{ 0x31cd24894eae8624ull }; // registry key to access the configuration 91static constexpr UniqueKey CONFIG_REGKEY{ 0x31cd24894eae8624ull }; // registry key to access the configuration
diff --git a/src/uniquekey.h b/src/uniquekey.h
index 78c0765..84553a5 100644
--- a/src/uniquekey.h
+++ b/src/uniquekey.h
@@ -44,8 +44,10 @@ class UniqueKey
44 } 44 }
45 void pushValue(lua_State* const L) const 45 void pushValue(lua_State* const L) const
46 { 46 {
47 STACK_CHECK_START_REL(L, 0);
47 pushKey(L); 48 pushKey(L);
48 lua_rawget(L, LUA_REGISTRYINDEX); 49 lua_rawget(L, LUA_REGISTRYINDEX);
50 STACK_CHECK(L, 1);
49 } 51 }
50 template <typename OP> 52 template <typename OP>
51 void setValue(lua_State* L, OP operation_) const 53 void setValue(lua_State* L, OP operation_) const
diff --git a/src/universe.cpp b/src/universe.cpp
index 4c53987..a02a5e6 100644
--- a/src/universe.cpp
+++ b/src/universe.cpp
@@ -41,7 +41,7 @@ static constexpr UniqueKey UNIVERSE_FULL_REGKEY{ 0x99CA130C09EDC074ull };
41// xxh64 of string "UNIVERSE_LIGHT_REGKEY" generated at http://www.nitrxgen.net/hashgen/ 41// xxh64 of string "UNIVERSE_LIGHT_REGKEY" generated at http://www.nitrxgen.net/hashgen/
42static constexpr UniqueKey UNIVERSE_LIGHT_REGKEY{ 0x3663C07C742CEB81ull }; 42static constexpr UniqueKey UNIVERSE_LIGHT_REGKEY{ 0x3663C07C742CEB81ull };
43 43
44// ################################################################################################ 44// #################################################################################################
45 45
46Universe::Universe() 46Universe::Universe()
47{ 47{
@@ -70,13 +70,13 @@ Universe::Universe()
70#endif // PLATFORM_LINUX 70#endif // PLATFORM_LINUX
71} 71}
72 72
73// ################################################################################################ 73// #################################################################################################
74 74
75// only called from the master state 75// only called from the master state
76Universe* universe_create(lua_State* L) 76Universe* universe_create(lua_State* L)
77{ 77{
78 ASSERT_L(universe_get(L) == nullptr); 78 ASSERT_L(universe_get(L) == nullptr);
79 Universe* const U = static_cast<Universe*>(lua_newuserdatauv(L, sizeof(Universe), 0)); // universe 79 Universe* const U{ lua_newuserdatauv<Universe>(L, 0) }; // universe
80 U->Universe::Universe(); 80 U->Universe::Universe();
81 STACK_CHECK_START_REL(L, 1); 81 STACK_CHECK_START_REL(L, 1);
82 UNIVERSE_FULL_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); }); 82 UNIVERSE_FULL_REGKEY.setValue(L, [](lua_State* L) { lua_pushvalue(L, -2); });
@@ -85,7 +85,7 @@ Universe* universe_create(lua_State* L)
85 return U; 85 return U;
86} 86}
87 87
88// ################################################################################################ 88// #################################################################################################
89 89
90void universe_store(lua_State* L, Universe* U) 90void universe_store(lua_State* L, Universe* U)
91{ 91{
@@ -95,7 +95,7 @@ void universe_store(lua_State* L, Universe* U)
95 STACK_CHECK(L, 0); 95 STACK_CHECK(L, 0);
96} 96}
97 97
98// ################################################################################################ 98// #################################################################################################
99 99
100Universe* universe_get(lua_State* L) 100Universe* universe_get(lua_State* L)
101{ 101{
diff --git a/src/universe.h b/src/universe.h
index eb85133..320c400 100644
--- a/src/universe.h
+++ b/src/universe.h
@@ -13,7 +13,7 @@ extern "C" {
13 13
14#include <mutex> 14#include <mutex>
15 15
16// ################################################################################################ 16// #################################################################################################
17 17
18// forwards 18// forwards
19struct DeepPrelude; 19struct DeepPrelude;
@@ -25,7 +25,7 @@ class Lane;
25*/ 25*/
26#define HAVE_LANE_TRACKING() 1 26#define HAVE_LANE_TRACKING() 1
27 27
28// ################################################################################################ 28// #################################################################################################
29 29
30// everything we need to provide to lua_newstate() 30// everything we need to provide to lua_newstate()
31class AllocatorDefinition 31class AllocatorDefinition
@@ -35,6 +35,7 @@ class AllocatorDefinition
35 lua_Alloc m_allocF{ nullptr }; 35 lua_Alloc m_allocF{ nullptr };
36 void* m_allocUD{ nullptr }; 36 void* m_allocUD{ nullptr };
37 37
38 [[nodiscard]] static void* operator new(size_t size_) noexcept = delete; // can't create one outside of a Lua state
38 [[nodiscard]] static void* operator new(size_t size_, lua_State* L) noexcept { return lua_newuserdatauv(L, size_, 0); } 39 [[nodiscard]] static void* operator new(size_t size_, lua_State* L) noexcept { return lua_newuserdatauv(L, size_, 0); }
39 // always embedded somewhere else or "in-place constructed" as a full userdata 40 // always embedded somewhere else or "in-place constructed" as a full userdata
40 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception 41 // can't actually delete the operator because the compiler generates stack unwinding code that could call it in case of exception
@@ -72,7 +73,7 @@ class AllocatorDefinition
72 } 73 }
73}; 74};
74 75
75// ################################################################################################ 76// #################################################################################################
76 77
77// mutex-protected allocator for use with Lua states that share a non-threadsafe allocator 78// mutex-protected allocator for use with Lua states that share a non-threadsafe allocator
78class ProtectedAllocator : public AllocatorDefinition 79class ProtectedAllocator : public AllocatorDefinition
@@ -115,7 +116,7 @@ class ProtectedAllocator : public AllocatorDefinition
115 } 116 }
116}; 117};
117 118
118// ################################################################################################ 119// #################################################################################################
119 120
120// everything regarding the Lanes universe is stored in that global structure 121// everything regarding the Lanes universe is stored in that global structure
121// held as a full userdata in the master Lua state that required it for the first time 122// held as a full userdata in the master Lua state that required it for the first time
@@ -183,13 +184,13 @@ class Universe
183 Universe& operator=(Universe&&) = delete; 184 Universe& operator=(Universe&&) = delete;
184}; 185};
185 186
186// ################################################################################################ 187// #################################################################################################
187 188
188[[nodiscard]] Universe* universe_get(lua_State* L); 189[[nodiscard]] Universe* universe_get(lua_State* L);
189[[nodiscard]] Universe* universe_create(lua_State* L); 190[[nodiscard]] Universe* universe_create(lua_State* L);
190void universe_store(lua_State* L, Universe* U); 191void universe_store(lua_State* L, Universe* U);
191 192
192// ################################################################################################ 193// #################################################################################################
193 194
194#if USE_DEBUG_SPEW() 195#if USE_DEBUG_SPEW()
195class DebugSpewIndentScope 196class DebugSpewIndentScope