]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/common/c_internal.cpp
f811dd5d31b545aaf73daed57d5be2ab5d0ec46a
[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 "debug.h"
22 #include "log.h"
23 #include "main.h"
24 #include "settings.h"
25
26 std::string script_get_backtrace(lua_State *L)
27 {
28         std::string s;
29         lua_getglobal(L, "debug");
30         if(lua_istable(L, -1)){
31                 lua_getfield(L, -1, "traceback");
32                 if(lua_isfunction(L, -1)) {
33                         lua_call(L, 0, 1);
34                         if(lua_isstring(L, -1)){
35                                 s = lua_tostring(L, -1);
36                         }
37                 }
38                 lua_pop(L, 1);
39         }
40         lua_pop(L, 1);
41         return s;
42 }
43
44 int script_error_handler(lua_State *L) {
45         lua_getglobal(L, "debug");
46         if (!lua_istable(L, -1)) {
47                 lua_pop(L, 1);
48                 return 1;
49         }
50         lua_getfield(L, -1, "traceback");
51         if (!lua_isfunction(L, -1)) {
52                 lua_pop(L, 2);
53                 return 1;
54         }
55         lua_pushvalue(L, 1);
56         lua_pushinteger(L, 2);
57         lua_call(L, 2, 1);
58         return 1;
59 }
60
61 int script_exception_wrapper(lua_State *L, lua_CFunction f)
62 {
63         try {
64                 return f(L);  // Call wrapped function and return result.
65         } catch (const char *s) {  // Catch and convert exceptions.
66                 lua_pushstring(L, s);
67         } catch (std::exception& e) {
68                 lua_pushstring(L, e.what());
69         } catch (...) {
70                 lua_pushliteral(L, "caught (...)");
71         }
72         return lua_error(L);  // Rethrow as a Lua error.
73 }
74
75 void script_error(lua_State *L)
76 {
77         const char *s = lua_tostring(L, -1);
78         std::string str(s ? s : "");
79         throw LuaError(str);
80 }
81
82 // Push the list of callbacks (a lua table).
83 // Then push nargs arguments.
84 // Then call this function, which
85 // - runs the callbacks
86 // - replaces the table and arguments with the return value,
87 //     computed depending on mode
88 void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode)
89 {
90         assert(lua_gettop(L) >= nargs + 1);
91
92         // Insert error handler
93         lua_pushcfunction(L, script_error_handler);
94         int errorhandler = lua_gettop(L) - nargs - 1;
95         lua_insert(L, errorhandler);
96
97         // Insert run_callbacks between error handler and table
98         lua_getglobal(L, "core");
99         lua_getfield(L, -1, "run_callbacks");
100         lua_remove(L, -2);
101         lua_insert(L, errorhandler + 1);
102
103         // Insert mode after table
104         lua_pushnumber(L, (int) mode);
105         lua_insert(L, errorhandler + 3);
106
107         // Stack now looks like this:
108         // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
109
110         if (lua_pcall(L, nargs + 2, 1, errorhandler)) {
111                 script_error(L);
112         }
113
114         lua_remove(L, -2); // Remove error handler
115 }
116
117 void log_deprecated(lua_State *L, std::string message)
118 {
119         static bool configured = false;
120         static bool dolog      = false;
121         static bool doerror    = false;
122
123         // performance optimization to not have to read and compare setting for every logline
124         if (!configured) {
125                 std::string value = g_settings->get("deprecated_lua_api_handling");
126                 if (value == "log") {
127                         dolog = true;
128                 }
129                 if (value == "error") {
130                         dolog = true;
131                         doerror = true;
132                 }
133         }
134
135         if (doerror) {
136                 if (L != NULL) {
137                         script_error(L);
138                 } else {
139                         /* As of april 2014 assert is not optimized to nop in release builds
140                          * therefore this is correct. */
141                         assert("Can't do a scripterror for this deprecated message, so exit completely!");
142                 }
143         }
144
145         if (dolog) {
146                 /* abusing actionstream because of lack of file-only-logged loglevel */
147                 actionstream << message << std::endl;
148                 if (L != NULL) {
149                         actionstream << script_get_backtrace(L) << std::endl;
150                 }
151         }
152 }
153