Browse Source

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

Jude Melton-Houghton 1 year ago
parent
commit
0fc97a1483

+ 3 - 0
builtin/init.lua

@@ -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("#", ...), {...}

+ 14 - 0
doc/lua_api.txt

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

+ 22 - 2
src/script/common/c_internal.cpp

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

+ 4 - 2
src/script/common/c_internal.h

@@ -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);
 

+ 2 - 5
src/script/cpp_api/s_base.cpp

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