summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/lua_function.cpp57
-rw-r--r--indra/llcommon/lua_function.h13
-rw-r--r--indra/newview/llluamanager.cpp6
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);