X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fscript%2Fcommon%2Fc_internal.cpp;h=df82dba146ec7ddcb5d0becc7446b743e8c71e17;hb=391eec9ee78fc9dfdc476ad2a8ed7755009e4a2f;hp=f22e9b0ff948782a4a909d8ae65217333ad75ba1;hpb=5323d800cc67814938fb668730d474011631fab5;p=minetest.git diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index f22e9b0ff..df82dba14 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -18,149 +18,151 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "common/c_internal.h" +#include "util/numeric.h" #include "debug.h" +#include "log.h" +#include "porting.h" +#include "settings.h" +#include // std::find std::string script_get_backtrace(lua_State *L) { - std::string s; - lua_getglobal(L, "debug"); - if(lua_istable(L, -1)){ - lua_getfield(L, -1, "traceback"); - if(lua_isfunction(L, -1)) { - lua_call(L, 0, 1); - if(lua_isstring(L, -1)){ - s = lua_tostring(L, -1); - } - } - lua_pop(L, 1); - } + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE); + lua_call(L, 0, 1); + std::string result = luaL_checkstring(L, -1); lua_pop(L, 1); - return s; + return result; } -int script_error_handler(lua_State *L) { - lua_getglobal(L, "debug"); - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - return 1; +int script_exception_wrapper(lua_State *L, lua_CFunction f) +{ + try { + return f(L); // Call wrapped function and return result. + } catch (const char *s) { // Catch and convert exceptions. + lua_pushstring(L, s); + } catch (std::exception &e) { + lua_pushstring(L, e.what()); + } + return lua_error(L); // Rethrow as a Lua error. +} + +/* + * 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 + * not execute the error handler, and by the time lua_pcall returns the + * execution stack will have already been unwound. For LUA_ERRERR, there was + * another error while trying to generate a backtrace from a LUA_ERRRUN. It is + * presumed there is an error with the internal Lua state and thus not possible + * to gather a coherent backtrace. Realistically, the best we can do here is + * print which C function performed the failing pcall. + */ +void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn) +{ + if (pcall_result == 0) + return; + + const char *err_type; + switch (pcall_result) { + case LUA_ERRRUN: + err_type = "Runtime"; + break; + case LUA_ERRMEM: + err_type = "OOM"; + break; + case LUA_ERRERR: + err_type = "Double fault"; + break; + default: + err_type = "Unknown"; } - lua_getfield(L, -1, "traceback"); - if (!lua_isfunction(L, -1)) { - lua_pop(L, 2); - return 1; + + if (!mod) + mod = "??"; + + if (!fxn) + fxn = "??"; + + const char *err_descr = lua_tostring(L, -1); + if (!err_descr) + err_descr = ""; + + char buf[256]; + porting::mt_snprintf(buf, sizeof(buf), "%s error from mod '%s' in callback %s(): ", + err_type, mod, fxn); + + std::string err_msg(buf); + err_msg += err_descr; + + if (pcall_result == LUA_ERRMEM) { + err_msg += "\nCurrent Lua memory usage: " + + itos(lua_gc(L, LUA_GCCOUNT, 0) >> 10) + " MB"; } - lua_pushvalue(L, 1); - lua_pushinteger(L, 2); - lua_call(L, 2, 1); - return 1; + + throw LuaError(err_msg); } -void script_error(lua_State *L) +static void script_log_add_source(lua_State *L, std::string &message, int stack_depth) { - const char *s = lua_tostring(L, -1); - std::string str(s ? s : ""); - throw LuaError(NULL, str); + lua_Debug ar; + + if (lua_getstack(L, stack_depth, &ar)) { + FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed"); + message.append(" (at " + std::string(ar.short_src) + ":" + + std::to_string(ar.currentline) + ")"); + } else { + message.append(" (at ?:?)"); + } } -// Push the list of callbacks (a lua table). -// Then push nargs arguments. -// Then call this function, which -// - runs the callbacks -// - removes the table and arguments from the lua stack -// - pushes the return value, computed depending on mode -void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode) +bool script_log_unique(lua_State *L, std::string message, std::ostream &log_to, + int stack_depth) { - // Insert the return value into the lua stack, below the table - assert(lua_gettop(L) >= nargs + 1); - - lua_pushnil(L); - int rv = lua_gettop(L) - nargs - 1; - lua_insert(L, rv); - - // Insert error handler after return value - lua_pushcfunction(L, script_error_handler); - int errorhandler = rv + 1; - lua_insert(L, errorhandler); - - // Stack now looks like this: - // ... ... - - int table = errorhandler + 1; - int arg = table + 1; - - luaL_checktype(L, table, LUA_TTABLE); - - // Foreach - lua_pushnil(L); - bool first_loop = true; - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - for(int i = 0; i < nargs; i++) - lua_pushvalue(L, arg+i); - if(lua_pcall(L, nargs, 1, errorhandler)) - script_error(L); - - // Move return value to designated space in stack - // Or pop it - if(first_loop){ - // Result of first callback is always moved - lua_replace(L, rv); - first_loop = false; - } else { - // Otherwise, what happens depends on the mode - if(mode == RUN_CALLBACKS_MODE_FIRST) - lua_pop(L, 1); - else if(mode == RUN_CALLBACKS_MODE_LAST) - lua_replace(L, rv); - else if(mode == RUN_CALLBACKS_MODE_AND || - mode == RUN_CALLBACKS_MODE_AND_SC){ - if((bool)lua_toboolean(L, rv) == true && - (bool)lua_toboolean(L, -1) == false) - lua_replace(L, rv); - else - lua_pop(L, 1); - } - else if(mode == RUN_CALLBACKS_MODE_OR || - mode == RUN_CALLBACKS_MODE_OR_SC){ - if((bool)lua_toboolean(L, rv) == false && - (bool)lua_toboolean(L, -1) == true) - lua_replace(L, rv); - else - lua_pop(L, 1); - } - else - assert(0); - } + thread_local std::vector logged_messages; - // Handle short circuit modes - if(mode == RUN_CALLBACKS_MODE_AND_SC && - (bool)lua_toboolean(L, rv) == false) - break; - else if(mode == RUN_CALLBACKS_MODE_OR_SC && - (bool)lua_toboolean(L, rv) == true) - break; + script_log_add_source(L, message, stack_depth); + u64 hash = murmur_hash_64_ua(message.data(), message.length(), 0xBADBABE); - // value removed, keep key for next iteration - } + if (std::find(logged_messages.begin(), logged_messages.end(), hash) + == logged_messages.end()) { - // Remove stuff from stack, leaving only the return value - lua_settop(L, rv); + logged_messages.emplace_back(hash); + log_to << message << std::endl; + return true; + } + return false; +} - // Fix return value in case no callbacks were called - if(first_loop){ - if(mode == RUN_CALLBACKS_MODE_AND || - mode == RUN_CALLBACKS_MODE_AND_SC){ - lua_pop(L, 1); - lua_pushboolean(L, true); - } - else if(mode == RUN_CALLBACKS_MODE_OR || - mode == RUN_CALLBACKS_MODE_OR_SC){ - lua_pop(L, 1); - lua_pushboolean(L, false); +DeprecatedHandlingMode get_deprecated_handling_mode() +{ + static thread_local bool configured = false; + static thread_local DeprecatedHandlingMode ret = DeprecatedHandlingMode::Ignore; + + // Only read settings on first call + if (!configured) { + std::string value = g_settings->get("deprecated_lua_api_handling"); + if (value == "log") { + ret = DeprecatedHandlingMode::Log; + } else if (value == "error") { + ret = DeprecatedHandlingMode::Error; } + configured = true; } + + return ret; } +void log_deprecated(lua_State *L, std::string message, int stack_depth) +{ + DeprecatedHandlingMode mode = get_deprecated_handling_mode(); + if (mode == DeprecatedHandlingMode::Ignore) + return; + + script_log_add_source(L, message, stack_depth); + warningstream << message << std::endl; + + if (mode == DeprecatedHandlingMode::Error) + script_error(L, LUA_ERRRUN, NULL, NULL); + else + infostream << script_get_backtrace(L) << std::endl; +}