]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/cpp_api/s_base.cpp
Fix possible 0 pointer access
[dragonfireclient.git] / src / script / cpp_api / s_base.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 <stdio.h>
21 #include <cstdarg>
22
23 extern "C" {
24 #include "lua.h"
25 #include "lauxlib.h"
26 }
27
28 #include "cpp_api/s_base.h"
29 #include "lua_api/l_object.h"
30 #include "serverobject.h"
31
32 ScriptApiBase::ScriptApiBase() :
33                 m_luastackmutex(),
34 #ifdef LOCK_DEBUG
35                 m_locked(false),
36 #endif
37                 m_luastack(0),
38                 m_server(0),
39                 m_environment(0)
40 {
41
42 }
43
44
45 void ScriptApiBase::realityCheck()
46 {
47         int top = lua_gettop(m_luastack);
48         if(top >= 30){
49                 dstream<<"Stack is over 30:"<<std::endl;
50                 stackDump(dstream);
51                 scriptError("Stack is over 30 (reality check)");
52         }
53 }
54
55 void  ScriptApiBase::scriptError(const char *fmt, ...)
56 {
57         va_list argp;
58         va_start(argp, fmt);
59         char buf[10000];
60         vsnprintf(buf, 10000, fmt, argp);
61         va_end(argp);
62         //errorstream<<"SCRIPT ERROR: "<<buf;
63         throw LuaError(m_luastack, buf);
64 }
65
66 void ScriptApiBase::stackDump(std::ostream &o)
67 {
68   int i;
69   int top = lua_gettop(m_luastack);
70   for (i = 1; i <= top; i++) {  /* repeat for each level */
71         int t = lua_type(m_luastack, i);
72         switch (t) {
73
74           case LUA_TSTRING:  /* strings */
75                 o<<"\""<<lua_tostring(m_luastack, i)<<"\"";
76                 break;
77
78           case LUA_TBOOLEAN:  /* booleans */
79                 o<<(lua_toboolean(m_luastack, i) ? "true" : "false");
80                 break;
81
82           case LUA_TNUMBER:  /* numbers */ {
83                 char buf[10];
84                 snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i));
85                 o<<buf;
86                 break; }
87
88           default:  /* other values */
89                 o<<lua_typename(m_luastack, t);
90                 break;
91
92         }
93         o<<" ";
94   }
95   o<<std::endl;
96 }
97
98 // Push the list of callbacks (a lua table).
99 // Then push nargs arguments.
100 // Then call this function, which
101 // - runs the callbacks
102 // - removes the table and arguments from the lua stack
103 // - pushes the return value, computed depending on mode
104 void ScriptApiBase::runCallbacks(int nargs,RunCallbacksMode mode)
105 {
106         lua_State *L = getStack();
107
108         // Insert the return value into the lua stack, below the table
109         assert(lua_gettop(L) >= nargs + 1);
110         lua_pushnil(L);
111         lua_insert(L, -(nargs + 1) - 1);
112         // Stack now looks like this:
113         // ... <return value = nil> <table> <arg#1> <arg#2> ... <arg#n>
114
115         int rv = lua_gettop(L) - nargs - 1;
116         int table = rv + 1;
117         int arg = table + 1;
118
119         luaL_checktype(L, table, LUA_TTABLE);
120
121         // Foreach
122         lua_pushnil(L);
123         bool first_loop = true;
124         while(lua_next(L, table) != 0){
125                 // key at index -2 and value at index -1
126                 luaL_checktype(L, -1, LUA_TFUNCTION);
127                 // Call function
128                 for(int i = 0; i < nargs; i++)
129                         lua_pushvalue(L, arg+i);
130                 if(lua_pcall(L, nargs, 1, 0))
131                         scriptError("error: %s", lua_tostring(L, -1));
132
133                 // Move return value to designated space in stack
134                 // Or pop it
135                 if(first_loop){
136                         // Result of first callback is always moved
137                         lua_replace(L, rv);
138                         first_loop = false;
139                 } else {
140                         // Otherwise, what happens depends on the mode
141                         if(mode == RUN_CALLBACKS_MODE_FIRST)
142                                 lua_pop(L, 1);
143                         else if(mode == RUN_CALLBACKS_MODE_LAST)
144                                 lua_replace(L, rv);
145                         else if(mode == RUN_CALLBACKS_MODE_AND ||
146                                         mode == RUN_CALLBACKS_MODE_AND_SC){
147                                 if((bool)lua_toboolean(L, rv) == true &&
148                                                 (bool)lua_toboolean(L, -1) == false)
149                                         lua_replace(L, rv);
150                                 else
151                                         lua_pop(L, 1);
152                         }
153                         else if(mode == RUN_CALLBACKS_MODE_OR ||
154                                         mode == RUN_CALLBACKS_MODE_OR_SC){
155                                 if((bool)lua_toboolean(L, rv) == false &&
156                                                 (bool)lua_toboolean(L, -1) == true)
157                                         lua_replace(L, rv);
158                                 else
159                                         lua_pop(L, 1);
160                         }
161                         else
162                                 assert(0);
163                 }
164
165                 // Handle short circuit modes
166                 if(mode == RUN_CALLBACKS_MODE_AND_SC &&
167                                 (bool)lua_toboolean(L, rv) == false)
168                         break;
169                 else if(mode == RUN_CALLBACKS_MODE_OR_SC &&
170                                 (bool)lua_toboolean(L, rv) == true)
171                         break;
172
173                 // value removed, keep key for next iteration
174         }
175
176         // Remove stuff from stack, leaving only the return value
177         lua_settop(L, rv);
178
179         // Fix return value in case no callbacks were called
180         if(first_loop){
181                 if(mode == RUN_CALLBACKS_MODE_AND ||
182                                 mode == RUN_CALLBACKS_MODE_AND_SC){
183                         lua_pop(L, 1);
184                         lua_pushboolean(L, true);
185                 }
186                 else if(mode == RUN_CALLBACKS_MODE_OR ||
187                                 mode == RUN_CALLBACKS_MODE_OR_SC){
188                         lua_pop(L, 1);
189                         lua_pushboolean(L, false);
190                 }
191         }
192 }
193
194 void ScriptApiBase::addObjectReference(ServerActiveObject *cobj)
195 {
196         SCRIPTAPI_PRECHECKHEADER
197         //infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;
198
199         // Create object on stack
200         ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
201         int object = lua_gettop(L);
202
203         // Get minetest.object_refs table
204         lua_getglobal(L, "minetest");
205         lua_getfield(L, -1, "object_refs");
206         luaL_checktype(L, -1, LUA_TTABLE);
207         int objectstable = lua_gettop(L);
208
209         // object_refs[id] = object
210         lua_pushnumber(L, cobj->getId()); // Push id
211         lua_pushvalue(L, object); // Copy object to top of stack
212         lua_settable(L, objectstable);
213 }
214
215 void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj)
216 {
217         SCRIPTAPI_PRECHECKHEADER
218         //infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;
219
220         // Get minetest.object_refs table
221         lua_getglobal(L, "minetest");
222         lua_getfield(L, -1, "object_refs");
223         luaL_checktype(L, -1, LUA_TTABLE);
224         int objectstable = lua_gettop(L);
225
226         // Get object_refs[id]
227         lua_pushnumber(L, cobj->getId()); // Push id
228         lua_gettable(L, objectstable);
229         // Set object reference to NULL
230         ObjectRef::set_null(L);
231         lua_pop(L, 1); // pop object
232
233         // Set object_refs[id] = nil
234         lua_pushnumber(L, cobj->getId()); // Push id
235         lua_pushnil(L);
236         lua_settable(L, objectstable);
237 }
238
239 // Creates a new anonymous reference if cobj=NULL or id=0
240 void ScriptApiBase::objectrefGetOrCreate(
241                 ServerActiveObject *cobj)
242 {
243         lua_State *L = getStack();
244
245         if(cobj == NULL || cobj->getId() == 0){
246                 ObjectRef::create(L, cobj);
247         } else {
248                 objectrefGet(cobj->getId());
249         }
250 }
251
252 void ScriptApiBase::objectrefGet(u16 id)
253 {
254         lua_State *L = getStack();
255
256         // Get minetest.object_refs[i]
257         lua_getglobal(L, "minetest");
258         lua_getfield(L, -1, "object_refs");
259         luaL_checktype(L, -1, LUA_TTABLE);
260         lua_pushnumber(L, id);
261         lua_gettable(L, -2);
262         lua_remove(L, -2); // object_refs
263         lua_remove(L, -2); // minetest
264 }