diff options
author | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-29 14:27:17 +0200 |
---|---|---|
committer | Benoit Germain <benoit.germain@ubisoft.com> | 2024-05-29 14:27:17 +0200 |
commit | 4007dbf5a2262a7c4f2f26089071942dde7c3a91 (patch) | |
tree | a53df94b2d640d9b374241fdec8905864a012c01 /src | |
parent | 5890678289e28cc9e666c1dda8265712bd27ac03 (diff) | |
download | lanes-4007dbf5a2262a7c4f2f26089071942dde7c3a91.tar.gz lanes-4007dbf5a2262a7c4f2f26089071942dde7c3a91.tar.bz2 lanes-4007dbf5a2262a7c4f2f26089071942dde7c3a91.zip |
Moved implementation of lanes.nameof in a separate file
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/intercopycontext.cpp | 5 | ||||
-rw-r--r-- | src/lane.cpp | 4 | ||||
-rw-r--r-- | src/lanes.cpp | 13 | ||||
-rw-r--r-- | src/nameof.cpp | 208 | ||||
-rw-r--r-- | src/nameof.h | 5 | ||||
-rw-r--r-- | src/state.cpp | 4 | ||||
-rw-r--r-- | src/tools.cpp | 316 | ||||
-rw-r--r-- | src/tools.h | 18 |
9 files changed, 309 insertions, 266 deletions
diff --git a/src/Makefile b/src/Makefile index a01f7c3..0362b8b 100644 --- a/src/Makefile +++ b/src/Makefile | |||
@@ -7,7 +7,7 @@ | |||
7 | 7 | ||
8 | MODULE=lanes | 8 | MODULE=lanes |
9 | 9 | ||
10 | SRC=cancel.cpp compat.cpp deep.cpp intercopycontext.cpp keeper.cpp lane.cpp lanes.cpp linda.cpp lindafactory.cpp state.cpp threading.cpp tools.cpp tracker.cpp universe.cpp | 10 | SRC=cancel.cpp compat.cpp deep.cpp intercopycontext.cpp keeper.cpp lane.cpp lanes.cpp linda.cpp lindafactory.cpp nameof.cpp state.cpp threading.cpp tools.cpp tracker.cpp universe.cpp |
11 | 11 | ||
12 | OBJ=$(SRC:.cpp=.o) | 12 | OBJ=$(SRC:.cpp=.o) |
13 | 13 | ||
diff --git a/src/intercopycontext.cpp b/src/intercopycontext.cpp index 8058838..044b197 100644 --- a/src/intercopycontext.cpp +++ b/src/intercopycontext.cpp | |||
@@ -31,6 +31,7 @@ THE SOFTWARE. | |||
31 | #include "keeper.h" | 31 | #include "keeper.h" |
32 | #include "lane.h" | 32 | #include "lane.h" |
33 | #include "linda.h" | 33 | #include "linda.h" |
34 | #include "nameof.h" | ||
34 | #include "universe.h" | 35 | #include "universe.h" |
35 | 36 | ||
36 | // ################################################################################################# | 37 | // ################################################################################################# |
@@ -109,8 +110,8 @@ THE SOFTWARE. | |||
109 | // try to discover the name of the function we want to send | 110 | // try to discover the name of the function we want to send |
110 | kLaneNameRegKey.pushValue(L1); // L1: ... v ... lane_name | 111 | kLaneNameRegKey.pushValue(L1); // L1: ... v ... lane_name |
111 | char const* _from{ lua_tostring(L1, -1) }; | 112 | char const* _from{ lua_tostring(L1, -1) }; |
112 | lua_pushcfunction(L1, luaG_nameof); // L1: ... v ... lane_name luaG_nameof | 113 | lua_pushcfunction(L1, LG_nameof); // L1: ... v ... lane_name LG_nameof |
113 | lua_pushvalue(L1, L1_i); // L1: ... v ... lane_name luaG_nameof t | 114 | lua_pushvalue(L1, L1_i); // L1: ... v ... lane_name LG_nameof t |
114 | lua_call(L1, 1, 2); // L1: ... v ... lane_name "type" "name"|nil | 115 | lua_call(L1, 1, 2); // L1: ... v ... lane_name "type" "name"|nil |
115 | char const* _typewhat{ (lua_type(L1, -2) == LUA_TSTRING) ? lua_tostring(L1, -2) : luaL_typename(L1, -2) }; | 116 | char const* _typewhat{ (lua_type(L1, -2) == LUA_TSTRING) ? lua_tostring(L1, -2) : luaL_typename(L1, -2) }; |
116 | // second return value can be nil if the table was not found | 117 | // second return value can be nil if the table was not found |
diff --git a/src/lane.cpp b/src/lane.cpp index f4d4bd1..79ea028 100644 --- a/src/lane.cpp +++ b/src/lane.cpp | |||
@@ -642,7 +642,7 @@ static void PrepareLaneHelpers(Lane* lane_) | |||
642 | lua_State* const _L{ lane_->L }; | 642 | lua_State* const _L{ lane_->L }; |
643 | // Tie "set_finalizer()" to the state | 643 | // Tie "set_finalizer()" to the state |
644 | lua_pushcfunction(_L, LG_set_finalizer); | 644 | lua_pushcfunction(_L, LG_set_finalizer); |
645 | populate_func_lookup_table(_L, -1, "set_finalizer"); | 645 | tools::PopulateFuncLookupTable(_L, -1, "set_finalizer"); |
646 | lua_setglobal(_L, "set_finalizer"); | 646 | lua_setglobal(_L, "set_finalizer"); |
647 | 647 | ||
648 | // Tie "set_debug_threadname()" to the state | 648 | // Tie "set_debug_threadname()" to the state |
@@ -653,7 +653,7 @@ static void PrepareLaneHelpers(Lane* lane_) | |||
653 | 653 | ||
654 | // Tie "cancel_test()" to the state | 654 | // Tie "cancel_test()" to the state |
655 | lua_pushcfunction(_L, LG_cancel_test); | 655 | lua_pushcfunction(_L, LG_cancel_test); |
656 | populate_func_lookup_table(_L, -1, "cancel_test"); | 656 | tools::PopulateFuncLookupTable(_L, -1, "cancel_test"); |
657 | lua_setglobal(_L, "cancel_test"); | 657 | lua_setglobal(_L, "cancel_test"); |
658 | } | 658 | } |
659 | 659 | ||
diff --git a/src/lanes.cpp b/src/lanes.cpp index a82e4ba..04b0955 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp | |||
@@ -85,6 +85,7 @@ THE SOFTWARE. | |||
85 | #include "intercopycontext.h" | 85 | #include "intercopycontext.h" |
86 | #include "keeper.h" | 86 | #include "keeper.h" |
87 | #include "lane.h" | 87 | #include "lane.h" |
88 | #include "nameof.h" | ||
88 | #include "state.h" | 89 | #include "state.h" |
89 | #include "threading.h" | 90 | #include "threading.h" |
90 | #include "tools.h" | 91 | #include "tools.h" |
@@ -175,7 +176,7 @@ LUAG_FUNC(require) | |||
175 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: "name" ... require | 176 | lua_pushvalue(L_, lua_upvalueindex(1)); // L_: "name" ... require |
176 | lua_insert(L_, 1); // L_: require "name" ... | 177 | lua_insert(L_, 1); // L_: require "name" ... |
177 | lua_call(L_, _nargs, 1); // L_: module | 178 | lua_call(L_, _nargs, 1); // L_: module |
178 | populate_func_lookup_table(L_, -1, _name); | 179 | tools::PopulateFuncLookupTable(L_, -1, _name); |
179 | DEBUGSPEW_CODE(DebugSpew(_U) << "lanes.require '" << _name << "' END" << std::endl); | 180 | DEBUGSPEW_CODE(DebugSpew(_U) << "lanes.require '" << _name << "' END" << std::endl); |
180 | STACK_CHECK(L_, 0); | 181 | STACK_CHECK(L_, 0); |
181 | return 1; | 182 | return 1; |
@@ -197,7 +198,7 @@ LUAG_FUNC(register) | |||
197 | STACK_CHECK_START_REL(L_, 0); // "name" mod_table | 198 | STACK_CHECK_START_REL(L_, 0); // "name" mod_table |
198 | DEBUGSPEW_CODE(DebugSpew(_U) << "lanes.register '" << _name << "' BEGIN" << std::endl); | 199 | DEBUGSPEW_CODE(DebugSpew(_U) << "lanes.register '" << _name << "' BEGIN" << std::endl); |
199 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); | 200 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); |
200 | populate_func_lookup_table(L_, -1, _name); | 201 | tools::PopulateFuncLookupTable(L_, -1, _name); |
201 | DEBUGSPEW_CODE(DebugSpew(_U) << "lanes.register '" << _name << "' END" << std::endl); | 202 | DEBUGSPEW_CODE(DebugSpew(_U) << "lanes.register '" << _name << "' END" << std::endl); |
202 | STACK_CHECK(L_, 0); | 203 | STACK_CHECK(L_, 0); |
203 | return 0; | 204 | return 0; |
@@ -414,7 +415,7 @@ LUAG_FUNC(lane_new) | |||
414 | } | 415 | } |
415 | // here the module was successfully required // L_: [fixed] args... n "modname" L2: ret | 416 | // here the module was successfully required // L_: [fixed] args... n "modname" L2: ret |
416 | // after requiring the module, register the functions it exported in our name<->function database | 417 | // after requiring the module, register the functions it exported in our name<->function database |
417 | populate_func_lookup_table(_L2, -1, _name); | 418 | tools::PopulateFuncLookupTable(_L2, -1, _name); |
418 | lua_pop(_L2, 1); // L_: [fixed] args... n "modname" L2: | 419 | lua_pop(_L2, 1); // L_: [fixed] args... n "modname" L2: |
419 | } | 420 | } |
420 | } | 421 | } |
@@ -596,7 +597,7 @@ namespace { | |||
596 | { "wakeup_conv", LG_wakeup_conv }, | 597 | { "wakeup_conv", LG_wakeup_conv }, |
597 | { "set_thread_priority", LG_set_thread_priority }, | 598 | { "set_thread_priority", LG_set_thread_priority }, |
598 | { "set_thread_affinity", LG_set_thread_affinity }, | 599 | { "set_thread_affinity", LG_set_thread_affinity }, |
599 | { "nameof", luaG_nameof }, | 600 | { "nameof", LG_nameof }, |
600 | { "register", LG_register }, | 601 | { "register", LG_register }, |
601 | { Universe::kFinally, Universe::InitializeFinalizer }, | 602 | { Universe::kFinally, Universe::InitializeFinalizer }, |
602 | { "set_singlethreaded", LG_set_singlethreaded }, | 603 | { "set_singlethreaded", LG_set_singlethreaded }, |
@@ -742,7 +743,7 @@ LUAG_FUNC(configure) | |||
742 | // register all native functions found in that module in the transferable functions database | 743 | // register all native functions found in that module in the transferable functions database |
743 | // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) | 744 | // we process it before _G because we don't want to find the module when scanning _G (this would generate longer names) |
744 | // for example in package.loaded["lanes.core"].* | 745 | // for example in package.loaded["lanes.core"].* |
745 | populate_func_lookup_table(L_, -1, _name); | 746 | tools::PopulateFuncLookupTable(L_, -1, _name); |
746 | STACK_CHECK(L_, 2); | 747 | STACK_CHECK(L_, 2); |
747 | 748 | ||
748 | // record all existing C/JIT-fast functions | 749 | // record all existing C/JIT-fast functions |
@@ -752,7 +753,7 @@ LUAG_FUNC(configure) | |||
752 | // because we will do it after on_state_create() is called, | 753 | // because we will do it after on_state_create() is called, |
753 | // and we don't want to skip _G because of caching in case globals are created then | 754 | // and we don't want to skip _G because of caching in case globals are created then |
754 | lua_pushglobaltable(L_); // L_: settings M _G | 755 | lua_pushglobaltable(L_); // L_: settings M _G |
755 | populate_func_lookup_table(L_, -1, {}); | 756 | tools::PopulateFuncLookupTable(L_, -1, {}); |
756 | lua_pop(L_, 1); // L_: settings M | 757 | lua_pop(L_, 1); // L_: settings M |
757 | } | 758 | } |
758 | lua_pop(L_, 1); // L_: settings | 759 | lua_pop(L_, 1); // L_: settings |
diff --git a/src/nameof.cpp b/src/nameof.cpp new file mode 100644 index 0000000..7614577 --- /dev/null +++ b/src/nameof.cpp | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | =============================================================================== | ||
3 | |||
4 | Copyright (C) 2024 benoit Germain <bnt.germain@gmail.com> | ||
5 | |||
6 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
7 | of this software and associated documentation files (the "Software"), to deal | ||
8 | in the Software without restriction, including without limitation the rights | ||
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
10 | copies of the Software, and to permit persons to whom the Software is | ||
11 | furnished to do so, subject to the following conditions: | ||
12 | |||
13 | The above copyright notice and this permission notice shall be included in | ||
14 | all copies or substantial portions of the Software. | ||
15 | |||
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
22 | THE SOFTWARE. | ||
23 | |||
24 | =============================================================================== | ||
25 | */ | ||
26 | |||
27 | #include "nameof.h" | ||
28 | |||
29 | #include "tools.h" | ||
30 | |||
31 | // ################################################################################################# | ||
32 | |||
33 | // Return some name helping to identify an object | ||
34 | [[nodiscard]] static int DiscoverObjectNameRecur(lua_State* L_, int shortest_, int depth_) | ||
35 | { | ||
36 | static constexpr int kWhat{ 1 }; // the object to investigate // L_: o "r" {c} {fqn} ... {?} | ||
37 | static constexpr int kResult{ 2 }; // where the result string is stored | ||
38 | static constexpr int kCache{ 3 }; // a cache | ||
39 | static constexpr int kFQN{ 4 }; // the name compositing stack | ||
40 | // no need to scan this table if the name we will discover is longer than one we already know | ||
41 | if (shortest_ <= depth_ + 1) { | ||
42 | return shortest_; | ||
43 | } | ||
44 | STACK_GROW(L_, 3); | ||
45 | STACK_CHECK_START_REL(L_, 0); | ||
46 | // stack top contains the table to search in | ||
47 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} | ||
48 | lua_rawget(L_, kCache); // L_: o "r" {c} {fqn} ... {?} nil/1 | ||
49 | // if table is already visited, we are done | ||
50 | if (!lua_isnil(L_, -1)) { | ||
51 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} | ||
52 | return shortest_; | ||
53 | } | ||
54 | // examined table is not in the cache, add it now | ||
55 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} | ||
56 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} | ||
57 | lua_pushinteger(L_, 1); // L_: o "r" {c} {fqn} ... {?} {?} 1 | ||
58 | lua_rawset(L_, kCache); // L_: o "r" {c} {fqn} ... {?} | ||
59 | // scan table contents | ||
60 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} nil | ||
61 | while (lua_next(L_, -2)) { // L_: o "r" {c} {fqn} ... {?} k v | ||
62 | // std::string_view const _strKey{ (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostringview(L_, -2) : "" }; // only for debugging | ||
63 | // lua_Number const numKey = (lua_type(L_, -2) == LUA_TNUMBER) ? lua_tonumber(L_, -2) : -6666; // only for debugging | ||
64 | STACK_CHECK(L_, 2); | ||
65 | // append key name to fqn stack | ||
66 | ++depth_; | ||
67 | lua_pushvalue(L_, -2); // L_: o "r" {c} {fqn} ... {?} k v k | ||
68 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k v | ||
69 | if (lua_rawequal(L_, -1, kWhat)) { // is it what we are looking for? | ||
70 | STACK_CHECK(L_, 2); | ||
71 | // update shortest name | ||
72 | if (depth_ < shortest_) { | ||
73 | shortest_ = depth_; | ||
74 | std::ignore = tools::PushFQN(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k v "fqn" | ||
75 | lua_replace(L_, kResult); // L_: o "r" {c} {fqn} ... {?} k v | ||
76 | } | ||
77 | // no need to search further at this level | ||
78 | lua_pop(L_, 2); // L_: o "r" {c} {fqn} ... {?} | ||
79 | STACK_CHECK(L_, 0); | ||
80 | break; | ||
81 | } | ||
82 | switch (lua_type(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k v | ||
83 | default: // nil, boolean, light userdata, number and string aren't identifiable | ||
84 | break; | ||
85 | |||
86 | case LUA_TTABLE: // L_: o "r" {c} {fqn} ... {?} k {} | ||
87 | STACK_CHECK(L_, 2); | ||
88 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, depth_); | ||
89 | // search in the table's metatable too | ||
90 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k {} {mt} | ||
91 | if (lua_istable(L_, -1)) { | ||
92 | ++depth_; | ||
93 | lua_pushliteral(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" | ||
94 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} | ||
95 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, depth_); | ||
96 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} nil | ||
97 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} | ||
98 | --depth_; | ||
99 | } | ||
100 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k {} | ||
101 | } | ||
102 | STACK_CHECK(L_, 2); | ||
103 | break; | ||
104 | |||
105 | case LUA_TTHREAD: // L_: o "r" {c} {fqn} ... {?} k T | ||
106 | // TODO: explore the thread's stack frame looking for our culprit? | ||
107 | break; | ||
108 | |||
109 | case LUA_TUSERDATA: // L_: o "r" {c} {fqn} ... {?} k U | ||
110 | STACK_CHECK(L_, 2); | ||
111 | // search in the object's metatable (some modules are built that way) | ||
112 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k U {mt} | ||
113 | if (lua_istable(L_, -1)) { | ||
114 | ++depth_; | ||
115 | lua_pushliteral(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} k U {mt} "__metatable" | ||
116 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k U {mt} | ||
117 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, depth_); | ||
118 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k U {mt} nil | ||
119 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k U {mt} | ||
120 | --depth_; | ||
121 | } | ||
122 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U | ||
123 | } | ||
124 | STACK_CHECK(L_, 2); | ||
125 | // search in the object's uservalues | ||
126 | { | ||
127 | int _uvi{ 1 }; | ||
128 | while (lua_getiuservalue(L_, -1, _uvi) != LUA_TNONE) { // L_: o "r" {c} {fqn} ... {?} k U {u} | ||
129 | if (lua_istable(L_, -1)) { // if it is a table, look inside | ||
130 | ++depth_; | ||
131 | lua_pushliteral(L_, "uservalue"); // L_: o "r" {c} {fqn} ... {?} k v {u} "uservalue" | ||
132 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k v {u} | ||
133 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, depth_); | ||
134 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k v {u} nil | ||
135 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k v {u} | ||
136 | --depth_; | ||
137 | } | ||
138 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U | ||
139 | ++_uvi; | ||
140 | } | ||
141 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | ||
142 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U | ||
143 | } | ||
144 | STACK_CHECK(L_, 2); | ||
145 | break; | ||
146 | } | ||
147 | // make ready for next iteration | ||
148 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k | ||
149 | // remove name from fqn stack | ||
150 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k nil | ||
151 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k | ||
152 | STACK_CHECK(L_, 1); | ||
153 | --depth_; | ||
154 | } // L_: o "r" {c} {fqn} ... {?} | ||
155 | STACK_CHECK(L_, 0); | ||
156 | // remove the visited table from the cache, in case a shorter path to the searched object exists | ||
157 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} | ||
158 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} {?} nil | ||
159 | lua_rawset(L_, kCache); // L_: o "r" {c} {fqn} ... {?} | ||
160 | STACK_CHECK(L_, 0); | ||
161 | return shortest_; | ||
162 | } | ||
163 | |||
164 | // ################################################################################################# | ||
165 | |||
166 | // "type", "name" = lanes.nameof(o) | ||
167 | LUAG_FUNC(nameof) | ||
168 | { | ||
169 | int const _what{ lua_gettop(L_) }; | ||
170 | if (_what > 1) { | ||
171 | raise_luaL_argerror(L_, _what, "too many arguments."); | ||
172 | } | ||
173 | |||
174 | // nil, boolean, light userdata, number and string aren't identifiable | ||
175 | if (lua_type(L_, 1) < LUA_TTABLE) { | ||
176 | lua_pushstring(L_, luaL_typename(L_, 1)); // L_: o "type" | ||
177 | lua_insert(L_, -2); // L_: "type" o | ||
178 | return 2; | ||
179 | } | ||
180 | |||
181 | STACK_GROW(L_, 4); | ||
182 | STACK_CHECK_START_REL(L_, 0); | ||
183 | // this slot will contain the shortest name we found when we are done | ||
184 | lua_pushnil(L_); // L_: o nil | ||
185 | // push a cache that will contain all already visited tables | ||
186 | lua_newtable(L_); // L_: o nil {c} | ||
187 | // push a table whose contents are strings that, when concatenated, produce unique name | ||
188 | lua_newtable(L_); // L_: o nil {c} {fqn} | ||
189 | // {fqn}[1] = "_G" | ||
190 | lua_pushliteral(L_, LUA_GNAME); // L_: o nil {c} {fqn} "_G" | ||
191 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} | ||
192 | // this is where we start the search | ||
193 | lua_pushglobaltable(L_); // L_: o nil {c} {fqn} _G | ||
194 | std::ignore = DiscoverObjectNameRecur(L_, std::numeric_limits<int>::max(), 1); | ||
195 | if (lua_isnil(L_, 2)) { // try again with registry, just in case... | ||
196 | lua_pop(L_, 1); // L_: o nil {c} {fqn} | ||
197 | lua_pushliteral(L_, "_R"); // L_: o nil {c} {fqn} "_R" | ||
198 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} | ||
199 | lua_pushvalue(L_, LUA_REGISTRYINDEX); // L_: o nil {c} {fqn} _R | ||
200 | std::ignore = DiscoverObjectNameRecur(L_, std::numeric_limits<int>::max(), 1); | ||
201 | } | ||
202 | lua_pop(L_, 3); // L_: o "result" | ||
203 | STACK_CHECK(L_, 1); | ||
204 | lua_pushstring(L_, luaL_typename(L_, 1)); // L_: o "result" "type" | ||
205 | lua_replace(L_, -3); // L_: "type" "result" | ||
206 | return 2; | ||
207 | } | ||
208 | |||
diff --git a/src/nameof.h b/src/nameof.h new file mode 100644 index 0000000..0e15a70 --- /dev/null +++ b/src/nameof.h | |||
@@ -0,0 +1,5 @@ | |||
1 | #pragma once | ||
2 | |||
3 | #include "macros_and_utils.h" | ||
4 | |||
5 | LUAG_FUNC(nameof); | ||
diff --git a/src/state.cpp b/src/state.cpp index 8520346..271e3a7 100644 --- a/src/state.cpp +++ b/src/state.cpp | |||
@@ -107,7 +107,7 @@ static void open1lib(lua_State* L_, std::string_view const& name_) | |||
107 | luaL_requiref(L_, _name.data(), _libfunc, !isLanesCore); // L_: {lib} | 107 | luaL_requiref(L_, _name.data(), _libfunc, !isLanesCore); // L_: {lib} |
108 | // lanes.core doesn't declare a global, so scan it here and now | 108 | // lanes.core doesn't declare a global, so scan it here and now |
109 | if (isLanesCore) { | 109 | if (isLanesCore) { |
110 | populate_func_lookup_table(L_, -1, _name); | 110 | tools::PopulateFuncLookupTable(L_, -1, _name); |
111 | } | 111 | } |
112 | lua_pop(L_, 1); // L_: | 112 | lua_pop(L_, 1); // L_: |
113 | STACK_CHECK(L_, 0); | 113 | STACK_CHECK(L_, 0); |
@@ -340,7 +340,7 @@ lua_State* luaG_newstate(Universe* U_, SourceState from_, std::optional<std::str | |||
340 | STACK_CHECK(_L, 0); | 340 | STACK_CHECK(_L, 0); |
341 | // after all this, register everything we find in our name<->function database | 341 | // after all this, register everything we find in our name<->function database |
342 | lua_pushglobaltable(_L); // L: _G | 342 | lua_pushglobaltable(_L); // L: _G |
343 | populate_func_lookup_table(_L, -1, {}); | 343 | tools::PopulateFuncLookupTable(_L, -1, {}); |
344 | lua_pop(_L, 1); // L: | 344 | lua_pop(_L, 1); // L: |
345 | STACK_CHECK(_L, 0); | 345 | STACK_CHECK(_L, 0); |
346 | 346 | ||
diff --git a/src/tools.cpp b/src/tools.cpp index d270aac..e9114e0 100644 --- a/src/tools.cpp +++ b/src/tools.cpp | |||
@@ -89,28 +89,32 @@ static constexpr int kWriterReturnCode{ 666 }; | |||
89 | 89 | ||
90 | // ################################################################################################# | 90 | // ################################################################################################# |
91 | 91 | ||
92 | // inspired from tconcat() in ltablib.c | 92 | namespace tools { |
93 | [[nodiscard]] static std::string_view luaG_pushFQN(lua_State* L_, int t_, int last_) | 93 | |
94 | { | 94 | // inspired from tconcat() in ltablib.c |
95 | luaL_Buffer _b; | 95 | [[nodiscard]] std::string_view PushFQN(lua_State* L_, int t_, int last_) |
96 | STACK_CHECK_START_REL(L_, 0); | 96 | { |
97 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... | 97 | luaL_Buffer _b; |
98 | luaL_buffinit(L_, &_b); // L_: ... {} ... &b? | 98 | STACK_CHECK_START_REL(L_, 0); |
99 | int _i{ 1 }; | 99 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... |
100 | for (; _i < last_; ++_i) { | 100 | luaL_buffinit(L_, &_b); // L_: ... {} ... &b? |
101 | lua_rawgeti(L_, t_, _i); | 101 | int _i{ 1 }; |
102 | luaL_addvalue(&_b); | 102 | for (; _i < last_; ++_i) { |
103 | luaL_addlstring(&_b, "/", 1); | 103 | lua_rawgeti(L_, t_, _i); |
104 | } | 104 | luaL_addvalue(&_b); |
105 | if (_i == last_) { // add last value (if interval was not empty) | 105 | luaL_addlstring(&_b, "/", 1); |
106 | lua_rawgeti(L_, t_, _i); | 106 | } |
107 | luaL_addvalue(&_b); | 107 | if (_i == last_) { // add last value (if interval was not empty) |
108 | lua_rawgeti(L_, t_, _i); | ||
109 | luaL_addvalue(&_b); | ||
110 | } | ||
111 | // &b is popped at that point (-> replaced by the result) | ||
112 | luaL_pushresult(&_b); // L_: ... {} ... "<result>" | ||
113 | STACK_CHECK(L_, 1); | ||
114 | return lua_tostringview(L_, -1); | ||
108 | } | 115 | } |
109 | // &b is popped at that point (-> replaced by the result) | 116 | |
110 | luaL_pushresult(&_b); // L_: ... {} ... "<result>" | 117 | } // namespace tools |
111 | STACK_CHECK(L_, 1); | ||
112 | return lua_tostringview(L_, -1); | ||
113 | } | ||
114 | 118 | ||
115 | // ################################################################################################# | 119 | // ################################################################################################# |
116 | 120 | ||
@@ -144,7 +148,7 @@ static void update_lookup_entry(lua_State* L_, int ctxBase_, int depth_) | |||
144 | ++depth_; | 148 | ++depth_; |
145 | lua_rawseti(L_, _fqn, depth_); // L_: ... {bfc} k o name? | 149 | lua_rawseti(L_, _fqn, depth_); // L_: ... {bfc} k o name? |
146 | // generate name | 150 | // generate name |
147 | std::string_view const _newName{ luaG_pushFQN(L_, _fqn, depth_) }; // L_: ... {bfc} k o name? "f.q.n" | 151 | std::string_view const _newName{ tools::PushFQN(L_, _fqn, depth_) }; // L_: ... {bfc} k o name? "f.q.n" |
148 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order | 152 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order |
149 | // on different VMs even when the tables are populated the exact same way. | 153 | // on different VMs even when the tables are populated the exact same way. |
150 | // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), | 154 | // When Lua is built with compatibility options (such as LUA_COMPAT_ALL), |
@@ -299,233 +303,59 @@ static void populate_func_lookup_table_recur(lua_State* L_, int dbIdx_, int i_, | |||
299 | 303 | ||
300 | // ################################################################################################# | 304 | // ################################################################################################# |
301 | 305 | ||
302 | // create a "fully.qualified.name" <-> function equivalence database | 306 | namespace tools { |
303 | void populate_func_lookup_table(lua_State* const L_, int const i_, std::string_view const& name_) | ||
304 | { | ||
305 | int const _in_base{ lua_absindex(L_, i_) }; | ||
306 | DEBUGSPEW_CODE(Universe* _U = universe_get(L_)); | ||
307 | std::string_view _name{ name_.empty() ? std::string_view{} : name_ }; | ||
308 | DEBUGSPEW_CODE(DebugSpew(_U) << L_ << ": populate_func_lookup_table('" << _name << "')" << std::endl); | ||
309 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); | ||
310 | STACK_GROW(L_, 3); | ||
311 | STACK_CHECK_START_REL(L_, 0); | ||
312 | kLookupRegKey.pushValue(L_); // L_: {} | ||
313 | int const _dbIdx{ lua_gettop(L_) }; | ||
314 | STACK_CHECK(L_, 1); | ||
315 | LUA_ASSERT(L_, lua_istable(L_, -1)); | ||
316 | if (lua_type(L_, _in_base) == LUA_TFUNCTION) { // for example when a module is a simple function | ||
317 | if (_name.empty()) { | ||
318 | _name = "nullptr"; | ||
319 | } | ||
320 | lua_pushvalue(L_, _in_base); // L_: {} f | ||
321 | std::ignore = lua_pushstringview(L_, _name); // L_: {} f name_ | ||
322 | lua_rawset(L_, -3); // L_: {} | ||
323 | std::ignore = lua_pushstringview(L_, _name); // L_: {} name_ | ||
324 | lua_pushvalue(L_, _in_base); // L_: {} name_ f | ||
325 | lua_rawset(L_, -3); // L_: {} | ||
326 | lua_pop(L_, 1); // L_: | ||
327 | } else if (lua_type(L_, _in_base) == LUA_TTABLE) { | ||
328 | lua_newtable(L_); // L_: {} {fqn} | ||
329 | int _startDepth{ 0 }; | ||
330 | if (!_name.empty()) { | ||
331 | STACK_CHECK(L_, 2); | ||
332 | std::ignore = lua_pushstringview(L_, _name); // L_: {} {fqn} "name" | ||
333 | // generate a name, and if we already had one name, keep whichever is the shorter | ||
334 | lua_pushvalue(L_, _in_base); // L_: {} {fqn} "name" t | ||
335 | update_lookup_entry(L_, _dbIdx, _startDepth); // L_: {} {fqn} "name" | ||
336 | // don't forget to store the name at the bottom of the fqn stack | ||
337 | lua_rawseti(L_, -2, ++_startDepth); // L_: {} {fqn} | ||
338 | STACK_CHECK(L_, 2); | ||
339 | } | ||
340 | // retrieve the cache, create it if we haven't done it yet | ||
341 | std::ignore = kLookupCacheRegKey.getSubTable(L_, 0, 0); // L_: {} {fqn} {cache} | ||
342 | // process everything we find in that table, filling in lookup data for all functions and tables we see there | ||
343 | populate_func_lookup_table_recur(L_, _dbIdx, _in_base, _startDepth); | ||
344 | lua_pop(L_, 3); // L_: | ||
345 | } else { | ||
346 | lua_pop(L_, 1); // L_: | ||
347 | raise_luaL_error(L_, "unsupported module type %s", lua_typename(L_, lua_type(L_, _in_base))); | ||
348 | } | ||
349 | STACK_CHECK(L_, 0); | ||
350 | } | ||
351 | |||
352 | // ################################################################################################# | ||
353 | 307 | ||
354 | // Return some name helping to identify an object | 308 | // create a "fully.qualified.name" <-> function equivalence database |
355 | [[nodiscard]] static int DiscoverObjectNameRecur(lua_State* L_, int shortest_, int depth_) | 309 | void PopulateFuncLookupTable(lua_State* const L_, int const i_, std::string_view const& name_) |
356 | { | 310 | { |
357 | static constexpr int kWhat{ 1 }; // the object to investigate // L_: o "r" {c} {fqn} ... {?} | 311 | int const _in_base{ lua_absindex(L_, i_) }; |
358 | static constexpr int kResult{ 2 }; // where the result string is stored | 312 | DEBUGSPEW_CODE(Universe* _U = universe_get(L_)); |
359 | static constexpr int kCache{ 3 }; // a cache | 313 | std::string_view _name{ name_.empty() ? std::string_view{} : name_ }; |
360 | static constexpr int kFQN{ 4 }; // the name compositing stack | 314 | DEBUGSPEW_CODE(DebugSpew(_U) << L_ << ": PopulateFuncLookupTable('" << _name << "')" << std::endl); |
361 | // no need to scan this table if the name we will discover is longer than one we already know | 315 | DEBUGSPEW_CODE(DebugSpewIndentScope _scope{ _U }); |
362 | if (shortest_ <= depth_ + 1) { | 316 | STACK_GROW(L_, 3); |
363 | return shortest_; | 317 | STACK_CHECK_START_REL(L_, 0); |
364 | } | 318 | kLookupRegKey.pushValue(L_); // L_: {} |
365 | STACK_GROW(L_, 3); | 319 | int const _dbIdx{ lua_gettop(L_) }; |
366 | STACK_CHECK_START_REL(L_, 0); | 320 | STACK_CHECK(L_, 1); |
367 | // stack top contains the table to search in | 321 | LUA_ASSERT(L_, lua_istable(L_, -1)); |
368 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} | 322 | if (lua_type(L_, _in_base) == LUA_TFUNCTION) { // for example when a module is a simple function |
369 | lua_rawget(L_, kCache); // L_: o "r" {c} {fqn} ... {?} nil/1 | 323 | if (_name.empty()) { |
370 | // if table is already visited, we are done | 324 | _name = "nullptr"; |
371 | if (!lua_isnil(L_, -1)) { | ||
372 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} | ||
373 | return shortest_; | ||
374 | } | ||
375 | // examined table is not in the cache, add it now | ||
376 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} | ||
377 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} | ||
378 | lua_pushinteger(L_, 1); // L_: o "r" {c} {fqn} ... {?} {?} 1 | ||
379 | lua_rawset(L_, kCache); // L_: o "r" {c} {fqn} ... {?} | ||
380 | // scan table contents | ||
381 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} nil | ||
382 | while (lua_next(L_, -2)) { // L_: o "r" {c} {fqn} ... {?} k v | ||
383 | // std::string_view const _strKey{ (lua_type(L_, -2) == LUA_TSTRING) ? lua_tostringview(L_, -2) : "" }; // only for debugging | ||
384 | // lua_Number const numKey = (lua_type(L_, -2) == LUA_TNUMBER) ? lua_tonumber(L_, -2) : -6666; // only for debugging | ||
385 | STACK_CHECK(L_, 2); | ||
386 | // append key name to fqn stack | ||
387 | ++depth_; | ||
388 | lua_pushvalue(L_, -2); // L_: o "r" {c} {fqn} ... {?} k v k | ||
389 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k v | ||
390 | if (lua_rawequal(L_, -1, kWhat)) { // is it what we are looking for? | ||
391 | STACK_CHECK(L_, 2); | ||
392 | // update shortest name | ||
393 | if (depth_ < shortest_) { | ||
394 | shortest_ = depth_; | ||
395 | std::ignore = luaG_pushFQN(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k v "fqn" | ||
396 | lua_replace(L_, kResult); // L_: o "r" {c} {fqn} ... {?} k v | ||
397 | } | ||
398 | // no need to search further at this level | ||
399 | lua_pop(L_, 2); // L_: o "r" {c} {fqn} ... {?} | ||
400 | STACK_CHECK(L_, 0); | ||
401 | break; | ||
402 | } | ||
403 | switch (lua_type(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k v | ||
404 | default: // nil, boolean, light userdata, number and string aren't identifiable | ||
405 | break; | ||
406 | |||
407 | case LUA_TTABLE: // L_: o "r" {c} {fqn} ... {?} k {} | ||
408 | STACK_CHECK(L_, 2); | ||
409 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, depth_); | ||
410 | // search in the table's metatable too | ||
411 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k {} {mt} | ||
412 | if (lua_istable(L_, -1)) { | ||
413 | ++depth_; | ||
414 | lua_pushliteral(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} k {} {mt} "__metatable" | ||
415 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} | ||
416 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, depth_); | ||
417 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} nil | ||
418 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k {} {mt} | ||
419 | --depth_; | ||
420 | } | ||
421 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k {} | ||
422 | } | ||
423 | STACK_CHECK(L_, 2); | ||
424 | break; | ||
425 | |||
426 | case LUA_TTHREAD: // L_: o "r" {c} {fqn} ... {?} k T | ||
427 | // TODO: explore the thread's stack frame looking for our culprit? | ||
428 | break; | ||
429 | |||
430 | case LUA_TUSERDATA: // L_: o "r" {c} {fqn} ... {?} k U | ||
431 | STACK_CHECK(L_, 2); | ||
432 | // search in the object's metatable (some modules are built that way) | ||
433 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k U {mt} | ||
434 | if (lua_istable(L_, -1)) { | ||
435 | ++depth_; | ||
436 | lua_pushliteral(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} k U {mt} "__metatable" | ||
437 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k U {mt} | ||
438 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, depth_); | ||
439 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k U {mt} nil | ||
440 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k U {mt} | ||
441 | --depth_; | ||
442 | } | ||
443 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U | ||
444 | } | 325 | } |
445 | STACK_CHECK(L_, 2); | 326 | lua_pushvalue(L_, _in_base); // L_: {} f |
446 | // search in the object's uservalues | 327 | std::ignore = lua_pushstringview(L_, _name); // L_: {} f name_ |
447 | { | 328 | lua_rawset(L_, -3); // L_: {} |
448 | int _uvi{ 1 }; | 329 | std::ignore = lua_pushstringview(L_, _name); // L_: {} name_ |
449 | while (lua_getiuservalue(L_, -1, _uvi) != LUA_TNONE) { // L_: o "r" {c} {fqn} ... {?} k U {u} | 330 | lua_pushvalue(L_, _in_base); // L_: {} name_ f |
450 | if (lua_istable(L_, -1)) { // if it is a table, look inside | 331 | lua_rawset(L_, -3); // L_: {} |
451 | ++depth_; | 332 | lua_pop(L_, 1); // L_: |
452 | lua_pushliteral(L_, "uservalue"); // L_: o "r" {c} {fqn} ... {?} k v {u} "uservalue" | 333 | } else if (lua_type(L_, _in_base) == LUA_TTABLE) { |
453 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k v {u} | 334 | lua_newtable(L_); // L_: {} {fqn} |
454 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, depth_); | 335 | int _startDepth{ 0 }; |
455 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k v {u} nil | 336 | if (!_name.empty()) { |
456 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k v {u} | 337 | STACK_CHECK(L_, 2); |
457 | --depth_; | 338 | std::ignore = lua_pushstringview(L_, _name); // L_: {} {fqn} "name" |
458 | } | 339 | // generate a name, and if we already had one name, keep whichever is the shorter |
459 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U | 340 | lua_pushvalue(L_, _in_base); // L_: {} {fqn} "name" t |
460 | ++_uvi; | 341 | update_lookup_entry(L_, _dbIdx, _startDepth); // L_: {} {fqn} "name" |
461 | } | 342 | // don't forget to store the name at the bottom of the fqn stack |
462 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 343 | lua_rawseti(L_, -2, ++_startDepth); // L_: {} {fqn} |
463 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U | 344 | STACK_CHECK(L_, 2); |
464 | } | 345 | } |
465 | STACK_CHECK(L_, 2); | 346 | // retrieve the cache, create it if we haven't done it yet |
466 | break; | 347 | std::ignore = kLookupCacheRegKey.getSubTable(L_, 0, 0); // L_: {} {fqn} {cache} |
348 | // process everything we find in that table, filling in lookup data for all functions and tables we see there | ||
349 | populate_func_lookup_table_recur(L_, _dbIdx, _in_base, _startDepth); | ||
350 | lua_pop(L_, 3); // L_: | ||
351 | } else { | ||
352 | lua_pop(L_, 1); // L_: | ||
353 | raise_luaL_error(L_, "unsupported module type %s", lua_typename(L_, lua_type(L_, _in_base))); | ||
467 | } | 354 | } |
468 | // make ready for next iteration | 355 | STACK_CHECK(L_, 0); |
469 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k | ||
470 | // remove name from fqn stack | ||
471 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} k nil | ||
472 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {?} k | ||
473 | STACK_CHECK(L_, 1); | ||
474 | --depth_; | ||
475 | } // L_: o "r" {c} {fqn} ... {?} | ||
476 | STACK_CHECK(L_, 0); | ||
477 | // remove the visited table from the cache, in case a shorter path to the searched object exists | ||
478 | lua_pushvalue(L_, -1); // L_: o "r" {c} {fqn} ... {?} {?} | ||
479 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} {?} nil | ||
480 | lua_rawset(L_, kCache); // L_: o "r" {c} {fqn} ... {?} | ||
481 | STACK_CHECK(L_, 0); | ||
482 | return shortest_; | ||
483 | } | ||
484 | |||
485 | // ################################################################################################# | ||
486 | |||
487 | // "type", "name" = lanes.nameof(o) | ||
488 | int luaG_nameof(lua_State* L_) | ||
489 | { | ||
490 | int const _what{ lua_gettop(L_) }; | ||
491 | if (_what > 1) { | ||
492 | raise_luaL_argerror(L_, _what, "too many arguments."); | ||
493 | } | ||
494 | |||
495 | // nil, boolean, light userdata, number and string aren't identifiable | ||
496 | if (lua_type(L_, 1) < LUA_TTABLE) { | ||
497 | lua_pushstring(L_, luaL_typename(L_, 1)); // L_: o "type" | ||
498 | lua_insert(L_, -2); // L_: "type" o | ||
499 | return 2; | ||
500 | } | 356 | } |
501 | 357 | ||
502 | STACK_GROW(L_, 4); | 358 | } // namespace tools |
503 | STACK_CHECK_START_REL(L_, 0); | ||
504 | // this slot will contain the shortest name we found when we are done | ||
505 | lua_pushnil(L_); // L_: o nil | ||
506 | // push a cache that will contain all already visited tables | ||
507 | lua_newtable(L_); // L_: o nil {c} | ||
508 | // push a table whose contents are strings that, when concatenated, produce unique name | ||
509 | lua_newtable(L_); // L_: o nil {c} {fqn} | ||
510 | // {fqn}[1] = "_G" | ||
511 | lua_pushliteral(L_, LUA_GNAME); // L_: o nil {c} {fqn} "_G" | ||
512 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} | ||
513 | // this is where we start the search | ||
514 | lua_pushglobaltable(L_); // L_: o nil {c} {fqn} _G | ||
515 | std::ignore = DiscoverObjectNameRecur(L_, std::numeric_limits<int>::max(), 1); | ||
516 | if (lua_isnil(L_, 2)) { // try again with registry, just in case... | ||
517 | lua_pop(L_, 1); // L_: o nil {c} {fqn} | ||
518 | lua_pushliteral(L_, "_R"); // L_: o nil {c} {fqn} "_R" | ||
519 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} | ||
520 | lua_pushvalue(L_, LUA_REGISTRYINDEX); // L_: o nil {c} {fqn} _R | ||
521 | std::ignore = DiscoverObjectNameRecur(L_, std::numeric_limits<int>::max(), 1); | ||
522 | } | ||
523 | lua_pop(L_, 3); // L_: o "result" | ||
524 | STACK_CHECK(L_, 1); | ||
525 | lua_pushstring(L_, luaL_typename(L_, 1)); // L_: o "result" "type" | ||
526 | lua_replace(L_, -3); // L_: "type" "result" | ||
527 | return 2; | ||
528 | } | ||
529 | 359 | ||
530 | // ################################################################################################# | 360 | // ################################################################################################# |
531 | 361 | ||
diff --git a/src/tools.h b/src/tools.h index 3659b42..e240fdb 100644 --- a/src/tools.h +++ b/src/tools.h | |||
@@ -22,18 +22,16 @@ enum class FuncSubType | |||
22 | 22 | ||
23 | // ################################################################################################# | 23 | // ################################################################################################# |
24 | 24 | ||
25 | [[nodiscard]] int luaG_nameof(lua_State* L_); | ||
26 | |||
27 | void populate_func_lookup_table(lua_State* const L_, int const i_, std::string_view const& name_); | ||
28 | |||
29 | namespace tools { | ||
30 | void SerializeRequire(lua_State* L_); | ||
31 | } // namespace tools | ||
32 | |||
33 | // ################################################################################################# | ||
34 | |||
35 | // xxh64 of string "kConfigRegKey" generated at https://www.pelock.com/products/hash-calculator | 25 | // xxh64 of string "kConfigRegKey" generated at https://www.pelock.com/products/hash-calculator |
36 | static constexpr RegistryUniqueKey kConfigRegKey{ 0x608379D20A398046ull }; // registry key to access the configuration | 26 | static constexpr RegistryUniqueKey kConfigRegKey{ 0x608379D20A398046ull }; // registry key to access the configuration |
37 | 27 | ||
38 | // xxh64 of string "kLookupRegKey" generated at https://www.pelock.com/products/hash-calculator | 28 | // xxh64 of string "kLookupRegKey" generated at https://www.pelock.com/products/hash-calculator |
39 | static constexpr RegistryUniqueKey kLookupRegKey{ 0xBF1FC5CF3C6DD47Bull }; // registry key to access the lookup database | 29 | static constexpr RegistryUniqueKey kLookupRegKey{ 0xBF1FC5CF3C6DD47Bull }; // registry key to access the lookup database |
30 | |||
31 | // ################################################################################################# | ||
32 | |||
33 | namespace tools { | ||
34 | void PopulateFuncLookupTable(lua_State* const L_, int const i_, std::string_view const& name_); | ||
35 | [[nodiscard]] std::string_view PushFQN(lua_State* L_, int t_, int last_); | ||
36 | void SerializeRequire(lua_State* L_); | ||
37 | } // namespace tools | ||