]> git.lizzy.rs Git - minetest.git/blob - src/script/common/c_internal.cpp
f22e9b0ff948782a4a909d8ae65217333ad75ba1
[minetest.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
23 std::string script_get_backtrace(lua_State *L)
24 {
25         std::string s;
26         lua_getglobal(L, "debug");
27         if(lua_istable(L, -1)){
28                 lua_getfield(L, -1, "traceback");
29                 if(lua_isfunction(L, -1)) {
30                         lua_call(L, 0, 1);
31                         if(lua_isstring(L, -1)){
32                                 s = lua_tostring(L, -1);
33                         }
34                 }
35                 lua_pop(L, 1);
36         }
37         lua_pop(L, 1);
38         return s;
39 }
40
41 int script_error_handler(lua_State *L) {
42         lua_getglobal(L, "debug");
43         if (!lua_istable(L, -1)) {
44                 lua_pop(L, 1);
45                 return 1;
46         }
47         lua_getfield(L, -1, "traceback");
48         if (!lua_isfunction(L, -1)) {
49                 lua_pop(L, 2);
50                 return 1;
51         }
52         lua_pushvalue(L, 1);
53         lua_pushinteger(L, 2);
54         lua_call(L, 2, 1);
55         return 1;
56 }
57
58 void script_error(lua_State *L)
59 {
60         const char *s = lua_tostring(L, -1);
61         std::string str(s ? s : "");
62         throw LuaError(NULL, str);
63 }
64
65 // Push the list of callbacks (a lua table).
66 // Then push nargs arguments.
67 // Then call this function, which
68 // - runs the callbacks
69 // - removes the table and arguments from the lua stack
70 // - pushes the return value, computed depending on mode
71 void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode)
72 {
73         // Insert the return value into the lua stack, below the table
74         assert(lua_gettop(L) >= nargs + 1);
75
76         lua_pushnil(L);
77         int rv = lua_gettop(L) - nargs - 1;
78         lua_insert(L, rv);
79
80         // Insert error handler after return value
81         lua_pushcfunction(L, script_error_handler);
82         int errorhandler = rv + 1;
83         lua_insert(L, errorhandler);
84
85         // Stack now looks like this:
86         // ... <return value = nil> <error handler> <table> <arg#1> <arg#2> ... <arg#n>
87
88         int table = errorhandler + 1;
89         int arg = table + 1;
90
91         luaL_checktype(L, table, LUA_TTABLE);
92
93         // Foreach
94         lua_pushnil(L);
95         bool first_loop = true;
96         while(lua_next(L, table) != 0){
97                 // key at index -2 and value at index -1
98                 luaL_checktype(L, -1, LUA_TFUNCTION);
99                 // Call function
100                 for(int i = 0; i < nargs; i++)
101                         lua_pushvalue(L, arg+i);
102                 if(lua_pcall(L, nargs, 1, errorhandler))
103                         script_error(L);
104
105                 // Move return value to designated space in stack
106                 // Or pop it
107                 if(first_loop){
108                         // Result of first callback is always moved
109                         lua_replace(L, rv);
110                         first_loop = false;
111                 } else {
112                         // Otherwise, what happens depends on the mode
113                         if(mode == RUN_CALLBACKS_MODE_FIRST)
114                                 lua_pop(L, 1);
115                         else if(mode == RUN_CALLBACKS_MODE_LAST)
116                                 lua_replace(L, rv);
117                         else if(mode == RUN_CALLBACKS_MODE_AND ||
118                                         mode == RUN_CALLBACKS_MODE_AND_SC){
119                                 if((bool)lua_toboolean(L, rv) == true &&
120                                                 (bool)lua_toboolean(L, -1) == false)
121                                         lua_replace(L, rv);
122                                 else
123                                         lua_pop(L, 1);
124                         }
125                         else if(mode == RUN_CALLBACKS_MODE_OR ||
126                                         mode == RUN_CALLBACKS_MODE_OR_SC){
127                                 if((bool)lua_toboolean(L, rv) == false &&
128                                                 (bool)lua_toboolean(L, -1) == true)
129                                         lua_replace(L, rv);
130                                 else
131                                         lua_pop(L, 1);
132                         }
133                         else
134                                 assert(0);
135                 }
136
137                 // Handle short circuit modes
138                 if(mode == RUN_CALLBACKS_MODE_AND_SC &&
139                                 (bool)lua_toboolean(L, rv) == false)
140                         break;
141                 else if(mode == RUN_CALLBACKS_MODE_OR_SC &&
142                                 (bool)lua_toboolean(L, rv) == true)
143                         break;
144
145                 // value removed, keep key for next iteration
146         }
147
148         // Remove stuff from stack, leaving only the return value
149         lua_settop(L, rv);
150
151         // Fix return value in case no callbacks were called
152         if(first_loop){
153                 if(mode == RUN_CALLBACKS_MODE_AND ||
154                                 mode == RUN_CALLBACKS_MODE_AND_SC){
155                         lua_pop(L, 1);
156                         lua_pushboolean(L, true);
157                 }
158                 else if(mode == RUN_CALLBACKS_MODE_OR ||
159                                 mode == RUN_CALLBACKS_MODE_OR_SC){
160                         lua_pop(L, 1);
161                         lua_pushboolean(L, false);
162                 }
163         }
164 }
165
166