diff options
-rw-r--r-- | indra/llcommon/lua_function.cpp | 57 | ||||
-rw-r--r-- | indra/llcommon/lua_function.h | 13 | ||||
-rw-r--r-- | indra/newview/llluamanager.cpp | 6 |
3 files changed, 47 insertions, 29 deletions
diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp index b173d17ede..defadea358 100644 --- a/indra/llcommon/lua_function.cpp +++ b/indra/llcommon/lua_function.cpp @@ -127,7 +127,7 @@ std::string lua_tostdstring(lua_State* L, int index) void lua_pushstdstring(lua_State* L, const std::string& str) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_pushlstring(L, str.c_str(), str.length()); } @@ -240,10 +240,10 @@ LLSD lua_tollsd(lua_State* L, int index) // undefined LLSD. Naturally, though, those won't survive a second // round trip. - // This is the most important of the luaL_checkstack() calls because a + // This is the most important of the lluau_checkstack() calls because a // deeply nested Lua structure will enter this case at each level, and // we'll need another 2 stack slots to traverse each nested table. - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); // BEFORE we push nil to initialize the lua_next() traversal, convert // 'index' to absolute! Our caller might have passed a relative index; // we do, below: lua_tollsd(L, -1). If 'index' is -1, then when we @@ -400,7 +400,7 @@ LLSD lua_tollsd(lua_State* L, int index) void lua_pushllsd(lua_State* L, const LLSD& data) { // might need 2 slots for array or map - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); switch (data.type()) { case LLSD::TypeUndefined: @@ -487,39 +487,54 @@ LuaState::LuaState(script_finished_fn cb): LuaState::~LuaState() { - // We're just about to destroy this lua_State mState. lua_close() doesn't - // implicitly garbage-collect everything, so (for instance) any lingering - // objects with __gc metadata methods aren't cleaned up. This is why we - // provide atexit(). - luaL_checkstack(mState, 2, nullptr); + // We're just about to destroy this lua_State mState. Did this Lua chunk + // register any atexit() functions? + lluau_checkstack(mState, 3); // look up Registry.atexit lua_getfield(mState, LUA_REGISTRYINDEX, "atexit"); // stack contains Registry.atexit if (lua_istable(mState, -1)) { + // Push debug.traceback() onto the stack as lua_pcall()'s error + // handler function. On error, lua_pcall() calls the specified error + // handler function with the original error message; the message + // returned by the error handler is then returned by lua_pcall(). + // Luau's debug.traceback() is called with a message to prepend to the + // returned traceback string. Almost as if they'd been designed to + // work together... + lua_getglobal(mState, "debug"); + lua_getfield(mState, -1, "traceback"); + // ditch "debug" + lua_remove(mState, -2); + // stack now contains atexit, debug.traceback() + // We happen to know that Registry.atexit is built by appending array // entries using table.insert(). That's important because it means // there are no holes, and therefore lua_objlen() should be correct. // That's important because we walk the atexit table backwards, to // destroy last the things we created (passed to LL.atexit()) first. - for (int i(lua_objlen(mState, -1)); i >= 1; --i) + for (int i(lua_objlen(mState, -2)); i >= 1; --i) { lua_pushinteger(mState, i); - // stack contains Registry.atexit, i - lua_gettable(mState, -2); - // stack contains Registry.atexit, atexit[i] + // stack contains Registry.atexit, debug.traceback(), i + lua_gettable(mState, -3); + // stack contains Registry.atexit, debug.traceback(), atexit[i] // Call atexit[i](), no args, no return values. // Use lua_pcall() because errors in any one atexit() function - // shouldn't cancel the rest of them. - if (lua_pcall(mState, 0, 0, 0) != LUA_OK) + // shouldn't cancel the rest of them. Pass debug.traceback() as + // the error handler function. + if (lua_pcall(mState, 0, 0, -2) != LUA_OK) { auto error{ lua_tostdstring(mState, -1) }; LL_WARNS("Lua") << "atexit() function error: " << error << LL_ENDL; // pop error message lua_pop(mState, 1); } - // lua_pcall() has already popped atexit[i]: stack contains atexit + // lua_pcall() has already popped atexit[i]: + // stack contains atexit, debug.traceback() } + // pop debug.traceback() + lua_pop(mState, 1); } // pop Registry.atexit (either table or nil) lua_pop(mState, 1); @@ -625,7 +640,7 @@ std::pair<int, LLSD> LuaState::expr(const std::string& desc, const std::string& LuaListener& LuaState::obtainListener(lua_State* L) { - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); lua_getfield(L, LUA_REGISTRYINDEX, "LuaListener"); // compare lua_type() because lua_isuserdata() also accepts light userdata if (lua_type(L, -1) != LUA_TUSERDATA) @@ -655,7 +670,7 @@ LuaListener& LuaState::obtainListener(lua_State* L) lua_function(atexit, "atexit(function): " "register Lua function to be called at script termination") { - luaL_checkstack(L, 4, nullptr); + lluau_checkstack(L, 4); // look up the global name "table" lua_getglobal(L, "table"); // stack contains function, table @@ -705,7 +720,7 @@ LuaFunction::LuaFunction(const std::string_view& name, lua_CFunction function, void LuaFunction::init(lua_State* L) { const auto& [registry, lookup] = getRState(); - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); // create LL table -- // it happens that we know exactly how many non-array members we want lua_createtable(L, 0, int(narrow(lookup.size()))); @@ -743,7 +758,7 @@ std::pair<LuaFunction::Registry&, LuaFunction::Lookup&> LuaFunction::getState() *****************************************************************************/ lua_function(source_path, "source_path(): return the source path of the running Lua script") { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_pushstdstring(L, lluau::source_path(L).u8string()); return 1; } @@ -753,7 +768,7 @@ lua_function(source_path, "source_path(): return the source path of the running *****************************************************************************/ lua_function(source_dir, "source_dir(): return the source directory of the running Lua script") { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_pushstdstring(L, lluau::source_path(L).parent_path().u8string()); return 1; } diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h index 7b59af30f5..c1a4d736a0 100644 --- a/indra/llcommon/lua_function.h +++ b/indra/llcommon/lua_function.h @@ -65,6 +65,9 @@ namespace lluau void check_interrupts_counter(lua_State* L); } // namespace lluau +// must be a macro because __FUNCTION__ is context-sensitive +#define lluau_checkstack(L, n) luaL_checkstack((L), (n), __FUNCTION__) + std::string lua_tostdstring(lua_State* L, int index); void lua_pushstdstring(lua_State* L, const std::string& str); LLSD lua_tollsd(lua_State* L, int index); @@ -294,7 +297,7 @@ auto lua_to<void*>(lua_State* L, int index) template <typename T> auto lua_getfieldv(lua_State* L, int index, const char* k) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_getfield(L, index, k); LuaPopper pop(L, 1); return lua_to<T>(L, -1); @@ -304,7 +307,7 @@ auto lua_getfieldv(lua_State* L, int index, const char* k) template <typename T> auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_push(L, value); lua_setfield(L, index, k); } @@ -313,7 +316,7 @@ auto lua_setfieldv(lua_State* L, int index, const char* k, const T& value) template <typename T> auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); lua_pushlstring(L, k.data(), k.length()); lua_rawget(L, index); LuaPopper pop(L, 1); @@ -324,7 +327,7 @@ auto lua_rawgetfield(lua_State* L, int index, const std::string_view& k) template <typename T> void lua_rawsetfield(lua_State* L, int index, const std::string_view& k, const T& value) { - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); lua_pushlstring(L, k.data(), k.length()); lua_push(L, value); lua_rawset(L, index); @@ -430,7 +433,7 @@ DistinctInt TypeTag<T>::value; template <class T, typename... ARGS> void lua_emplace(lua_State* L, ARGS&&... args) { - luaL_checkstack(L, 1, nullptr); + lluau_checkstack(L, 1); int tag{ TypeTag<T>::value }; if (! lua_getuserdatadtor(L, tag)) { diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp index 7014c59e4e..0b1a73f4e9 100644 --- a/indra/newview/llluamanager.cpp +++ b/indra/newview/llluamanager.cpp @@ -66,7 +66,7 @@ std::string lua_print_msg(lua_State* L, const std::string_view& level) { // On top of existing Lua arguments, we're going to push tostring() and // duplicate each existing stack entry so we can stringize each one. - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); luaL_where(L, 1); // start with the 'where' info at the top of the stack std::ostringstream out; @@ -139,7 +139,7 @@ lua_function(get_event_pumps, "Events posted to replypump are queued for get_event_next().\n" "post_on(commandpump, ...) to engage LLEventAPI operations (see helpleap()).") { - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); auto& listener{ LuaState::obtainListener(L) }; // return the reply pump name and the command pump name on caller's lua_State lua_pushstdstring(L, listener.getReplyName()); @@ -153,7 +153,7 @@ lua_function(get_event_next, "is returned by get_event_pumps(). Blocks the calling chunk until an\n" "event becomes available.") { - luaL_checkstack(L, 2, nullptr); + lluau_checkstack(L, 2); auto& listener{ LuaState::obtainListener(L) }; const auto& [pump, data]{ listener.getNext() }; lua_pushstdstring(L, pump); |