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