1 | /* |
2 | ** Debug library. |
3 | ** Copyright (C) 2005-2021 Mike Pall. See Copyright Notice in luajit.h |
4 | ** |
5 | ** Major portions taken verbatim or adapted from the Lua interpreter. |
6 | ** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
7 | */ |
8 | |
9 | #define lib_debug_c |
10 | #define LUA_LIB |
11 | |
12 | #include "lua.h" |
13 | #include "lauxlib.h" |
14 | #include "lualib.h" |
15 | |
16 | #include "lj_obj.h" |
17 | #include "lj_gc.h" |
18 | #include "lj_err.h" |
19 | #include "lj_debug.h" |
20 | #include "lj_lib.h" |
21 | |
22 | /* ------------------------------------------------------------------------ */ |
23 | |
24 | #define LJLIB_MODULE_debug |
25 | |
26 | LJLIB_CF(debug_getregistry) |
27 | { |
28 | copyTV(L, L->top++, registry(L)); |
29 | return 1; |
30 | } |
31 | |
32 | LJLIB_CF(debug_getmetatable) LJLIB_REC(.) |
33 | { |
34 | lj_lib_checkany(L, 1); |
35 | if (!lua_getmetatable(L, 1)) { |
36 | setnilV(L->top-1); |
37 | } |
38 | return 1; |
39 | } |
40 | |
41 | LJLIB_CF(debug_setmetatable) |
42 | { |
43 | lj_lib_checktabornil(L, 2); |
44 | L->top = L->base+2; |
45 | lua_setmetatable(L, 1); |
46 | #if !LJ_52 |
47 | setboolV(L->top-1, 1); |
48 | #endif |
49 | return 1; |
50 | } |
51 | |
52 | LJLIB_CF(debug_getfenv) |
53 | { |
54 | lj_lib_checkany(L, 1); |
55 | lua_getfenv(L, 1); |
56 | return 1; |
57 | } |
58 | |
59 | LJLIB_CF(debug_setfenv) |
60 | { |
61 | lj_lib_checktab(L, 2); |
62 | L->top = L->base+2; |
63 | if (!lua_setfenv(L, 1)) |
64 | lj_err_caller(L, LJ_ERR_SETFENV); |
65 | return 1; |
66 | } |
67 | |
68 | /* ------------------------------------------------------------------------ */ |
69 | |
70 | static void settabss(lua_State *L, const char *i, const char *v) |
71 | { |
72 | lua_pushstring(L, v); |
73 | lua_setfield(L, -2, i); |
74 | } |
75 | |
76 | static void settabsi(lua_State *L, const char *i, int v) |
77 | { |
78 | lua_pushinteger(L, v); |
79 | lua_setfield(L, -2, i); |
80 | } |
81 | |
82 | static void settabsb(lua_State *L, const char *i, int v) |
83 | { |
84 | lua_pushboolean(L, v); |
85 | lua_setfield(L, -2, i); |
86 | } |
87 | |
88 | static lua_State *getthread(lua_State *L, int *arg) |
89 | { |
90 | if (L->base < L->top && tvisthread(L->base)) { |
91 | *arg = 1; |
92 | return threadV(L->base); |
93 | } else { |
94 | *arg = 0; |
95 | return L; |
96 | } |
97 | } |
98 | |
99 | static void treatstackoption(lua_State *L, lua_State *L1, const char *fname) |
100 | { |
101 | if (L == L1) { |
102 | lua_pushvalue(L, -2); |
103 | lua_remove(L, -3); |
104 | } |
105 | else |
106 | lua_xmove(L1, L, 1); |
107 | lua_setfield(L, -2, fname); |
108 | } |
109 | |
110 | LJLIB_CF(debug_getinfo) |
111 | { |
112 | lj_Debug ar; |
113 | int arg, opt_f = 0, opt_L = 0; |
114 | lua_State *L1 = getthread(L, &arg); |
115 | const char *options = luaL_optstring(L, arg+2, "flnSu" ); |
116 | if (lua_isnumber(L, arg+1)) { |
117 | if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), (lua_Debug *)&ar)) { |
118 | setnilV(L->top-1); |
119 | return 1; |
120 | } |
121 | } else if (L->base+arg < L->top && tvisfunc(L->base+arg)) { |
122 | options = lua_pushfstring(L, ">%s" , options); |
123 | setfuncV(L1, L1->top++, funcV(L->base+arg)); |
124 | } else { |
125 | lj_err_arg(L, arg+1, LJ_ERR_NOFUNCL); |
126 | } |
127 | if (!lj_debug_getinfo(L1, options, &ar, 1)) |
128 | lj_err_arg(L, arg+2, LJ_ERR_INVOPT); |
129 | lua_createtable(L, 0, 16); /* Create result table. */ |
130 | for (; *options; options++) { |
131 | switch (*options) { |
132 | case 'S': |
133 | settabss(L, "source" , ar.source); |
134 | settabss(L, "short_src" , ar.short_src); |
135 | settabsi(L, "linedefined" , ar.linedefined); |
136 | settabsi(L, "lastlinedefined" , ar.lastlinedefined); |
137 | settabss(L, "what" , ar.what); |
138 | break; |
139 | case 'l': |
140 | settabsi(L, "currentline" , ar.currentline); |
141 | break; |
142 | case 'u': |
143 | settabsi(L, "nups" , ar.nups); |
144 | settabsi(L, "nparams" , ar.nparams); |
145 | settabsb(L, "isvararg" , ar.isvararg); |
146 | break; |
147 | case 'n': |
148 | settabss(L, "name" , ar.name); |
149 | settabss(L, "namewhat" , ar.namewhat); |
150 | break; |
151 | case 'f': opt_f = 1; break; |
152 | case 'L': opt_L = 1; break; |
153 | default: break; |
154 | } |
155 | } |
156 | if (opt_L) treatstackoption(L, L1, "activelines" ); |
157 | if (opt_f) treatstackoption(L, L1, "func" ); |
158 | return 1; /* Return result table. */ |
159 | } |
160 | |
161 | LJLIB_CF(debug_getlocal) |
162 | { |
163 | int arg; |
164 | lua_State *L1 = getthread(L, &arg); |
165 | lua_Debug ar; |
166 | const char *name; |
167 | int slot = lj_lib_checkint(L, arg+2); |
168 | if (tvisfunc(L->base+arg)) { |
169 | L->top = L->base+arg+1; |
170 | lua_pushstring(L, lua_getlocal(L, NULL, slot)); |
171 | return 1; |
172 | } |
173 | if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar)) |
174 | lj_err_arg(L, arg+1, LJ_ERR_LVLRNG); |
175 | name = lua_getlocal(L1, &ar, slot); |
176 | if (name) { |
177 | lua_xmove(L1, L, 1); |
178 | lua_pushstring(L, name); |
179 | lua_pushvalue(L, -2); |
180 | return 2; |
181 | } else { |
182 | setnilV(L->top-1); |
183 | return 1; |
184 | } |
185 | } |
186 | |
187 | LJLIB_CF(debug_setlocal) |
188 | { |
189 | int arg; |
190 | lua_State *L1 = getthread(L, &arg); |
191 | lua_Debug ar; |
192 | TValue *tv; |
193 | if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar)) |
194 | lj_err_arg(L, arg+1, LJ_ERR_LVLRNG); |
195 | tv = lj_lib_checkany(L, arg+3); |
196 | copyTV(L1, L1->top++, tv); |
197 | lua_pushstring(L, lua_setlocal(L1, &ar, lj_lib_checkint(L, arg+2))); |
198 | return 1; |
199 | } |
200 | |
201 | static int debug_getupvalue(lua_State *L, int get) |
202 | { |
203 | int32_t n = lj_lib_checkint(L, 2); |
204 | const char *name; |
205 | lj_lib_checkfunc(L, 1); |
206 | name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); |
207 | if (name) { |
208 | lua_pushstring(L, name); |
209 | if (!get) return 1; |
210 | copyTV(L, L->top, L->top-2); |
211 | L->top++; |
212 | return 2; |
213 | } |
214 | return 0; |
215 | } |
216 | |
217 | LJLIB_CF(debug_getupvalue) |
218 | { |
219 | return debug_getupvalue(L, 1); |
220 | } |
221 | |
222 | LJLIB_CF(debug_setupvalue) |
223 | { |
224 | lj_lib_checkany(L, 3); |
225 | return debug_getupvalue(L, 0); |
226 | } |
227 | |
228 | LJLIB_CF(debug_upvalueid) |
229 | { |
230 | GCfunc *fn = lj_lib_checkfunc(L, 1); |
231 | int32_t n = lj_lib_checkint(L, 2) - 1; |
232 | if ((uint32_t)n >= fn->l.nupvalues) |
233 | lj_err_arg(L, 2, LJ_ERR_IDXRNG); |
234 | lua_pushlightuserdata(L, isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) : |
235 | (void *)&fn->c.upvalue[n]); |
236 | return 1; |
237 | } |
238 | |
239 | LJLIB_CF(debug_upvaluejoin) |
240 | { |
241 | GCfunc *fn[2]; |
242 | GCRef *p[2]; |
243 | int i; |
244 | for (i = 0; i < 2; i++) { |
245 | int32_t n; |
246 | fn[i] = lj_lib_checkfunc(L, 2*i+1); |
247 | if (!isluafunc(fn[i])) |
248 | lj_err_arg(L, 2*i+1, LJ_ERR_NOLFUNC); |
249 | n = lj_lib_checkint(L, 2*i+2) - 1; |
250 | if ((uint32_t)n >= fn[i]->l.nupvalues) |
251 | lj_err_arg(L, 2*i+2, LJ_ERR_IDXRNG); |
252 | p[i] = &fn[i]->l.uvptr[n]; |
253 | } |
254 | setgcrefr(*p[0], *p[1]); |
255 | lj_gc_objbarrier(L, fn[0], gcref(*p[1])); |
256 | return 0; |
257 | } |
258 | |
259 | #if LJ_52 |
260 | LJLIB_CF(debug_getuservalue) |
261 | { |
262 | TValue *o = L->base; |
263 | if (o < L->top && tvisudata(o)) |
264 | settabV(L, o, tabref(udataV(o)->env)); |
265 | else |
266 | setnilV(o); |
267 | L->top = o+1; |
268 | return 1; |
269 | } |
270 | |
271 | LJLIB_CF(debug_setuservalue) |
272 | { |
273 | TValue *o = L->base; |
274 | if (!(o < L->top && tvisudata(o))) |
275 | lj_err_argt(L, 1, LUA_TUSERDATA); |
276 | if (!(o+1 < L->top && tvistab(o+1))) |
277 | lj_err_argt(L, 2, LUA_TTABLE); |
278 | L->top = o+2; |
279 | lua_setfenv(L, 1); |
280 | return 1; |
281 | } |
282 | #endif |
283 | |
284 | /* ------------------------------------------------------------------------ */ |
285 | |
286 | #define KEY_HOOK (U64x(80000000,00000000)|'h') |
287 | |
288 | static void hookf(lua_State *L, lua_Debug *ar) |
289 | { |
290 | static const char *const hooknames[] = |
291 | {"call" , "return" , "line" , "count" , "tail return" }; |
292 | (L->top++)->u64 = KEY_HOOK; |
293 | lua_rawget(L, LUA_REGISTRYINDEX); |
294 | if (lua_isfunction(L, -1)) { |
295 | lua_pushstring(L, hooknames[(int)ar->event]); |
296 | if (ar->currentline >= 0) |
297 | lua_pushinteger(L, ar->currentline); |
298 | else lua_pushnil(L); |
299 | lua_call(L, 2, 0); |
300 | } |
301 | } |
302 | |
303 | static int makemask(const char *smask, int count) |
304 | { |
305 | int mask = 0; |
306 | if (strchr(smask, 'c')) mask |= LUA_MASKCALL; |
307 | if (strchr(smask, 'r')) mask |= LUA_MASKRET; |
308 | if (strchr(smask, 'l')) mask |= LUA_MASKLINE; |
309 | if (count > 0) mask |= LUA_MASKCOUNT; |
310 | return mask; |
311 | } |
312 | |
313 | static char *unmakemask(int mask, char *smask) |
314 | { |
315 | int i = 0; |
316 | if (mask & LUA_MASKCALL) smask[i++] = 'c'; |
317 | if (mask & LUA_MASKRET) smask[i++] = 'r'; |
318 | if (mask & LUA_MASKLINE) smask[i++] = 'l'; |
319 | smask[i] = '\0'; |
320 | return smask; |
321 | } |
322 | |
323 | LJLIB_CF(debug_sethook) |
324 | { |
325 | int arg, mask, count; |
326 | lua_Hook func; |
327 | (void)getthread(L, &arg); |
328 | if (lua_isnoneornil(L, arg+1)) { |
329 | lua_settop(L, arg+1); |
330 | func = NULL; mask = 0; count = 0; /* turn off hooks */ |
331 | } else { |
332 | const char *smask = luaL_checkstring(L, arg+2); |
333 | luaL_checktype(L, arg+1, LUA_TFUNCTION); |
334 | count = luaL_optint(L, arg+3, 0); |
335 | func = hookf; mask = makemask(smask, count); |
336 | } |
337 | (L->top++)->u64 = KEY_HOOK; |
338 | lua_pushvalue(L, arg+1); |
339 | lua_rawset(L, LUA_REGISTRYINDEX); |
340 | lua_sethook(L, func, mask, count); |
341 | return 0; |
342 | } |
343 | |
344 | LJLIB_CF(debug_gethook) |
345 | { |
346 | char buff[5]; |
347 | int mask = lua_gethookmask(L); |
348 | lua_Hook hook = lua_gethook(L); |
349 | if (hook != NULL && hook != hookf) { /* external hook? */ |
350 | lua_pushliteral(L, "external hook" ); |
351 | } else { |
352 | (L->top++)->u64 = KEY_HOOK; |
353 | lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ |
354 | } |
355 | lua_pushstring(L, unmakemask(mask, buff)); |
356 | lua_pushinteger(L, lua_gethookcount(L)); |
357 | return 3; |
358 | } |
359 | |
360 | /* ------------------------------------------------------------------------ */ |
361 | |
362 | LJLIB_CF(debug_debug) |
363 | { |
364 | for (;;) { |
365 | char buffer[250]; |
366 | fputs("lua_debug> " , stderr); |
367 | if (fgets(buffer, sizeof(buffer), stdin) == 0 || |
368 | strcmp(buffer, "cont\n" ) == 0) |
369 | return 0; |
370 | if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)" ) || |
371 | lua_pcall(L, 0, 0, 0)) { |
372 | const char *s = lua_tostring(L, -1); |
373 | fputs(s ? s : "(error object is not a string)" , stderr); |
374 | fputs("\n" , stderr); |
375 | } |
376 | lua_settop(L, 0); /* remove eventual returns */ |
377 | } |
378 | } |
379 | |
380 | /* ------------------------------------------------------------------------ */ |
381 | |
382 | #define LEVELS1 12 /* size of the first part of the stack */ |
383 | #define LEVELS2 10 /* size of the second part of the stack */ |
384 | |
385 | LJLIB_CF(debug_traceback) |
386 | { |
387 | int arg; |
388 | lua_State *L1 = getthread(L, &arg); |
389 | const char *msg = lua_tostring(L, arg+1); |
390 | if (msg == NULL && L->top > L->base+arg) |
391 | L->top = L->base+arg+1; |
392 | else |
393 | luaL_traceback(L, L1, msg, lj_lib_optint(L, arg+2, (L == L1))); |
394 | return 1; |
395 | } |
396 | |
397 | /* ------------------------------------------------------------------------ */ |
398 | |
399 | #include "lj_libdef.h" |
400 | |
401 | LUALIB_API int luaopen_debug(lua_State *L) |
402 | { |
403 | LJ_LIB_REG(L, LUA_DBLIBNAME, debug); |
404 | return 1; |
405 | } |
406 | |
407 | |