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 | ||
