|
1 /* |
|
2 ** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $ |
|
3 ** Interface from Lua to its debug API |
|
4 ** See Copyright Notice in lua.h |
|
5 */ |
|
6 |
|
7 |
|
8 #include <stdio.h> |
|
9 #include <stdlib.h> |
|
10 #include <string.h> |
|
11 |
|
12 #define ldblib_c |
|
13 #define LUA_LIB |
|
14 |
|
15 #include "lua.h" |
|
16 |
|
17 #include "lauxlib.h" |
|
18 #include "lualib.h" |
|
19 |
|
20 |
|
21 |
|
22 static int db_getregistry (lua_State *L) { |
|
23 lua_pushvalue(L, LUA_REGISTRYINDEX); |
|
24 return 1; |
|
25 } |
|
26 |
|
27 |
|
28 static int db_getmetatable (lua_State *L) { |
|
29 luaL_checkany(L, 1); |
|
30 if (!lua_getmetatable(L, 1)) { |
|
31 lua_pushnil(L); /* no metatable */ |
|
32 } |
|
33 return 1; |
|
34 } |
|
35 |
|
36 |
|
37 static int db_setmetatable (lua_State *L) { |
|
38 int t = lua_type(L, 2); |
|
39 luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, |
|
40 "nil or table expected"); |
|
41 lua_settop(L, 2); |
|
42 lua_pushboolean(L, lua_setmetatable(L, 1)); |
|
43 return 1; |
|
44 } |
|
45 |
|
46 |
|
47 static int db_getfenv (lua_State *L) { |
|
48 lua_getfenv(L, 1); |
|
49 return 1; |
|
50 } |
|
51 |
|
52 |
|
53 static int db_setfenv (lua_State *L) { |
|
54 luaL_checktype(L, 2, LUA_TTABLE); |
|
55 lua_settop(L, 2); |
|
56 if (lua_setfenv(L, 1) == 0) |
|
57 luaL_error(L, LUA_QL("setfenv") |
|
58 " cannot change environment of given object"); |
|
59 return 1; |
|
60 } |
|
61 |
|
62 |
|
63 static void settabss (lua_State *L, const char *i, const char *v) { |
|
64 lua_pushstring(L, v); |
|
65 lua_setfield(L, -2, i); |
|
66 } |
|
67 |
|
68 |
|
69 static void settabsi (lua_State *L, const char *i, int v) { |
|
70 lua_pushinteger(L, v); |
|
71 lua_setfield(L, -2, i); |
|
72 } |
|
73 |
|
74 |
|
75 static lua_State *getthread (lua_State *L, int *arg) { |
|
76 if (lua_isthread(L, 1)) { |
|
77 *arg = 1; |
|
78 return lua_tothread(L, 1); |
|
79 } |
|
80 else { |
|
81 *arg = 0; |
|
82 return L; |
|
83 } |
|
84 } |
|
85 |
|
86 |
|
87 static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { |
|
88 if (L == L1) { |
|
89 lua_pushvalue(L, -2); |
|
90 lua_remove(L, -3); |
|
91 } |
|
92 else |
|
93 lua_xmove(L1, L, 1); |
|
94 lua_setfield(L, -2, fname); |
|
95 } |
|
96 |
|
97 |
|
98 static int db_getinfo (lua_State *L) { |
|
99 lua_Debug ar; |
|
100 int arg; |
|
101 lua_State *L1 = getthread(L, &arg); |
|
102 const char *options = luaL_optstring(L, arg+2, "flnSu"); |
|
103 if (lua_isnumber(L, arg+1)) { |
|
104 if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { |
|
105 lua_pushnil(L); /* level out of range */ |
|
106 return 1; |
|
107 } |
|
108 } |
|
109 else if (lua_isfunction(L, arg+1)) { |
|
110 lua_pushfstring(L, ">%s", options); |
|
111 options = lua_tostring(L, -1); |
|
112 lua_pushvalue(L, arg+1); |
|
113 lua_xmove(L, L1, 1); |
|
114 } |
|
115 else |
|
116 return luaL_argerror(L, arg+1, "function or level expected"); |
|
117 if (!lua_getinfo(L1, options, &ar)) |
|
118 return luaL_argerror(L, arg+2, "invalid option"); |
|
119 lua_createtable(L, 0, 2); |
|
120 if (strchr(options, 'S')) { |
|
121 settabss(L, "source", ar.source); |
|
122 settabss(L, "short_src", ar.short_src); |
|
123 settabsi(L, "linedefined", ar.linedefined); |
|
124 settabsi(L, "lastlinedefined", ar.lastlinedefined); |
|
125 settabss(L, "what", ar.what); |
|
126 } |
|
127 if (strchr(options, 'l')) |
|
128 settabsi(L, "currentline", ar.currentline); |
|
129 if (strchr(options, 'u')) |
|
130 settabsi(L, "nups", ar.nups); |
|
131 if (strchr(options, 'n')) { |
|
132 settabss(L, "name", ar.name); |
|
133 settabss(L, "namewhat", ar.namewhat); |
|
134 } |
|
135 if (strchr(options, 'L')) |
|
136 treatstackoption(L, L1, "activelines"); |
|
137 if (strchr(options, 'f')) |
|
138 treatstackoption(L, L1, "func"); |
|
139 return 1; /* return table */ |
|
140 } |
|
141 |
|
142 |
|
143 static int db_getlocal (lua_State *L) { |
|
144 int arg; |
|
145 lua_State *L1 = getthread(L, &arg); |
|
146 lua_Debug ar; |
|
147 const char *name; |
|
148 if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ |
|
149 return luaL_argerror(L, arg+1, "level out of range"); |
|
150 name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2)); |
|
151 if (name) { |
|
152 lua_xmove(L1, L, 1); |
|
153 lua_pushstring(L, name); |
|
154 lua_pushvalue(L, -2); |
|
155 return 2; |
|
156 } |
|
157 else { |
|
158 lua_pushnil(L); |
|
159 return 1; |
|
160 } |
|
161 } |
|
162 |
|
163 |
|
164 static int db_setlocal (lua_State *L) { |
|
165 int arg; |
|
166 lua_State *L1 = getthread(L, &arg); |
|
167 lua_Debug ar; |
|
168 if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ |
|
169 return luaL_argerror(L, arg+1, "level out of range"); |
|
170 luaL_checkany(L, arg+3); |
|
171 lua_settop(L, arg+3); |
|
172 lua_xmove(L, L1, 1); |
|
173 lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2))); |
|
174 return 1; |
|
175 } |
|
176 |
|
177 |
|
178 static int auxupvalue (lua_State *L, int get) { |
|
179 const char *name; |
|
180 int n = luaL_checkint(L, 2); |
|
181 luaL_checktype(L, 1, LUA_TFUNCTION); |
|
182 if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ |
|
183 name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); |
|
184 if (name == NULL) return 0; |
|
185 lua_pushstring(L, name); |
|
186 lua_insert(L, -(get+1)); |
|
187 return get + 1; |
|
188 } |
|
189 |
|
190 |
|
191 static int db_getupvalue (lua_State *L) { |
|
192 return auxupvalue(L, 1); |
|
193 } |
|
194 |
|
195 |
|
196 static int db_setupvalue (lua_State *L) { |
|
197 luaL_checkany(L, 3); |
|
198 return auxupvalue(L, 0); |
|
199 } |
|
200 |
|
201 |
|
202 |
|
203 static const char KEY_HOOK = 'h'; |
|
204 |
|
205 |
|
206 static void hookf (lua_State *L, lua_Debug *ar) { |
|
207 static const char *const hooknames[] = |
|
208 {"call", "return", "line", "count", "tail return"}; |
|
209 lua_pushlightuserdata(L, (void *)&KEY_HOOK); |
|
210 lua_rawget(L, LUA_REGISTRYINDEX); |
|
211 lua_pushlightuserdata(L, L); |
|
212 lua_rawget(L, -2); |
|
213 if (lua_isfunction(L, -1)) { |
|
214 lua_pushstring(L, hooknames[(int)ar->event]); |
|
215 if (ar->currentline >= 0) |
|
216 lua_pushinteger(L, ar->currentline); |
|
217 else lua_pushnil(L); |
|
218 lua_assert(lua_getinfo(L, "lS", ar)); |
|
219 lua_call(L, 2, 0); |
|
220 } |
|
221 } |
|
222 |
|
223 |
|
224 static int makemask (const char *smask, int count) { |
|
225 int mask = 0; |
|
226 if (strchr(smask, 'c')) mask |= LUA_MASKCALL; |
|
227 if (strchr(smask, 'r')) mask |= LUA_MASKRET; |
|
228 if (strchr(smask, 'l')) mask |= LUA_MASKLINE; |
|
229 if (count > 0) mask |= LUA_MASKCOUNT; |
|
230 return mask; |
|
231 } |
|
232 |
|
233 |
|
234 static char *unmakemask (int mask, char *smask) { |
|
235 int i = 0; |
|
236 if (mask & LUA_MASKCALL) smask[i++] = 'c'; |
|
237 if (mask & LUA_MASKRET) smask[i++] = 'r'; |
|
238 if (mask & LUA_MASKLINE) smask[i++] = 'l'; |
|
239 smask[i] = '\0'; |
|
240 return smask; |
|
241 } |
|
242 |
|
243 |
|
244 static void gethooktable (lua_State *L) { |
|
245 lua_pushlightuserdata(L, (void *)&KEY_HOOK); |
|
246 lua_rawget(L, LUA_REGISTRYINDEX); |
|
247 if (!lua_istable(L, -1)) { |
|
248 lua_pop(L, 1); |
|
249 lua_createtable(L, 0, 1); |
|
250 lua_pushlightuserdata(L, (void *)&KEY_HOOK); |
|
251 lua_pushvalue(L, -2); |
|
252 lua_rawset(L, LUA_REGISTRYINDEX); |
|
253 } |
|
254 } |
|
255 |
|
256 |
|
257 static int db_sethook (lua_State *L) { |
|
258 int arg, mask, count; |
|
259 lua_Hook func; |
|
260 lua_State *L1 = getthread(L, &arg); |
|
261 if (lua_isnoneornil(L, arg+1)) { |
|
262 lua_settop(L, arg+1); |
|
263 func = NULL; mask = 0; count = 0; /* turn off hooks */ |
|
264 } |
|
265 else { |
|
266 const char *smask = luaL_checkstring(L, arg+2); |
|
267 luaL_checktype(L, arg+1, LUA_TFUNCTION); |
|
268 count = luaL_optint(L, arg+3, 0); |
|
269 func = hookf; mask = makemask(smask, count); |
|
270 } |
|
271 gethooktable(L); |
|
272 lua_pushlightuserdata(L, L1); |
|
273 lua_pushvalue(L, arg+1); |
|
274 lua_rawset(L, -3); /* set new hook */ |
|
275 lua_pop(L, 1); /* remove hook table */ |
|
276 lua_sethook(L1, func, mask, count); /* set hooks */ |
|
277 return 0; |
|
278 } |
|
279 |
|
280 |
|
281 static int db_gethook (lua_State *L) { |
|
282 int arg; |
|
283 lua_State *L1 = getthread(L, &arg); |
|
284 char buff[5]; |
|
285 int mask = lua_gethookmask(L1); |
|
286 lua_Hook hook = lua_gethook(L1); |
|
287 if (hook != NULL && hook != hookf) /* external hook? */ |
|
288 lua_pushliteral(L, "external hook"); |
|
289 else { |
|
290 gethooktable(L); |
|
291 lua_pushlightuserdata(L, L1); |
|
292 lua_rawget(L, -2); /* get hook */ |
|
293 lua_remove(L, -2); /* remove hook table */ |
|
294 } |
|
295 lua_pushstring(L, unmakemask(mask, buff)); |
|
296 lua_pushinteger(L, lua_gethookcount(L1)); |
|
297 return 3; |
|
298 } |
|
299 |
|
300 |
|
301 static int db_debug (lua_State *L) { |
|
302 for (;;) { |
|
303 char buffer[250]; |
|
304 fputs("lua_debug> ", stderr); |
|
305 if (fgets(buffer, sizeof(buffer), stdin) == 0 || |
|
306 strcmp(buffer, "cont\n") == 0) |
|
307 return 0; |
|
308 if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || |
|
309 lua_pcall(L, 0, 0, 0)) { |
|
310 fputs(lua_tostring(L, -1), stderr); |
|
311 fputs("\n", stderr); |
|
312 } |
|
313 lua_settop(L, 0); /* remove eventual returns */ |
|
314 } |
|
315 } |
|
316 |
|
317 |
|
318 #define LEVELS1 12 /* size of the first part of the stack */ |
|
319 #define LEVELS2 10 /* size of the second part of the stack */ |
|
320 |
|
321 static int db_errorfb (lua_State *L) { |
|
322 int level; |
|
323 int firstpart = 1; /* still before eventual `...' */ |
|
324 int arg; |
|
325 lua_State *L1 = getthread(L, &arg); |
|
326 lua_Debug ar; |
|
327 if (lua_isnumber(L, arg+2)) { |
|
328 level = (int)lua_tointeger(L, arg+2); |
|
329 lua_pop(L, 1); |
|
330 } |
|
331 else |
|
332 level = (L == L1) ? 1 : 0; /* level 0 may be this own function */ |
|
333 if (lua_gettop(L) == arg) |
|
334 lua_pushliteral(L, ""); |
|
335 else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ |
|
336 else lua_pushliteral(L, "\n"); |
|
337 lua_pushliteral(L, "stack traceback:"); |
|
338 while (lua_getstack(L1, level++, &ar)) { |
|
339 if (level > LEVELS1 && firstpart) { |
|
340 /* no more than `LEVELS2' more levels? */ |
|
341 if (!lua_getstack(L1, level+LEVELS2, &ar)) |
|
342 level--; /* keep going */ |
|
343 else { |
|
344 lua_pushliteral(L, "\n\t..."); /* too many levels */ |
|
345 while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ |
|
346 level++; |
|
347 } |
|
348 firstpart = 0; |
|
349 continue; |
|
350 } |
|
351 lua_pushliteral(L, "\n\t"); |
|
352 lua_getinfo(L1, "Snl", &ar); |
|
353 lua_pushfstring(L, "%s:", ar.short_src); |
|
354 if (ar.currentline > 0) |
|
355 lua_pushfstring(L, "%d:", ar.currentline); |
|
356 if (*ar.namewhat != '\0') /* is there a name? */ |
|
357 lua_pushfstring(L, " in function " LUA_QS, ar.name); |
|
358 else { |
|
359 if (*ar.what == 'm') /* main? */ |
|
360 lua_pushfstring(L, " in main chunk"); |
|
361 else if (*ar.what == 'C' || *ar.what == 't') |
|
362 lua_pushliteral(L, " ?"); /* C function or tail call */ |
|
363 else |
|
364 lua_pushfstring(L, " in function <%s:%d>", |
|
365 ar.short_src, ar.linedefined); |
|
366 } |
|
367 lua_concat(L, lua_gettop(L) - arg); |
|
368 } |
|
369 lua_concat(L, lua_gettop(L) - arg); |
|
370 return 1; |
|
371 } |
|
372 |
|
373 |
|
374 static const luaL_Reg dblib[] = { |
|
375 {"debug", db_debug}, |
|
376 {"getfenv", db_getfenv}, |
|
377 {"gethook", db_gethook}, |
|
378 {"getinfo", db_getinfo}, |
|
379 {"getlocal", db_getlocal}, |
|
380 {"getregistry", db_getregistry}, |
|
381 {"getmetatable", db_getmetatable}, |
|
382 {"getupvalue", db_getupvalue}, |
|
383 {"setfenv", db_setfenv}, |
|
384 {"sethook", db_sethook}, |
|
385 {"setlocal", db_setlocal}, |
|
386 {"setmetatable", db_setmetatable}, |
|
387 {"setupvalue", db_setupvalue}, |
|
388 {"traceback", db_errorfb}, |
|
389 {NULL, NULL} |
|
390 }; |
|
391 |
|
392 |
|
393 LUALIB_API int luaopen_debug (lua_State *L) { |
|
394 luaL_register(L, LUA_DBLIBNAME, dblib); |
|
395 return 1; |
|
396 } |
|
397 |