]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/common/c_internal.cpp
Add spider
[dragonfireclient.git] / src / script / common / c_internal.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "common/c_internal.h"
21 #include "util/numeric.h"
22 #include "debug.h"
23 #include "log.h"
24 #include "porting.h"
25 #include "settings.h"
26 #include <algorithm> // std::find
27
28 std::string script_get_backtrace(lua_State *L)
29 {
30         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE);
31         lua_call(L, 0, 1);
32         std::string result = luaL_checkstring(L, -1);
33         lua_pop(L, 1);
34         return result;
35 }
36
37 int script_exception_wrapper(lua_State *L, lua_CFunction f)
38 {
39         try {
40                 return f(L);  // Call wrapped function and return result.
41         } catch (const char *s) {  // Catch and convert exceptions.
42                 lua_pushstring(L, s);
43         } catch (std::exception &e) {
44                 lua_pushstring(L, e.what());
45         }
46         return lua_error(L);  // Rethrow as a Lua error.
47 }
48
49 /*
50  * Note that we can't get tracebacks for LUA_ERRMEM or LUA_ERRERR (without
51  * hacking Lua internals).  For LUA_ERRMEM, this is because memory errors will
52  * not execute the error handler, and by the time lua_pcall returns the
53  * execution stack will have already been unwound.  For LUA_ERRERR, there was
54  * another error while trying to generate a backtrace from a LUA_ERRRUN.  It is
55  * presumed there is an error with the internal Lua state and thus not possible
56  * to gather a coherent backtrace.  Realistically, the best we can do here is
57  * print which C function performed the failing pcall.
58  */
59 void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn)
60 {
61         if (pcall_result == 0)
62                 return;
63
64         const char *err_type;
65         switch (pcall_result) {
66         case LUA_ERRRUN:
67                 err_type = "Runtime";
68                 break;
69         case LUA_ERRMEM:
70                 err_type = "OOM";
71                 break;
72         case LUA_ERRERR:
73                 err_type = "Double fault";
74                 break;
75         default:
76                 err_type = "Unknown";
77         }
78
79         if (!mod)
80                 mod = "??";
81
82         if (!fxn)
83                 fxn = "??";
84
85         const char *err_descr = lua_tostring(L, -1);
86         if (!err_descr)
87                 err_descr = "<no description>";
88
89         char buf[256];
90         porting::mt_snprintf(buf, sizeof(buf), "%s error from mod '%s' in callback %s(): ",
91                 err_type, mod, fxn);
92
93         std::string err_msg(buf);
94         err_msg += err_descr;
95
96         if (pcall_result == LUA_ERRMEM) {
97                 err_msg += "\nCurrent Lua memory usage: "
98                         + itos(lua_gc(L, LUA_GCCOUNT, 0) >> 10) + " MB";
99         }
100
101         throw LuaError(err_msg);
102 }
103
104 static void script_log_add_source(lua_State *L, std::string &message, int stack_depth)
105 {
106         lua_Debug ar;
107
108         if (lua_getstack(L, stack_depth, &ar)) {
109                 FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed");
110                 message.append(" (at " + std::string(ar.short_src) + ":"
111                         + std::to_string(ar.currentline) + ")");
112         } else {
113                 message.append(" (at ?:?)");
114         }
115 }
116
117 bool script_log_unique(lua_State *L, std::string message, std::ostream &log_to,
118         int stack_depth)
119 {
120         thread_local std::vector<u64> logged_messages;
121
122         script_log_add_source(L, message, stack_depth);
123         u64 hash = murmur_hash_64_ua(message.data(), message.length(), 0xBADBABE);
124
125         if (std::find(logged_messages.begin(), logged_messages.end(), hash)
126                         == logged_messages.end()) {
127
128                 logged_messages.emplace_back(hash);
129                 log_to << message << std::endl;
130                 return true;
131         }
132         return false;
133 }
134
135 DeprecatedHandlingMode get_deprecated_handling_mode()
136 {
137         static thread_local bool configured = false;
138         static thread_local DeprecatedHandlingMode ret = DeprecatedHandlingMode::Ignore;
139
140         // Only read settings on first call
141         if (!configured) {
142                 std::string value = g_settings->get("deprecated_lua_api_handling");
143                 if (value == "log") {
144                         ret = DeprecatedHandlingMode::Log;
145                 } else if (value == "error") {
146                         ret = DeprecatedHandlingMode::Error;
147                 }
148                 configured = true;
149         }
150
151         return ret;
152 }
153
154 void log_deprecated(lua_State *L, std::string message, int stack_depth)
155 {
156         DeprecatedHandlingMode mode = get_deprecated_handling_mode();
157         if (mode == DeprecatedHandlingMode::Ignore)
158                 return;
159
160         script_log_add_source(L, message, stack_depth);
161         warningstream << message << std::endl;
162
163         if (mode == DeprecatedHandlingMode::Error)
164                 script_error(L, LUA_ERRRUN, NULL, NULL);
165         else
166                 infostream << script_get_backtrace(L) << std::endl;
167 }
168