Use a Lua error handler that calls tostring (#11913)

This commit is contained in:
Jude Melton-Houghton 2022-12-15 07:37:49 -05:00 committed by GitHub
parent 1f3b5e553b
commit 0fc97a1483
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 45 additions and 9 deletions

@ -6,6 +6,9 @@
--
-- Initialize some very basic things
function core.error_handler(err, level)
return debug.traceback(tostring(err), level)
end
do
local function concat_args(...)
local n, t = select("#", ...), {...}

@ -9956,3 +9956,17 @@ Bit Library
Functions: bit.tobit, bit.tohex, bit.bnot, bit.band, bit.bor, bit.bxor, bit.lshift, bit.rshift, bit.arshift, bit.rol, bit.ror, bit.bswap
See http://bitop.luajit.org/ for advanced information.
Error Handling
--------------
When an error occurs that is not caught, Minetest calls the function
`minetest.error_handler` with the error object as its first argument. The second
argument is the stack level where the error occurred. The return value is the
error string that should be shown. By default this is a backtrace from
`debug.traceback`. If the error object is not a string, it is first converted
with `tostring` before being displayed. This means that you can use tables as
error objects so long as you give them `__tostring` metamethods.
You can override `minetest.error_handler`. You should call the previous handler
with the correct stack level in your implementation.

@ -27,10 +27,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
std::string script_get_backtrace(lua_State *L)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE);
lua_getglobal(L, "debug");
lua_getfield(L, -1, "traceback");
lua_call(L, 0, 1);
std::string result = luaL_checkstring(L, -1);
lua_pop(L, 1);
lua_pop(L, 2);
return result;
}
@ -46,6 +47,25 @@ int script_exception_wrapper(lua_State *L, lua_CFunction f)
return lua_error(L); // Rethrow as a Lua error.
}
int script_error_handler(lua_State *L)
{
lua_getglobal(L, "core");
lua_getfield(L, -1, "error_handler");
if (!lua_isnil(L, -1)) {
lua_pushvalue(L, 1);
} else {
// No Lua error handler available. Call debug.traceback(tostring(#1), level).
lua_getglobal(L, "debug");
lua_getfield(L, -1, "traceback");
lua_getglobal(L, "tostring");
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
}
lua_pushinteger(L, 2); // Stack level
lua_call(L, 2, 1);
return 1;
}
/*
* Note that we can't get tracebacks for LUA_ERRMEM or LUA_ERRERR (without
* hacking Lua internals). For LUA_ERRMEM, this is because memory errors will

@ -54,7 +54,7 @@ enum {
CUSTOM_RIDX_SCRIPTAPI,
CUSTOM_RIDX_GLOBALS_BACKUP,
CUSTOM_RIDX_CURRENT_MOD_NAME,
CUSTOM_RIDX_BACKTRACE,
CUSTOM_RIDX_ERROR_HANDLER,
CUSTOM_RIDX_HTTP_API_LUA,
CUSTOM_RIDX_METATABLE_MAP,
@ -78,7 +78,7 @@ enum {
// Pushes the error handler onto the stack and returns its index
#define PUSH_ERROR_HANDLER(L) \
(lua_rawgeti((L), LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE), lua_gettop((L)))
(lua_rawgeti((L), LUA_REGISTRYINDEX, CUSTOM_RIDX_ERROR_HANDLER), lua_gettop((L)))
#define PCALL_RESL(L, RES) { \
int result_ = (RES); \
@ -121,6 +121,8 @@ enum RunCallbacksMode
std::string script_get_backtrace(lua_State *L);
// Wrapper for CFunction calls that converts C++ exceptions to Lua errors
int script_exception_wrapper(lua_State *L, lua_CFunction f);
// Acts as the error handler for lua_pcall
int script_error_handler(lua_State *L);
// Takes an error from lua_pcall and throws it as a LuaError
void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn);

@ -103,11 +103,8 @@ ScriptApiBase::ScriptApiBase(ScriptingType type):
#endif
lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
// Add and save an error handler
lua_getglobal(m_luastack, "debug");
lua_getfield(m_luastack, -1, "traceback");
lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE);
lua_pop(m_luastack, 1); // pop debug
lua_pushcfunction(m_luastack, script_error_handler);
lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_ERROR_HANDLER);
// Add a C++ wrapper function to catch exceptions thrown in Lua -> C++ calls
#if USE_LUAJIT