diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/nameof.cpp | 310 | ||||
| -rw-r--r-- | src/tools.cpp | 9 | ||||
| -rw-r--r-- | src/tools.hpp | 2 |
3 files changed, 206 insertions, 115 deletions
diff --git a/src/nameof.cpp b/src/nameof.cpp index b3874f3..1f338a5 100644 --- a/src/nameof.cpp +++ b/src/nameof.cpp | |||
| @@ -31,137 +31,225 @@ THE SOFTWARE. | |||
| 31 | 31 | ||
| 32 | // ################################################################################################# | 32 | // ################################################################################################# |
| 33 | 33 | ||
| 34 | DECLARE_UNIQUE_TYPE(FqnLength, lua_Unsigned); | ||
| 35 | |||
| 34 | // Return some name helping to identify an object | 36 | // Return some name helping to identify an object |
| 35 | [[nodiscard]] | 37 | [[nodiscard]] |
| 36 | static int DiscoverObjectNameRecur(lua_State* const L_, int shortest_, TableIndex const curDepth_) | 38 | FqnLength DiscoverObjectNameRecur(lua_State* const L_, FqnLength const shortest_) |
| 37 | { | 39 | { |
| 38 | static constexpr StackIndex kWhat{ 1 }; // the object to investigate // L_: o "r" {c} {fqn} ... {?} | 40 | static constexpr StackIndex kWhat{ 1 }; // the object to investigate // L_: o "r" {c} {fqn} ... <> |
| 39 | static constexpr StackIndex kResult{ 2 }; // where the result string is stored | 41 | static constexpr StackIndex kResult{ 2 }; // where the result string is stored |
| 40 | static constexpr StackIndex kCache{ 3 }; // a cache | 42 | static constexpr StackIndex kCache{ 3 }; // a cache, where visited locations remember the FqnLength to reach them |
| 41 | static constexpr StackIndex kFQN{ 4 }; // the name compositing stack | 43 | static constexpr StackIndex kFQN{ 4 }; // the name compositing stack |
| 42 | // no need to scan this table if the name we will discover is longer than one we already know | 44 | |
| 43 | TableIndex const _nextDepth{ curDepth_ + 1 }; | 45 | // no need to scan this location if the name we will discover is longer than one we already know |
| 44 | if (shortest_ <= _nextDepth) { | 46 | FqnLength const _fqnLength{ lua_rawlen(L_, kFQN) }; |
| 47 | if (shortest_ <= _fqnLength) { | ||
| 45 | return shortest_; | 48 | return shortest_; |
| 46 | } | 49 | } |
| 47 | 50 | ||
| 48 | auto _pushNameOnFQN = [L_](std::string_view const& name_, TableIndex const depth_) { | 51 | // in: k, v at the top of the stack |
| 49 | luaG_pushstring(L_, name_); // L_: o "r" {c} {fqn} ... name_ | 52 | static constexpr auto _pushNameOnFQN = [](lua_State* const L_) { |
| 50 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... {mt} | 53 | STACK_CHECK_START_REL(L_, 0); |
| 54 | lua_pushvalue(L_, -2); // L_: o "r" {c} {fqn} ... k v k | ||
| 55 | auto const _keyType{ luaG_type(L_, kIdxTop) }; | ||
| 56 | if (_keyType != LuaType::STRING) { | ||
| 57 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... k v | ||
| 58 | luaG_pushstring(L_, "<%s>", luaG_typename(L_, _keyType).data()); // L_: o "r" {c} {fqn} ... k v "<type of k>" | ||
| 59 | } else { | ||
| 60 | // decorate the key string with something that tells us the type of the value | ||
| 61 | switch (luaG_type(L_, StackIndex{ -2 })) { | ||
| 62 | default: | ||
| 63 | luaG_pushstring(L_, "??"); // L_: o "r" {c} {fqn} ... k v "k" "??" | ||
| 64 | break; | ||
| 65 | case LuaType::FUNCTION: | ||
| 66 | luaG_pushstring(L_, "()"); // L_: o "r" {c} {fqn} ... k v "k" "()" | ||
| 67 | break; | ||
| 68 | case LuaType::TABLE: | ||
| 69 | luaG_pushstring(L_, "[]"); // L_: o "r" {c} {fqn} ... k v "k" "[]" | ||
| 70 | break; | ||
| 71 | case LuaType::USERDATA: | ||
| 72 | luaG_pushstring(L_, "<>"); // L_: o "r" {c} {fqn} ... k v "k" "<>" | ||
| 73 | break; | ||
| 74 | } | ||
| 75 | lua_concat(L_, 2); // L_: o "r" {c} {fqn} ... k v "k??" | ||
| 76 | } | ||
| 77 | |||
| 78 | FqnLength const _depth{ lua_rawlen(L_, kFQN) + 1 }; | ||
| 79 | lua_rawseti(L_, kFQN, _depth); // L_: o "r" {c} {fqn} ... k v | ||
| 80 | STACK_CHECK(L_, 0); | ||
| 81 | STACK_CHECK(L_, 0); | ||
| 82 | return _depth; | ||
| 51 | }; | 83 | }; |
| 52 | 84 | ||
| 53 | auto _popNameFromFQN = [L_](TableIndex const depth_) { | 85 | static constexpr auto _popNameFromFQN = [](lua_State* const L_) { |
| 86 | STACK_CHECK_START_REL(L_, 0); | ||
| 54 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... nil | 87 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... nil |
| 55 | lua_rawseti(L_, kFQN, depth_); // L_: o "r" {c} {fqn} ... | 88 | lua_rawseti(L_, kFQN, lua_rawlen(L_, kFQN)); // L_: o "r" {c} {fqn} ... |
| 89 | STACK_CHECK(L_, 0); | ||
| 56 | }; | 90 | }; |
| 57 | 91 | ||
| 58 | auto _recurseIfTableThenPop = [&_pushNameOnFQN, &_popNameFromFQN, L_](std::string_view const& name_, TableIndex const depth_, int shortest_) { | 92 | static constexpr auto _recurseThenPop = [](lua_State* const L_, FqnLength const shortest_) -> FqnLength { |
| 59 | STACK_CHECK_START_REL(L_, 0); // L_: o "r" {c} {fqn} ... <> {}? | 93 | STACK_CHECK_START_REL(L_, 0); // L_: o "r" {c} {fqn} ... <> |
| 60 | if (lua_istable(L_, kIdxTop)) { | 94 | FqnLength r_{ shortest_ }; |
| 61 | _pushNameOnFQN(name_, depth_); | 95 | auto const _type{ luaG_type(L_, kIdxTop) }; |
| 62 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, TableIndex{ depth_ + 1 }); | 96 | if (_type == LuaType::TABLE || _type == LuaType::USERDATA || _type == LuaType::FUNCTION) { |
| 63 | _popNameFromFQN(depth_); | 97 | r_ = DiscoverObjectNameRecur(L_, shortest_); |
| 98 | STACK_CHECK(L_, 0); | ||
| 99 | _popNameFromFQN(L_); | ||
| 100 | STACK_CHECK(L_, 0); | ||
| 64 | } | 101 | } |
| 65 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... <> | 102 | |
| 103 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... | ||
| 66 | STACK_CHECK(L_, -1); | 104 | STACK_CHECK(L_, -1); |
| 67 | return shortest_; | 105 | return r_; |
| 68 | }; | 106 | }; |
| 69 | 107 | ||
| 70 | STACK_GROW(L_, 3); | 108 | // in: k, v at the top of the stack |
| 71 | STACK_CHECK_START_REL(L_, 0); | 109 | // out: v popped from the stack |
| 72 | // stack top contains the table to search in | 110 | static constexpr auto _processKeyValue = [](lua_State* const L_, FqnLength const shortest_) -> FqnLength { |
| 73 | LUA_ASSERT(L_, lua_istable(L_, kIdxTop)); | 111 | FqnLength _r{ shortest_ }; |
| 74 | lua_pushvalue(L_, kIdxTop); // L_: o "r" {c} {fqn} ... {?} {?} | 112 | STACK_GROW(L_, 2); |
| 75 | lua_rawget(L_, kCache); // L_: o "r" {c} {fqn} ... {?} nil/1 | 113 | STACK_CHECK_START_REL(L_, 0); // L_: o "r" {c} {fqn} ... k v |
| 76 | // if table is already visited, we are done | 114 | |
| 77 | if (!lua_isnil(L_, kIdxTop)) { | 115 | // filter out uninteresting values |
| 78 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} | 116 | auto const _valType{ luaG_type(L_, kIdxTop) }; |
| 79 | return shortest_; | 117 | if (_valType == LuaType::BOOLEAN || _valType == LuaType::LIGHTUSERDATA || _valType == LuaType::NUMBER || _valType == LuaType::STRING) { |
| 80 | } | 118 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... k |
| 81 | // examined table is not in the cache, add it now | 119 | return _r; |
| 82 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} | 120 | } |
| 83 | // cache[o] = 1 | 121 | |
| 84 | lua_pushvalue(L_, kIdxTop); // L_: o "r" {c} {fqn} ... {?} {?} | ||
| 85 | lua_pushinteger(L_, 1); // L_: o "r" {c} {fqn} ... {?} {?} 1 | ||
| 86 | lua_rawset(L_, kCache); // L_: o "r" {c} {fqn} ... {?} | ||
| 87 | // scan table contents | ||
| 88 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} nil | ||
| 89 | while (lua_next(L_, -2)) { // L_: o "r" {c} {fqn} ... {?} k v | ||
| 90 | // std::string_view const _strKey{ (luaG_type(L_, -2) == LuaType::STRING) ? luaG_tostring(L_, -2) : "" }; // only for debugging | ||
| 91 | // lua_Number const numKey = (luaG_type(L_, -2) == LuaType::NUMBER) ? lua_tonumber(L_, -2) : -6666; // only for debugging | ||
| 92 | STACK_CHECK(L_, 2); | ||
| 93 | // append key name to fqn stack | 122 | // append key name to fqn stack |
| 94 | lua_pushvalue(L_, -2); // L_: o "r" {c} {fqn} ... {?} k v k | 123 | FqnLength const _depth{ _pushNameOnFQN(L_) }; // L_: o "r" {c} {fqn} ... k v |
| 95 | lua_rawseti(L_, kFQN, _nextDepth); // L_: o "r" {c} {fqn} ... {?} k v | 124 | |
| 125 | // process the value | ||
| 96 | if (lua_rawequal(L_, kIdxTop, kWhat)) { // is it what we are looking for? | 126 | if (lua_rawequal(L_, kIdxTop, kWhat)) { // is it what we are looking for? |
| 97 | STACK_CHECK(L_, 2); | ||
| 98 | // update shortest name | 127 | // update shortest name |
| 99 | if (_nextDepth < shortest_) { | 128 | if (_depth < _r) { |
| 100 | shortest_ = _nextDepth; | 129 | _r = _depth; |
| 101 | std::ignore = tools::PushFQN(L_, kFQN, _nextDepth); // L_: o "r" {c} {fqn} ... {?} k v "fqn" | 130 | std::ignore = tools::PushFQN(L_, kFQN); // L_: o "r" {c} {fqn} ... k v "fqn" |
| 102 | lua_replace(L_, kResult); // L_: o "r" {c} {fqn} ... {?} k v | 131 | lua_replace(L_, kResult); // L_: o "r" {c} {fqn} ... k v |
| 103 | } | 132 | } |
| 104 | // no need to search further at this level | 133 | |
| 105 | lua_pop(L_, 2); // L_: o "r" {c} {fqn} ... {?} | 134 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... k |
| 106 | STACK_CHECK(L_, 0); | 135 | _popNameFromFQN(L_); |
| 107 | break; | 136 | } else { |
| 137 | // let's see if the value contains what we are looking for | ||
| 138 | _r = _recurseThenPop(L_, _r); // L_: o "r" {c} {fqn} ... k | ||
| 108 | } | 139 | } |
| 109 | switch (luaG_type(L_, kIdxTop)) { // L_: o "r" {c} {fqn} ... {?} k v | ||
| 110 | default: // nil, boolean, light userdata, number and string aren't identifiable | ||
| 111 | break; | ||
| 112 | |||
| 113 | case LuaType::TABLE: // L_: o "r" {c} {fqn} ... {?} k {} | ||
| 114 | STACK_CHECK(L_, 2); | ||
| 115 | shortest_ = DiscoverObjectNameRecur(L_, shortest_, _nextDepth); | ||
| 116 | // search in the table's metatable too | ||
| 117 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k {} {mt} | ||
| 118 | shortest_ = _recurseIfTableThenPop("__metatable", _nextDepth, shortest_); // L_: o "r" {c} {fqn} ... {?} k {} | ||
| 119 | } | ||
| 120 | STACK_CHECK(L_, 2); | ||
| 121 | break; | ||
| 122 | |||
| 123 | case LuaType::THREAD: // L_: o "r" {c} {fqn} ... {?} k T | ||
| 124 | // TODO: explore the thread's stack frame looking for our culprit? | ||
| 125 | break; | ||
| 126 | |||
| 127 | case LuaType::USERDATA: // L_: o "r" {c} {fqn} ... {?} k U | ||
| 128 | STACK_CHECK(L_, 2); | ||
| 129 | // search in the object's metatable (some modules are built that way) | ||
| 130 | if (lua_getmetatable(L_, -1)) { // L_: o "r" {c} {fqn} ... {?} k U {mt} | ||
| 131 | shortest_ = _recurseIfTableThenPop("__metatable", _nextDepth, shortest_); // L_: o "r" {c} {fqn} ... {?} k U | ||
| 132 | } | ||
| 133 | 140 | ||
| 134 | STACK_CHECK(L_, 2); | 141 | STACK_CHECK(L_, -1); |
| 135 | // search in the object's uservalues | 142 | return _r; |
| 136 | { | 143 | }; |
| 137 | UserValueIndex _uvi{ 1 }; | 144 | |
| 138 | while (lua_getiuservalue(L_, kIdxTop, _uvi) != LUA_TNONE) { // L_: o "r" {c} {fqn} ... {?} k U {u} | 145 | static constexpr auto _scanTable = [](lua_State* const L_, FqnLength const shortest_) -> FqnLength { |
| 139 | shortest_ = _recurseIfTableThenPop("uservalue", _nextDepth, shortest_); // L_: o "r" {c} {fqn} ... {?} k U | 146 | FqnLength r_{ shortest_ }; |
| 140 | ++_uvi; | 147 | STACK_GROW(L_, 2); |
| 141 | } | 148 | STACK_CHECK_START_REL(L_, 0); |
| 142 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | 149 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} nil |
| 143 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k U | 150 | while (lua_next(L_, -2)) { // L_: o "r" {c} {fqn} ... {?} k v |
| 151 | r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... {?} k | ||
| 152 | } // L_: o "r" {c} {fqn} ... {?} | ||
| 153 | |||
| 154 | if (lua_getmetatable(L_, kIdxTop)) { // L_: o "r" {c} {fqn} ... {?} {mt} | ||
| 155 | lua_pushstring(L_, "__metatable"); // L_: o "r" {c} {fqn} ... {?} {mt} "__metatable" | ||
| 156 | lua_insert(L_, -2); // L_: o "r" {c} {fqn} ... {?} "__metatable" {mt} | ||
| 157 | r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... {?} "__metatable" | ||
| 158 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} | ||
| 159 | } | ||
| 160 | |||
| 161 | STACK_CHECK(L_, 0); | ||
| 162 | return r_; | ||
| 163 | }; | ||
| 164 | |||
| 165 | static constexpr auto _scanUserData = [](lua_State* const L_, FqnLength const shortest_) -> FqnLength { | ||
| 166 | FqnLength r_{ shortest_ }; | ||
| 167 | FqnLength const _depth{ lua_rawlen(L_, kFQN) + 1 }; | ||
| 168 | STACK_GROW(L_, 2); | ||
| 169 | STACK_CHECK_START_REL(L_, 0); | ||
| 170 | if (lua_getmetatable(L_, kIdxTop)) { // L_: o "r" {c} {fqn} ... U {mt} | ||
| 171 | lua_pushstring(L_, "__metatable"); // L_: o "r" {c} {fqn} ... U {mt} "__metatable" | ||
| 172 | lua_insert(L_, -2); // L_: o "r" {c} {fqn} ... U "__metatable" {mt} | ||
| 173 | r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... U "__metatable" | ||
| 174 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... U | ||
| 175 | } | ||
| 176 | |||
| 177 | STACK_CHECK(L_, 0); | ||
| 178 | |||
| 179 | UserValueIndex _uvi{ 0 }; | ||
| 180 | while (lua_getiuservalue(L_, kIdxTop, ++_uvi) != LUA_TNONE) { // L_: o "r" {c} {fqn} ... U uv | ||
| 181 | luaG_pushstring(L_, "<uv:%d>", _uvi); // L_: o "r" {c} {fqn} ... U uv name | ||
| 182 | lua_insert(L_, -2); // L_: o "r" {c} {fqn} ... U name uv | ||
| 183 | r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... U name | ||
| 184 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... U | ||
| 185 | } | ||
| 186 | |||
| 187 | // when lua_getiuservalue() returned LUA_TNONE, it pushed a nil. pop it now | ||
| 188 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... U | ||
| 189 | |||
| 190 | STACK_CHECK(L_, 0); | ||
| 191 | return r_; | ||
| 192 | }; | ||
| 193 | |||
| 194 | static constexpr auto _scanFunction = [](lua_State* const L_, FqnLength const shortest_) -> FqnLength { | ||
| 195 | FqnLength r_{ shortest_ }; | ||
| 196 | STACK_GROW(L_, 2); | ||
| 197 | STACK_CHECK_START_REL(L_, 0); // L_: o "r" {c} {fqn} ... F | ||
| 198 | int _n{ 0 }; | ||
| 199 | for (char const* _upname{}; (_upname = lua_getupvalue(L_, kIdxTop, ++_n));) { // L_: o "r" {c} {fqn} ... F up | ||
| 200 | if (*_upname == 0) { | ||
| 201 | _upname = "<C>"; | ||
| 144 | } | 202 | } |
| 145 | STACK_CHECK(L_, 2); | ||
| 146 | break; | ||
| 147 | 203 | ||
| 148 | case LuaType::FUNCTION: // L_: o "r" {c} {fqn} ... {?} k F | 204 | luaG_pushstring(L_, "upvalue:%s", _upname); // L_: o "r" {c} {fqn} ... F up name |
| 149 | // TODO: explore the function upvalues | 205 | lua_insert(L_, -2); // L_: o "r" {c} {fqn} ... F name up |
| 150 | break; | 206 | r_ = _processKeyValue(L_, r_); // L_: o "r" {c} {fqn} ... F name |
| 207 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... F | ||
| 151 | } | 208 | } |
| 152 | // make ready for next iteration | 209 | |
| 153 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... {?} k | 210 | STACK_CHECK(L_, 0); |
| 154 | // remove name from fqn stack | 211 | return r_; |
| 155 | _popNameFromFQN(_nextDepth); | 212 | }; |
| 156 | STACK_CHECK(L_, 1); | 213 | |
| 157 | } // L_: o "r" {c} {fqn} ... {?} | 214 | STACK_GROW(L_, 2); |
| 158 | STACK_CHECK(L_, 0); | 215 | STACK_CHECK_START_REL(L_, 0); |
| 159 | // remove the visited table from the cache, in case a shorter path to the searched object exists | 216 | // stack top contains the location to search in (table, function, userdata) |
| 160 | lua_pushvalue(L_, kIdxTop); // L_: o "r" {c} {fqn} ... {?} {?} | 217 | [[maybe_unused]] auto const _typeWhere{ luaG_type(L_, kIdxTop) }; |
| 161 | lua_pushnil(L_); // L_: o "r" {c} {fqn} ... {?} {?} nil | 218 | LUA_ASSERT(L_, _typeWhere == LuaType::TABLE || _typeWhere == LuaType::USERDATA || _typeWhere == LuaType::FUNCTION); |
| 162 | lua_rawset(L_, kCache); // L_: o "r" {c} {fqn} ... {?} | 219 | lua_pushvalue(L_, kIdxTop); // L_: o "r" {c} {fqn} ... <> <> |
| 220 | lua_rawget(L_, kCache); // L_: o "r" {c} {fqn} ... <> nil/N | ||
| 221 | auto const _visitDepth{ lua_isnil(L_, kIdxTop) ? std::numeric_limits<FqnLength::type>::max() : lua_tointeger(L_, kIdxTop) }; | ||
| 222 | lua_pop(L_, 1); // L_: o "r" {c} {fqn} ... <> | ||
| 223 | // if location is already visited with a name of <= length, we are done | ||
| 224 | if (_visitDepth <= _fqnLength) { | ||
| 225 | return shortest_; | ||
| 226 | } | ||
| 227 | |||
| 228 | // examined location is not in the cache, add it now | ||
| 229 | // cache[o] = _fqnLength | ||
| 230 | lua_pushvalue(L_, kIdxTop); // L_: o "r" {c} {fqn} ... <> <> | ||
| 231 | lua_pushinteger(L_, _fqnLength); // L_: o "r" {c} {fqn} ... <> <> N | ||
| 232 | lua_rawset(L_, kCache); // L_: o "r" {c} {fqn} ... <> | ||
| 233 | |||
| 234 | FqnLength r_; | ||
| 235 | // scan location contents | ||
| 236 | switch (luaG_type(L_, kIdxTop)) { // L_: o "r" {c} {fqn} ... <> | ||
| 237 | default: | ||
| 238 | raise_luaL_error(L_, "unexpected error, please investigate"); | ||
| 239 | break; | ||
| 240 | case LuaType::TABLE: | ||
| 241 | r_ = _scanTable(L_, shortest_); | ||
| 242 | break; | ||
| 243 | case LuaType::USERDATA: | ||
| 244 | r_ = _scanUserData(L_, shortest_); | ||
| 245 | break; | ||
| 246 | case LuaType::FUNCTION: | ||
| 247 | r_ = _scanFunction(L_, shortest_); | ||
| 248 | break; | ||
| 249 | } | ||
| 250 | |||
| 163 | STACK_CHECK(L_, 0); | 251 | STACK_CHECK(L_, 0); |
| 164 | return shortest_; | 252 | return r_; |
| 165 | } | 253 | } |
| 166 | 254 | ||
| 167 | // ################################################################################################# | 255 | // ################################################################################################# |
| @@ -170,16 +258,17 @@ static int DiscoverObjectNameRecur(lua_State* const L_, int shortest_, TableInde | |||
| 170 | LUAG_FUNC(nameof) | 258 | LUAG_FUNC(nameof) |
| 171 | { | 259 | { |
| 172 | auto const _argCount{ lua_gettop(L_) }; | 260 | auto const _argCount{ lua_gettop(L_) }; |
| 173 | if (_argCount > 1) { | 261 | if (_argCount != 1) { |
| 174 | raise_luaL_argerror(L_, StackIndex{ _argCount }, "too many arguments."); | 262 | raise_luaL_argerror(L_, StackIndex{ _argCount }, "exactly 1 argument expected"); |
| 175 | } | 263 | } |
| 176 | 264 | ||
| 177 | // nil, boolean, light userdata, number and string aren't identifiable | 265 | // nil, boolean, light userdata, number and string aren't identifiable |
| 178 | auto const _isIdentifiable = [L_]() { | 266 | static constexpr auto _isIdentifiable = [](lua_State* const L_) { |
| 179 | auto const _valType{ luaG_type(L_, kIdxTop) }; | 267 | auto const _valType{ luaG_type(L_, kIdxTop) }; |
| 180 | return _valType == LuaType::TABLE || _valType == LuaType::FUNCTION || _valType == LuaType::USERDATA || _valType == LuaType::THREAD; | 268 | return _valType == LuaType::TABLE || _valType == LuaType::FUNCTION || _valType == LuaType::USERDATA || _valType == LuaType::THREAD; |
| 181 | }; | 269 | }; |
| 182 | if (!_isIdentifiable()) { | 270 | |
| 271 | if (!_isIdentifiable(L_)) { | ||
| 183 | luaG_pushstring(L_, luaG_typename(L_, kIdxTop)); // L_: o "type" | 272 | luaG_pushstring(L_, luaG_typename(L_, kIdxTop)); // L_: o "type" |
| 184 | lua_insert(L_, -2); // L_: "type" o | 273 | lua_insert(L_, -2); // L_: "type" o |
| 185 | return 2; | 274 | return 2; |
| @@ -198,13 +287,14 @@ LUAG_FUNC(nameof) | |||
| 198 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} | 287 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} |
| 199 | // this is where we start the search | 288 | // this is where we start the search |
| 200 | luaG_pushglobaltable(L_); // L_: o nil {c} {fqn} _G | 289 | luaG_pushglobaltable(L_); // L_: o nil {c} {fqn} _G |
| 201 | std::ignore = DiscoverObjectNameRecur(L_, std::numeric_limits<int>::max(), TableIndex{ 1 }); | 290 | auto const _foundInG{ DiscoverObjectNameRecur(L_, FqnLength{ std::numeric_limits<FqnLength::type>::max() }) }; |
| 202 | if (lua_isnil(L_, 2)) { // try again with registry, just in case... | 291 | if (lua_isnil(L_, 2)) { // try again with registry, just in case... |
| 292 | LUA_ASSERT(L_, _foundInG == std::numeric_limits<FqnLength::type>::max()); | ||
| 203 | lua_pop(L_, 1); // L_: o nil {c} {fqn} | 293 | lua_pop(L_, 1); // L_: o nil {c} {fqn} |
| 204 | luaG_pushstring(L_, "_R"); // L_: o nil {c} {fqn} "_R" | 294 | luaG_pushstring(L_, "_R"); // L_: o nil {c} {fqn} "_R" |
| 205 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} | 295 | lua_rawseti(L_, -2, 1); // L_: o nil {c} {fqn} |
| 206 | lua_pushvalue(L_, kIdxRegistry); // L_: o nil {c} {fqn} _R | 296 | lua_pushvalue(L_, kIdxRegistry); // L_: o nil {c} {fqn} _R |
| 207 | std::ignore = DiscoverObjectNameRecur(L_, std::numeric_limits<int>::max(), TableIndex{ 1 }); | 297 | [[maybe_unused]] auto const _foundInR{ DiscoverObjectNameRecur(L_, FqnLength{ std::numeric_limits<FqnLength::type>::max() }) }; |
| 208 | } | 298 | } |
| 209 | lua_pop(L_, 3); // L_: o "result" | 299 | lua_pop(L_, 3); // L_: o "result" |
| 210 | STACK_CHECK(L_, 1); | 300 | STACK_CHECK(L_, 1); |
diff --git a/src/tools.cpp b/src/tools.cpp index ee6d720..cbfefb0 100644 --- a/src/tools.cpp +++ b/src/tools.cpp | |||
| @@ -95,19 +95,20 @@ namespace tools { | |||
| 95 | 95 | ||
| 96 | // inspired from tconcat() in ltablib.c | 96 | // inspired from tconcat() in ltablib.c |
| 97 | [[nodiscard]] | 97 | [[nodiscard]] |
| 98 | std::string_view PushFQN(lua_State* const L_, StackIndex const t_, TableIndex const last_) | 98 | std::string_view PushFQN(lua_State* const L_, StackIndex const t_) |
| 99 | { | 99 | { |
| 100 | STACK_CHECK_START_REL(L_, 0); | 100 | STACK_CHECK_START_REL(L_, 0); |
| 101 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... | 101 | // Lua 5.4 pushes &b as light userdata on the stack. be aware of it... |
| 102 | luaL_Buffer _b; | 102 | luaL_Buffer _b; |
| 103 | luaL_buffinit(L_, &_b); // L_: ... {} ... &b? | 103 | luaL_buffinit(L_, &_b); // L_: ... {} ... &b? |
| 104 | TableIndex _i{ 1 }; | 104 | TableIndex _i{ 1 }; |
| 105 | for (; _i < last_; ++_i) { | 105 | TableIndex const _last{ static_cast<TableIndex::type>(lua_rawlen(L_, t_)) }; |
| 106 | for (; _i < _last; ++_i) { | ||
| 106 | lua_rawgeti(L_, t_, _i); | 107 | lua_rawgeti(L_, t_, _i); |
| 107 | luaL_addvalue(&_b); | 108 | luaL_addvalue(&_b); |
| 108 | luaL_addlstring(&_b, "/", 1); | 109 | luaL_addlstring(&_b, "/", 1); |
| 109 | } | 110 | } |
| 110 | if (_i == last_) { // add last value (if interval was not empty) | 111 | if (_i == _last) { // add last value (if interval was not empty) |
| 111 | lua_rawgeti(L_, t_, _i); | 112 | lua_rawgeti(L_, t_, _i); |
| 112 | luaL_addvalue(&_b); | 113 | luaL_addvalue(&_b); |
| 113 | } | 114 | } |
| @@ -151,7 +152,7 @@ static void update_lookup_entry(lua_State* const L_, StackIndex const ctxBase_, | |||
| 151 | TableIndex const _deeper{ depth_ + 1 }; | 152 | TableIndex const _deeper{ depth_ + 1 }; |
| 152 | lua_rawseti(L_, _fqn, _deeper); // L_: ... {bfc} k o name? | 153 | lua_rawseti(L_, _fqn, _deeper); // L_: ... {bfc} k o name? |
| 153 | // generate name | 154 | // generate name |
| 154 | std::string_view const _newName{ tools::PushFQN(L_, _fqn, _deeper) }; // L_: ... {bfc} k o name? "f.q.n" | 155 | std::string_view const _newName{ tools::PushFQN(L_, _fqn) }; // L_: ... {bfc} k o name? "f.q.n" |
| 155 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order | 156 | // Lua 5.2 introduced a hash randomizer seed which causes table iteration to yield a different key order |
| 156 | // on different VMs even when the tables are populated the exact same way. | 157 | // on different VMs even when the tables are populated the exact same way. |
| 157 | // Also, when Lua is built with compatibility options (such as LUA_COMPAT_ALL), some base libraries register functions under multiple names. | 158 | // Also, when Lua is built with compatibility options (such as LUA_COMPAT_ALL), some base libraries register functions under multiple names. |
diff --git a/src/tools.hpp b/src/tools.hpp index 7955263..420b5f8 100644 --- a/src/tools.hpp +++ b/src/tools.hpp | |||
| @@ -36,6 +36,6 @@ static constexpr RegistryUniqueKey kLookupRegKey{ 0xBF1FC5CF3C6DD47Bull }; // re | |||
| 36 | namespace tools { | 36 | namespace tools { |
| 37 | void PopulateFuncLookupTable(lua_State* L_, StackIndex i_, std::string_view const& name_); | 37 | void PopulateFuncLookupTable(lua_State* L_, StackIndex i_, std::string_view const& name_); |
| 38 | [[nodiscard]] | 38 | [[nodiscard]] |
| 39 | std::string_view PushFQN(lua_State* L_, StackIndex t_, TableIndex last_); | 39 | std::string_view PushFQN(lua_State* L_, StackIndex t_); |
| 40 | void SerializeRequire(lua_State* L_); | 40 | void SerializeRequire(lua_State* L_); |
| 41 | } // namespace tools | 41 | } // namespace tools |
