1 | /* |
2 | ** $Id: ltests.c $ |
3 | ** Internal Module for Debugging of the Lua Implementation |
4 | ** See Copyright Notice in lua.h |
5 | */ |
6 | |
7 | #define ltests_c |
8 | #define LUA_CORE |
9 | |
10 | #include "lprefix.h" |
11 | |
12 | |
13 | #include <limits.h> |
14 | #include <setjmp.h> |
15 | #include <stdio.h> |
16 | #include <stdlib.h> |
17 | #include <string.h> |
18 | |
19 | #include "lua.h" |
20 | |
21 | #include "lapi.h" |
22 | #include "lauxlib.h" |
23 | #include "lcode.h" |
24 | #include "lctype.h" |
25 | #include "ldebug.h" |
26 | #include "ldo.h" |
27 | #include "lfunc.h" |
28 | #include "lmem.h" |
29 | #include "lopcodes.h" |
30 | #include "lopnames.h" |
31 | #include "lstate.h" |
32 | #include "lstring.h" |
33 | #include "ltable.h" |
34 | #include "lualib.h" |
35 | |
36 | |
37 | |
38 | /* |
39 | ** The whole module only makes sense with LUA_DEBUG on |
40 | */ |
41 | #if defined(LUA_DEBUG) |
42 | |
43 | |
44 | void *l_Trick = 0; |
45 | |
46 | |
47 | #define obj_at(L,k) s2v(L->ci->func + (k)) |
48 | |
49 | |
50 | static int runC (lua_State *L, lua_State *L1, const char *pc); |
51 | |
52 | |
53 | static void setnameval (lua_State *L, const char *name, int val) { |
54 | lua_pushinteger(L, val); |
55 | lua_setfield(L, -2, name); |
56 | } |
57 | |
58 | |
59 | static void pushobject (lua_State *L, const TValue *o) { |
60 | setobj2s(L, L->top, o); |
61 | api_incr_top(L); |
62 | } |
63 | |
64 | |
65 | static void badexit (const char *fmt, const char *s1, const char *s2) { |
66 | fprintf(stderr, fmt, s1); |
67 | if (s2) |
68 | fprintf(stderr, "extra info: %s\n" , s2); |
69 | /* avoid assertion failures when exiting */ |
70 | l_memcontrol.numblocks = l_memcontrol.total = 0; |
71 | exit(EXIT_FAILURE); |
72 | } |
73 | |
74 | |
75 | static int tpanic (lua_State *L) { |
76 | const char *msg = lua_tostring(L, -1); |
77 | if (msg == NULL) msg = "error object is not a string" ; |
78 | return (badexit("PANIC: unprotected error in call to Lua API (%s)\n" , |
79 | msg, NULL), |
80 | 0); /* do not return to Lua */ |
81 | } |
82 | |
83 | |
84 | /* |
85 | ** Warning function for tests. First, it concatenates all parts of |
86 | ** a warning in buffer 'buff'. Then, it has three modes: |
87 | ** - 0.normal: messages starting with '#' are shown on standard output; |
88 | ** - other messages abort the tests (they represent real warning |
89 | ** conditions; the standard tests should not generate these conditions |
90 | ** unexpectedly); |
91 | ** - 1.allow: all messages are shown; |
92 | ** - 2.store: all warnings go to the global '_WARN'; |
93 | */ |
94 | static void warnf (void *ud, const char *msg, int tocont) { |
95 | lua_State *L = cast(lua_State *, ud); |
96 | static char buff[200] = "" ; /* should be enough for tests... */ |
97 | static int onoff = 0; |
98 | static int mode = 0; /* start in normal mode */ |
99 | static int lasttocont = 0; |
100 | if (!lasttocont && !tocont && *msg == '@') { /* control message? */ |
101 | if (buff[0] != '\0') |
102 | badexit("Control warning during warning: %s\naborting...\n" , msg, buff); |
103 | if (strcmp(msg, "@off" ) == 0) |
104 | onoff = 0; |
105 | else if (strcmp(msg, "@on" ) == 0) |
106 | onoff = 1; |
107 | else if (strcmp(msg, "@normal" ) == 0) |
108 | mode = 0; |
109 | else if (strcmp(msg, "@allow" ) == 0) |
110 | mode = 1; |
111 | else if (strcmp(msg, "@store" ) == 0) |
112 | mode = 2; |
113 | else |
114 | badexit("Invalid control warning in test mode: %s\naborting...\n" , |
115 | msg, NULL); |
116 | return; |
117 | } |
118 | lasttocont = tocont; |
119 | if (strlen(msg) >= sizeof(buff) - strlen(buff)) |
120 | badexit("warnf-buffer overflow (%s)\n" , msg, buff); |
121 | strcat(buff, msg); /* add new message to current warning */ |
122 | if (!tocont) { /* message finished? */ |
123 | lua_unlock(L); |
124 | lua_getglobal(L, "_WARN" ); |
125 | if (!lua_toboolean(L, -1)) |
126 | lua_pop(L, 1); /* ok, no previous unexpected warning */ |
127 | else { |
128 | badexit("Unhandled warning in store mode: %s\naborting...\n" , |
129 | lua_tostring(L, -1), buff); |
130 | } |
131 | lua_lock(L); |
132 | switch (mode) { |
133 | case 0: { /* normal */ |
134 | if (buff[0] != '#' && onoff) /* unexpected warning? */ |
135 | badexit("Unexpected warning in test mode: %s\naborting...\n" , |
136 | buff, NULL); |
137 | } /* FALLTHROUGH */ |
138 | case 1: { /* allow */ |
139 | if (onoff) |
140 | fprintf(stderr, "Lua warning: %s\n" , buff); /* print warning */ |
141 | break; |
142 | } |
143 | case 2: { /* store */ |
144 | lua_unlock(L); |
145 | lua_pushstring(L, buff); |
146 | lua_setglobal(L, "_WARN" ); /* assign message to global '_WARN' */ |
147 | lua_lock(L); |
148 | break; |
149 | } |
150 | } |
151 | buff[0] = '\0'; /* prepare buffer for next warning */ |
152 | } |
153 | } |
154 | |
155 | |
156 | /* |
157 | ** {====================================================================== |
158 | ** Controlled version for realloc. |
159 | ** ======================================================================= |
160 | */ |
161 | |
162 | #define MARK 0x55 /* 01010101 (a nice pattern) */ |
163 | |
164 | typedef union Header { |
165 | LUAI_MAXALIGN; |
166 | struct { |
167 | size_t size; |
168 | int type; |
169 | } d; |
170 | } Header; |
171 | |
172 | |
173 | #if !defined(EXTERNMEMCHECK) |
174 | |
175 | /* full memory check */ |
176 | #define MARKSIZE 16 /* size of marks after each block */ |
177 | #define fillmem(mem,size) memset(mem, -MARK, size) |
178 | |
179 | #else |
180 | |
181 | /* external memory check: don't do it twice */ |
182 | #define MARKSIZE 0 |
183 | #define fillmem(mem,size) /* empty */ |
184 | |
185 | #endif |
186 | |
187 | |
188 | Memcontrol l_memcontrol = |
189 | {0, 0UL, 0UL, 0UL, 0UL, (~0UL), |
190 | {0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL, 0UL}}; |
191 | |
192 | |
193 | static void freeblock (Memcontrol *mc, Header *block) { |
194 | if (block) { |
195 | size_t size = block->d.size; |
196 | int i; |
197 | for (i = 0; i < MARKSIZE; i++) /* check marks after block */ |
198 | lua_assert(*(cast_charp(block + 1) + size + i) == MARK); |
199 | mc->objcount[block->d.type]--; |
200 | fillmem(block, sizeof(Header) + size + MARKSIZE); /* erase block */ |
201 | free(block); /* actually free block */ |
202 | mc->numblocks--; /* update counts */ |
203 | mc->total -= size; |
204 | } |
205 | } |
206 | |
207 | |
208 | void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { |
209 | Memcontrol *mc = cast(Memcontrol *, ud); |
210 | Header *block = cast(Header *, b); |
211 | int type; |
212 | if (mc->memlimit == 0) { /* first time? */ |
213 | char *limit = getenv("MEMLIMIT" ); /* initialize memory limit */ |
214 | mc->memlimit = limit ? strtoul(limit, NULL, 10) : ULONG_MAX; |
215 | } |
216 | if (block == NULL) { |
217 | type = (oldsize < LUA_NUMTAGS) ? oldsize : 0; |
218 | oldsize = 0; |
219 | } |
220 | else { |
221 | block--; /* go to real header */ |
222 | type = block->d.type; |
223 | lua_assert(oldsize == block->d.size); |
224 | } |
225 | if (size == 0) { |
226 | freeblock(mc, block); |
227 | return NULL; |
228 | } |
229 | if (mc->failnext) { |
230 | mc->failnext = 0; |
231 | return NULL; /* fake a single memory allocation error */ |
232 | } |
233 | if (mc->countlimit != ~0UL && size != oldsize) { /* count limit in use? */ |
234 | if (mc->countlimit == 0) |
235 | return NULL; /* fake a memory allocation error */ |
236 | mc->countlimit--; |
237 | } |
238 | if (size > oldsize && mc->total+size-oldsize > mc->memlimit) |
239 | return NULL; /* fake a memory allocation error */ |
240 | else { |
241 | Header *newblock; |
242 | int i; |
243 | size_t commonsize = (oldsize < size) ? oldsize : size; |
244 | size_t realsize = sizeof(Header) + size + MARKSIZE; |
245 | if (realsize < size) return NULL; /* arithmetic overflow! */ |
246 | newblock = cast(Header *, malloc(realsize)); /* alloc a new block */ |
247 | if (newblock == NULL) |
248 | return NULL; /* really out of memory? */ |
249 | if (block) { |
250 | memcpy(newblock + 1, block + 1, commonsize); /* copy old contents */ |
251 | freeblock(mc, block); /* erase (and check) old copy */ |
252 | } |
253 | /* initialize new part of the block with something weird */ |
254 | fillmem(cast_charp(newblock + 1) + commonsize, size - commonsize); |
255 | /* initialize marks after block */ |
256 | for (i = 0; i < MARKSIZE; i++) |
257 | *(cast_charp(newblock + 1) + size + i) = MARK; |
258 | newblock->d.size = size; |
259 | newblock->d.type = type; |
260 | mc->total += size; |
261 | if (mc->total > mc->maxmem) |
262 | mc->maxmem = mc->total; |
263 | mc->numblocks++; |
264 | mc->objcount[type]++; |
265 | return newblock + 1; |
266 | } |
267 | } |
268 | |
269 | |
270 | /* }====================================================================== */ |
271 | |
272 | |
273 | |
274 | /* |
275 | ** {====================================================== |
276 | ** Functions to check memory consistency |
277 | ** ======================================================= |
278 | */ |
279 | |
280 | |
281 | /* |
282 | ** Check GC invariants. For incremental mode, a black object cannot |
283 | ** point to a white one. For generational mode, really old objects |
284 | ** cannot point to young objects. Both old1 and touched2 objects |
285 | ** cannot point to new objects (but can point to survivals). |
286 | ** (Threads and open upvalues, despite being marked "really old", |
287 | ** continue to be visited in all collections, and therefore can point to |
288 | ** new objects. They, and only they, are old but gray.) |
289 | */ |
290 | static int testobjref1 (global_State *g, GCObject *f, GCObject *t) { |
291 | if (isdead(g,t)) return 0; |
292 | if (issweepphase(g)) |
293 | return 1; /* no invariants */ |
294 | else if (g->gckind == KGC_INC) |
295 | return !(isblack(f) && iswhite(t)); /* basic incremental invariant */ |
296 | else { /* generational mode */ |
297 | if ((getage(f) == G_OLD && isblack(f)) && !isold(t)) |
298 | return 0; |
299 | if (((getage(f) == G_OLD1 || getage(f) == G_TOUCHED2) && isblack(f)) && |
300 | getage(t) == G_NEW) |
301 | return 0; |
302 | return 1; |
303 | } |
304 | } |
305 | |
306 | |
307 | static void printobj (global_State *g, GCObject *o) { |
308 | printf("||%s(%p)-%c%c(%02X)||" , |
309 | ttypename(novariant(o->tt)), (void *)o, |
310 | isdead(g,o) ? 'd' : isblack(o) ? 'b' : iswhite(o) ? 'w' : 'g', |
311 | "ns01oTt" [getage(o)], o->marked); |
312 | if (o->tt == LUA_VSHRSTR || o->tt == LUA_VLNGSTR) |
313 | printf(" '%s'" , getstr(gco2ts(o))); |
314 | } |
315 | |
316 | |
317 | void lua_printobj (lua_State *L, struct GCObject *o) { |
318 | printobj(G(L), o); |
319 | } |
320 | |
321 | static int testobjref (global_State *g, GCObject *f, GCObject *t) { |
322 | int r1 = testobjref1(g, f, t); |
323 | if (!r1) { |
324 | printf("%d(%02X) - " , g->gcstate, g->currentwhite); |
325 | printobj(g, f); |
326 | printf(" -> " ); |
327 | printobj(g, t); |
328 | printf("\n" ); |
329 | } |
330 | return r1; |
331 | } |
332 | |
333 | #define checkobjref(g,f,t) \ |
334 | { if (t) lua_longassert(testobjref(g,f,obj2gco(t))); } |
335 | |
336 | |
337 | static void checkvalref (global_State *g, GCObject *f, const TValue *t) { |
338 | lua_assert(!iscollectable(t) || |
339 | (righttt(t) && testobjref(g, f, gcvalue(t)))); |
340 | } |
341 | |
342 | |
343 | static void checktable (global_State *g, Table *h) { |
344 | unsigned int i; |
345 | unsigned int asize = luaH_realasize(h); |
346 | Node *n, *limit = gnode(h, sizenode(h)); |
347 | GCObject *hgc = obj2gco(h); |
348 | checkobjref(g, hgc, h->metatable); |
349 | for (i = 0; i < asize; i++) |
350 | checkvalref(g, hgc, &h->array[i]); |
351 | for (n = gnode(h, 0); n < limit; n++) { |
352 | if (!isempty(gval(n))) { |
353 | TValue k; |
354 | getnodekey(g->mainthread, &k, n); |
355 | lua_assert(!keyisnil(n)); |
356 | checkvalref(g, hgc, &k); |
357 | checkvalref(g, hgc, gval(n)); |
358 | } |
359 | } |
360 | } |
361 | |
362 | |
363 | static void checkudata (global_State *g, Udata *u) { |
364 | int i; |
365 | GCObject *hgc = obj2gco(u); |
366 | checkobjref(g, hgc, u->metatable); |
367 | for (i = 0; i < u->nuvalue; i++) |
368 | checkvalref(g, hgc, &u->uv[i].uv); |
369 | } |
370 | |
371 | |
372 | /* |
373 | ** All marks are conditional because a GC may happen while the |
374 | ** prototype is still being created |
375 | */ |
376 | static void checkproto (global_State *g, Proto *f) { |
377 | int i; |
378 | GCObject *fgc = obj2gco(f); |
379 | checkobjref(g, fgc, f->source); |
380 | for (i=0; i<f->sizek; i++) { |
381 | if (ttisstring(f->k + i)) |
382 | checkobjref(g, fgc, tsvalue(f->k + i)); |
383 | } |
384 | for (i=0; i<f->sizeupvalues; i++) |
385 | checkobjref(g, fgc, f->upvalues[i].name); |
386 | for (i=0; i<f->sizep; i++) |
387 | checkobjref(g, fgc, f->p[i]); |
388 | for (i=0; i<f->sizelocvars; i++) |
389 | checkobjref(g, fgc, f->locvars[i].varname); |
390 | } |
391 | |
392 | |
393 | static void checkCclosure (global_State *g, CClosure *cl) { |
394 | GCObject *clgc = obj2gco(cl); |
395 | int i; |
396 | for (i = 0; i < cl->nupvalues; i++) |
397 | checkvalref(g, clgc, &cl->upvalue[i]); |
398 | } |
399 | |
400 | |
401 | static void checkLclosure (global_State *g, LClosure *cl) { |
402 | GCObject *clgc = obj2gco(cl); |
403 | int i; |
404 | checkobjref(g, clgc, cl->p); |
405 | for (i=0; i<cl->nupvalues; i++) { |
406 | UpVal *uv = cl->upvals[i]; |
407 | if (uv) { |
408 | checkobjref(g, clgc, uv); |
409 | if (!upisopen(uv)) |
410 | checkvalref(g, obj2gco(uv), uv->v); |
411 | } |
412 | } |
413 | } |
414 | |
415 | |
416 | static int lua_checkpc (CallInfo *ci) { |
417 | if (!isLua(ci)) return 1; |
418 | else { |
419 | StkId f = ci->func; |
420 | Proto *p = clLvalue(s2v(f))->p; |
421 | return p->code <= ci->u.l.savedpc && |
422 | ci->u.l.savedpc <= p->code + p->sizecode; |
423 | } |
424 | } |
425 | |
426 | |
427 | static void checkstack (global_State *g, lua_State *L1) { |
428 | StkId o; |
429 | CallInfo *ci; |
430 | UpVal *uv; |
431 | lua_assert(!isdead(g, L1)); |
432 | if (L1->stack == NULL) { /* incomplete thread? */ |
433 | lua_assert(L1->openupval == NULL && L1->ci == NULL); |
434 | return; |
435 | } |
436 | for (uv = L1->openupval; uv != NULL; uv = uv->u.open.next) |
437 | lua_assert(upisopen(uv)); /* must be open */ |
438 | lua_assert(L1->top <= L1->stack_last); |
439 | for (ci = L1->ci; ci != NULL; ci = ci->previous) { |
440 | lua_assert(ci->top <= L1->stack_last); |
441 | lua_assert(lua_checkpc(ci)); |
442 | } |
443 | for (o = L1->stack; o < L1->stack_last; o++) |
444 | checkliveness(L1, s2v(o)); /* entire stack must have valid values */ |
445 | } |
446 | |
447 | |
448 | static void checkrefs (global_State *g, GCObject *o) { |
449 | switch (o->tt) { |
450 | case LUA_VUSERDATA: { |
451 | checkudata(g, gco2u(o)); |
452 | break; |
453 | } |
454 | case LUA_VUPVAL: { |
455 | checkvalref(g, o, gco2upv(o)->v); |
456 | break; |
457 | } |
458 | case LUA_VTABLE: { |
459 | checktable(g, gco2t(o)); |
460 | break; |
461 | } |
462 | case LUA_VTHREAD: { |
463 | checkstack(g, gco2th(o)); |
464 | break; |
465 | } |
466 | case LUA_VLCL: { |
467 | checkLclosure(g, gco2lcl(o)); |
468 | break; |
469 | } |
470 | case LUA_VCCL: { |
471 | checkCclosure(g, gco2ccl(o)); |
472 | break; |
473 | } |
474 | case LUA_VPROTO: { |
475 | checkproto(g, gco2p(o)); |
476 | break; |
477 | } |
478 | case LUA_VSHRSTR: |
479 | case LUA_VLNGSTR: { |
480 | lua_assert(!isgray(o)); /* strings are never gray */ |
481 | break; |
482 | } |
483 | default: lua_assert(0); |
484 | } |
485 | } |
486 | |
487 | |
488 | /* |
489 | ** Check consistency of an object: |
490 | ** - Dead objects can only happen in the 'allgc' list during a sweep |
491 | ** phase (controlled by the caller through 'maybedead'). |
492 | ** - During pause, all objects must be white. |
493 | ** - In generational mode: |
494 | ** * objects must be old enough for their lists ('listage'). |
495 | ** * old objects cannot be white. |
496 | ** * old objects must be black, except for 'touched1', 'old0', |
497 | ** threads, and open upvalues. |
498 | */ |
499 | static void checkobject (global_State *g, GCObject *o, int maybedead, |
500 | int listage) { |
501 | if (isdead(g, o)) |
502 | lua_assert(maybedead); |
503 | else { |
504 | lua_assert(g->gcstate != GCSpause || iswhite(o)); |
505 | if (g->gckind == KGC_GEN) { /* generational mode? */ |
506 | lua_assert(getage(o) >= listage); |
507 | lua_assert(!iswhite(o) || !isold(o)); |
508 | if (isold(o)) { |
509 | lua_assert(isblack(o) || |
510 | getage(o) == G_TOUCHED1 || |
511 | getage(o) == G_OLD0 || |
512 | o->tt == LUA_VTHREAD || |
513 | (o->tt == LUA_VUPVAL && upisopen(gco2upv(o)))); |
514 | } |
515 | } |
516 | checkrefs(g, o); |
517 | } |
518 | } |
519 | |
520 | |
521 | static lu_mem checkgraylist (global_State *g, GCObject *o) { |
522 | int total = 0; /* count number of elements in the list */ |
523 | ((void)g); /* better to keep it available if we need to print an object */ |
524 | while (o) { |
525 | lua_assert(!!isgray(o) ^ (getage(o) == G_TOUCHED2)); |
526 | lua_assert(!testbit(o->marked, TESTBIT)); |
527 | if (keepinvariant(g)) |
528 | l_setbit(o->marked, TESTBIT); /* mark that object is in a gray list */ |
529 | total++; |
530 | switch (o->tt) { |
531 | case LUA_VTABLE: o = gco2t(o)->gclist; break; |
532 | case LUA_VLCL: o = gco2lcl(o)->gclist; break; |
533 | case LUA_VCCL: o = gco2ccl(o)->gclist; break; |
534 | case LUA_VTHREAD: o = gco2th(o)->gclist; break; |
535 | case LUA_VPROTO: o = gco2p(o)->gclist; break; |
536 | case LUA_VUSERDATA: |
537 | lua_assert(gco2u(o)->nuvalue > 0); |
538 | o = gco2u(o)->gclist; |
539 | break; |
540 | default: lua_assert(0); /* other objects cannot be in a gray list */ |
541 | } |
542 | } |
543 | return total; |
544 | } |
545 | |
546 | |
547 | /* |
548 | ** Check objects in gray lists. |
549 | */ |
550 | static lu_mem checkgrays (global_State *g) { |
551 | int total = 0; /* count number of elements in all lists */ |
552 | if (!keepinvariant(g)) return total; |
553 | total += checkgraylist(g, g->gray); |
554 | total += checkgraylist(g, g->grayagain); |
555 | total += checkgraylist(g, g->weak); |
556 | total += checkgraylist(g, g->allweak); |
557 | total += checkgraylist(g, g->ephemeron); |
558 | return total; |
559 | } |
560 | |
561 | |
562 | /* |
563 | ** Check whether 'o' should be in a gray list. If so, increment |
564 | ** 'count' and check its TESTBIT. (It must have been previously set by |
565 | ** 'checkgraylist'.) |
566 | */ |
567 | static void incifingray (global_State *g, GCObject *o, lu_mem *count) { |
568 | if (!keepinvariant(g)) |
569 | return; /* gray lists not being kept in these phases */ |
570 | if (o->tt == LUA_VUPVAL) { |
571 | /* only open upvalues can be gray */ |
572 | lua_assert(!isgray(o) || upisopen(gco2upv(o))); |
573 | return; /* upvalues are never in gray lists */ |
574 | } |
575 | /* these are the ones that must be in gray lists */ |
576 | if (isgray(o) || getage(o) == G_TOUCHED2) { |
577 | (*count)++; |
578 | lua_assert(testbit(o->marked, TESTBIT)); |
579 | resetbit(o->marked, TESTBIT); /* prepare for next cycle */ |
580 | } |
581 | } |
582 | |
583 | |
584 | static lu_mem checklist (global_State *g, int maybedead, int tof, |
585 | GCObject *newl, GCObject *survival, GCObject *old, GCObject *reallyold) { |
586 | GCObject *o; |
587 | lu_mem total = 0; /* number of object that should be in gray lists */ |
588 | for (o = newl; o != survival; o = o->next) { |
589 | checkobject(g, o, maybedead, G_NEW); |
590 | incifingray(g, o, &total); |
591 | lua_assert(!tof == !tofinalize(o)); |
592 | } |
593 | for (o = survival; o != old; o = o->next) { |
594 | checkobject(g, o, 0, G_SURVIVAL); |
595 | incifingray(g, o, &total); |
596 | lua_assert(!tof == !tofinalize(o)); |
597 | } |
598 | for (o = old; o != reallyold; o = o->next) { |
599 | checkobject(g, o, 0, G_OLD1); |
600 | incifingray(g, o, &total); |
601 | lua_assert(!tof == !tofinalize(o)); |
602 | } |
603 | for (o = reallyold; o != NULL; o = o->next) { |
604 | checkobject(g, o, 0, G_OLD); |
605 | incifingray(g, o, &total); |
606 | lua_assert(!tof == !tofinalize(o)); |
607 | } |
608 | return total; |
609 | } |
610 | |
611 | |
612 | int lua_checkmemory (lua_State *L) { |
613 | global_State *g = G(L); |
614 | GCObject *o; |
615 | int maybedead; |
616 | lu_mem totalin; /* total of objects that are in gray lists */ |
617 | lu_mem totalshould; /* total of objects that should be in gray lists */ |
618 | if (keepinvariant(g)) { |
619 | lua_assert(!iswhite(g->mainthread)); |
620 | lua_assert(!iswhite(gcvalue(&g->l_registry))); |
621 | } |
622 | lua_assert(!isdead(g, gcvalue(&g->l_registry))); |
623 | lua_assert(g->sweepgc == NULL || issweepphase(g)); |
624 | totalin = checkgrays(g); |
625 | |
626 | /* check 'fixedgc' list */ |
627 | for (o = g->fixedgc; o != NULL; o = o->next) { |
628 | lua_assert(o->tt == LUA_VSHRSTR && isgray(o) && getage(o) == G_OLD); |
629 | } |
630 | |
631 | /* check 'allgc' list */ |
632 | maybedead = (GCSatomic < g->gcstate && g->gcstate <= GCSswpallgc); |
633 | totalshould = checklist(g, maybedead, 0, g->allgc, |
634 | g->survival, g->old1, g->reallyold); |
635 | |
636 | /* check 'finobj' list */ |
637 | totalshould += checklist(g, 0, 1, g->finobj, |
638 | g->finobjsur, g->finobjold1, g->finobjrold); |
639 | |
640 | /* check 'tobefnz' list */ |
641 | for (o = g->tobefnz; o != NULL; o = o->next) { |
642 | checkobject(g, o, 0, G_NEW); |
643 | incifingray(g, o, &totalshould); |
644 | lua_assert(tofinalize(o)); |
645 | lua_assert(o->tt == LUA_VUSERDATA || o->tt == LUA_VTABLE); |
646 | } |
647 | if (keepinvariant(g)) |
648 | lua_assert(totalin == totalshould); |
649 | return 0; |
650 | } |
651 | |
652 | /* }====================================================== */ |
653 | |
654 | |
655 | |
656 | /* |
657 | ** {====================================================== |
658 | ** Disassembler |
659 | ** ======================================================= |
660 | */ |
661 | |
662 | |
663 | static char *buildop (Proto *p, int pc, char *buff) { |
664 | char *obuff = buff; |
665 | Instruction i = p->code[pc]; |
666 | OpCode o = GET_OPCODE(i); |
667 | const char *name = opnames[o]; |
668 | int line = luaG_getfuncline(p, pc); |
669 | int lineinfo = (p->lineinfo != NULL) ? p->lineinfo[pc] : 0; |
670 | if (lineinfo == ABSLINEINFO) |
671 | buff += sprintf(buff, "(__" ); |
672 | else |
673 | buff += sprintf(buff, "(%2d" , lineinfo); |
674 | buff += sprintf(buff, " - %4d) %4d - " , line, pc); |
675 | switch (getOpMode(o)) { |
676 | case iABC: |
677 | sprintf(buff, "%-12s%4d %4d %4d%s" , name, |
678 | GETARG_A(i), GETARG_B(i), GETARG_C(i), |
679 | GETARG_k(i) ? " (k)" : "" ); |
680 | break; |
681 | case iABx: |
682 | sprintf(buff, "%-12s%4d %4d" , name, GETARG_A(i), GETARG_Bx(i)); |
683 | break; |
684 | case iAsBx: |
685 | sprintf(buff, "%-12s%4d %4d" , name, GETARG_A(i), GETARG_sBx(i)); |
686 | break; |
687 | case iAx: |
688 | sprintf(buff, "%-12s%4d" , name, GETARG_Ax(i)); |
689 | break; |
690 | case isJ: |
691 | sprintf(buff, "%-12s%4d" , name, GETARG_sJ(i)); |
692 | break; |
693 | } |
694 | return obuff; |
695 | } |
696 | |
697 | |
698 | #if 0 |
699 | void luaI_printcode (Proto *pt, int size) { |
700 | int pc; |
701 | for (pc=0; pc<size; pc++) { |
702 | char buff[100]; |
703 | printf("%s\n" , buildop(pt, pc, buff)); |
704 | } |
705 | printf("-------\n" ); |
706 | } |
707 | |
708 | |
709 | void luaI_printinst (Proto *pt, int pc) { |
710 | char buff[100]; |
711 | printf("%s\n" , buildop(pt, pc, buff)); |
712 | } |
713 | #endif |
714 | |
715 | |
716 | static int listcode (lua_State *L) { |
717 | int pc; |
718 | Proto *p; |
719 | luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), |
720 | 1, "Lua function expected" ); |
721 | p = getproto(obj_at(L, 1)); |
722 | lua_newtable(L); |
723 | setnameval(L, "maxstack" , p->maxstacksize); |
724 | setnameval(L, "numparams" , p->numparams); |
725 | for (pc=0; pc<p->sizecode; pc++) { |
726 | char buff[100]; |
727 | lua_pushinteger(L, pc+1); |
728 | lua_pushstring(L, buildop(p, pc, buff)); |
729 | lua_settable(L, -3); |
730 | } |
731 | return 1; |
732 | } |
733 | |
734 | |
735 | static int printcode (lua_State *L) { |
736 | int pc; |
737 | Proto *p; |
738 | luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), |
739 | 1, "Lua function expected" ); |
740 | p = getproto(obj_at(L, 1)); |
741 | printf("maxstack: %d\n" , p->maxstacksize); |
742 | printf("numparams: %d\n" , p->numparams); |
743 | for (pc=0; pc<p->sizecode; pc++) { |
744 | char buff[100]; |
745 | printf("%s\n" , buildop(p, pc, buff)); |
746 | } |
747 | return 0; |
748 | } |
749 | |
750 | |
751 | static int listk (lua_State *L) { |
752 | Proto *p; |
753 | int i; |
754 | luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), |
755 | 1, "Lua function expected" ); |
756 | p = getproto(obj_at(L, 1)); |
757 | lua_createtable(L, p->sizek, 0); |
758 | for (i=0; i<p->sizek; i++) { |
759 | pushobject(L, p->k+i); |
760 | lua_rawseti(L, -2, i+1); |
761 | } |
762 | return 1; |
763 | } |
764 | |
765 | |
766 | static int listabslineinfo (lua_State *L) { |
767 | Proto *p; |
768 | int i; |
769 | luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), |
770 | 1, "Lua function expected" ); |
771 | p = getproto(obj_at(L, 1)); |
772 | luaL_argcheck(L, p->abslineinfo != NULL, 1, "function has no debug info" ); |
773 | lua_createtable(L, 2 * p->sizeabslineinfo, 0); |
774 | for (i=0; i < p->sizeabslineinfo; i++) { |
775 | lua_pushinteger(L, p->abslineinfo[i].pc); |
776 | lua_rawseti(L, -2, 2 * i + 1); |
777 | lua_pushinteger(L, p->abslineinfo[i].line); |
778 | lua_rawseti(L, -2, 2 * i + 2); |
779 | } |
780 | return 1; |
781 | } |
782 | |
783 | |
784 | static int listlocals (lua_State *L) { |
785 | Proto *p; |
786 | int pc = cast_int(luaL_checkinteger(L, 2)) - 1; |
787 | int i = 0; |
788 | const char *name; |
789 | luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), |
790 | 1, "Lua function expected" ); |
791 | p = getproto(obj_at(L, 1)); |
792 | while ((name = luaF_getlocalname(p, ++i, pc)) != NULL) |
793 | lua_pushstring(L, name); |
794 | return i-1; |
795 | } |
796 | |
797 | /* }====================================================== */ |
798 | |
799 | |
800 | |
801 | static void printstack (lua_State *L) { |
802 | int i; |
803 | int n = lua_gettop(L); |
804 | printf("stack: >>\n" ); |
805 | for (i = 1; i <= n; i++) { |
806 | printf("%3d: %s\n" , i, luaL_tolstring(L, i, NULL)); |
807 | lua_pop(L, 1); |
808 | } |
809 | printf("<<\n" ); |
810 | } |
811 | |
812 | |
813 | static int get_limits (lua_State *L) { |
814 | lua_createtable(L, 0, 6); |
815 | setnameval(L, "IS32INT" , LUAI_IS32INT); |
816 | setnameval(L, "MAXARG_Ax" , MAXARG_Ax); |
817 | setnameval(L, "MAXARG_Bx" , MAXARG_Bx); |
818 | setnameval(L, "OFFSET_sBx" , OFFSET_sBx); |
819 | setnameval(L, "LFPF" , LFIELDS_PER_FLUSH); |
820 | setnameval(L, "NUM_OPCODES" , NUM_OPCODES); |
821 | return 1; |
822 | } |
823 | |
824 | |
825 | static int mem_query (lua_State *L) { |
826 | if (lua_isnone(L, 1)) { |
827 | lua_pushinteger(L, l_memcontrol.total); |
828 | lua_pushinteger(L, l_memcontrol.numblocks); |
829 | lua_pushinteger(L, l_memcontrol.maxmem); |
830 | return 3; |
831 | } |
832 | else if (lua_isnumber(L, 1)) { |
833 | unsigned long limit = cast(unsigned long, luaL_checkinteger(L, 1)); |
834 | if (limit == 0) limit = ULONG_MAX; |
835 | l_memcontrol.memlimit = limit; |
836 | return 0; |
837 | } |
838 | else { |
839 | const char *t = luaL_checkstring(L, 1); |
840 | int i; |
841 | for (i = LUA_NUMTAGS - 1; i >= 0; i--) { |
842 | if (strcmp(t, ttypename(i)) == 0) { |
843 | lua_pushinteger(L, l_memcontrol.objcount[i]); |
844 | return 1; |
845 | } |
846 | } |
847 | return luaL_error(L, "unknown type '%s'" , t); |
848 | } |
849 | } |
850 | |
851 | |
852 | static int alloc_count (lua_State *L) { |
853 | if (lua_isnone(L, 1)) |
854 | l_memcontrol.countlimit = ~0L; |
855 | else |
856 | l_memcontrol.countlimit = luaL_checkinteger(L, 1); |
857 | return 0; |
858 | } |
859 | |
860 | |
861 | static int alloc_failnext (lua_State *L) { |
862 | UNUSED(L); |
863 | l_memcontrol.failnext = 1; |
864 | return 0; |
865 | } |
866 | |
867 | |
868 | static int settrick (lua_State *L) { |
869 | if (ttisnil(obj_at(L, 1))) |
870 | l_Trick = NULL; |
871 | else |
872 | l_Trick = gcvalue(obj_at(L, 1)); |
873 | return 0; |
874 | } |
875 | |
876 | |
877 | static int gc_color (lua_State *L) { |
878 | TValue *o; |
879 | luaL_checkany(L, 1); |
880 | o = obj_at(L, 1); |
881 | if (!iscollectable(o)) |
882 | lua_pushstring(L, "no collectable" ); |
883 | else { |
884 | GCObject *obj = gcvalue(o); |
885 | lua_pushstring(L, isdead(G(L), obj) ? "dead" : |
886 | iswhite(obj) ? "white" : |
887 | isblack(obj) ? "black" : "gray" ); |
888 | } |
889 | return 1; |
890 | } |
891 | |
892 | |
893 | static int gc_age (lua_State *L) { |
894 | TValue *o; |
895 | luaL_checkany(L, 1); |
896 | o = obj_at(L, 1); |
897 | if (!iscollectable(o)) |
898 | lua_pushstring(L, "no collectable" ); |
899 | else { |
900 | static const char *gennames[] = {"new" , "survival" , "old0" , "old1" , |
901 | "old" , "touched1" , "touched2" }; |
902 | GCObject *obj = gcvalue(o); |
903 | lua_pushstring(L, gennames[getage(obj)]); |
904 | } |
905 | return 1; |
906 | } |
907 | |
908 | |
909 | static int gc_printobj (lua_State *L) { |
910 | TValue *o; |
911 | luaL_checkany(L, 1); |
912 | o = obj_at(L, 1); |
913 | if (!iscollectable(o)) |
914 | printf("no collectable\n" ); |
915 | else { |
916 | GCObject *obj = gcvalue(o); |
917 | printobj(G(L), obj); |
918 | printf("\n" ); |
919 | } |
920 | return 0; |
921 | } |
922 | |
923 | |
924 | static int gc_state (lua_State *L) { |
925 | static const char *statenames[] = { |
926 | "propagate" , "atomic" , "enteratomic" , "sweepallgc" , "sweepfinobj" , |
927 | "sweeptobefnz" , "sweepend" , "callfin" , "pause" , "" }; |
928 | static const int states[] = { |
929 | GCSpropagate, GCSenteratomic, GCSatomic, GCSswpallgc, GCSswpfinobj, |
930 | GCSswptobefnz, GCSswpend, GCScallfin, GCSpause, -1}; |
931 | int option = states[luaL_checkoption(L, 1, "" , statenames)]; |
932 | if (option == -1) { |
933 | lua_pushstring(L, statenames[G(L)->gcstate]); |
934 | return 1; |
935 | } |
936 | else { |
937 | global_State *g = G(L); |
938 | if (G(L)->gckind == KGC_GEN) |
939 | luaL_error(L, "cannot change states in generational mode" ); |
940 | lua_lock(L); |
941 | if (option < g->gcstate) { /* must cross 'pause'? */ |
942 | luaC_runtilstate(L, bitmask(GCSpause)); /* run until pause */ |
943 | } |
944 | luaC_runtilstate(L, bitmask(option)); |
945 | lua_assert(G(L)->gcstate == option); |
946 | lua_unlock(L); |
947 | return 0; |
948 | } |
949 | } |
950 | |
951 | |
952 | static int hash_query (lua_State *L) { |
953 | if (lua_isnone(L, 2)) { |
954 | luaL_argcheck(L, lua_type(L, 1) == LUA_TSTRING, 1, "string expected" ); |
955 | lua_pushinteger(L, tsvalue(obj_at(L, 1))->hash); |
956 | } |
957 | else { |
958 | TValue *o = obj_at(L, 1); |
959 | Table *t; |
960 | luaL_checktype(L, 2, LUA_TTABLE); |
961 | t = hvalue(obj_at(L, 2)); |
962 | lua_pushinteger(L, luaH_mainposition(t, o) - t->node); |
963 | } |
964 | return 1; |
965 | } |
966 | |
967 | |
968 | static int stacklevel (lua_State *L) { |
969 | unsigned long a = 0; |
970 | lua_pushinteger(L, (L->top - L->stack)); |
971 | lua_pushinteger(L, stacksize(L)); |
972 | lua_pushinteger(L, L->nCcalls); |
973 | lua_pushinteger(L, L->nci); |
974 | lua_pushinteger(L, (unsigned long)&a); |
975 | return 5; |
976 | } |
977 | |
978 | |
979 | static int table_query (lua_State *L) { |
980 | const Table *t; |
981 | int i = cast_int(luaL_optinteger(L, 2, -1)); |
982 | unsigned int asize; |
983 | luaL_checktype(L, 1, LUA_TTABLE); |
984 | t = hvalue(obj_at(L, 1)); |
985 | asize = luaH_realasize(t); |
986 | if (i == -1) { |
987 | lua_pushinteger(L, asize); |
988 | lua_pushinteger(L, allocsizenode(t)); |
989 | lua_pushinteger(L, isdummy(t) ? 0 : t->lastfree - t->node); |
990 | lua_pushinteger(L, t->alimit); |
991 | return 4; |
992 | } |
993 | else if ((unsigned int)i < asize) { |
994 | lua_pushinteger(L, i); |
995 | pushobject(L, &t->array[i]); |
996 | lua_pushnil(L); |
997 | } |
998 | else if ((i -= asize) < sizenode(t)) { |
999 | TValue k; |
1000 | getnodekey(L, &k, gnode(t, i)); |
1001 | if (!isempty(gval(gnode(t, i))) || |
1002 | ttisnil(&k) || |
1003 | ttisnumber(&k)) { |
1004 | pushobject(L, &k); |
1005 | } |
1006 | else |
1007 | lua_pushliteral(L, "<undef>" ); |
1008 | pushobject(L, gval(gnode(t, i))); |
1009 | if (gnext(&t->node[i]) != 0) |
1010 | lua_pushinteger(L, gnext(&t->node[i])); |
1011 | else |
1012 | lua_pushnil(L); |
1013 | } |
1014 | return 3; |
1015 | } |
1016 | |
1017 | |
1018 | static int string_query (lua_State *L) { |
1019 | stringtable *tb = &G(L)->strt; |
1020 | int s = cast_int(luaL_optinteger(L, 1, 0)) - 1; |
1021 | if (s == -1) { |
1022 | lua_pushinteger(L ,tb->size); |
1023 | lua_pushinteger(L ,tb->nuse); |
1024 | return 2; |
1025 | } |
1026 | else if (s < tb->size) { |
1027 | TString *ts; |
1028 | int n = 0; |
1029 | for (ts = tb->hash[s]; ts != NULL; ts = ts->u.hnext) { |
1030 | setsvalue2s(L, L->top, ts); |
1031 | api_incr_top(L); |
1032 | n++; |
1033 | } |
1034 | return n; |
1035 | } |
1036 | else return 0; |
1037 | } |
1038 | |
1039 | |
1040 | static int tref (lua_State *L) { |
1041 | int level = lua_gettop(L); |
1042 | luaL_checkany(L, 1); |
1043 | lua_pushvalue(L, 1); |
1044 | lua_pushinteger(L, luaL_ref(L, LUA_REGISTRYINDEX)); |
1045 | lua_assert(lua_gettop(L) == level+1); /* +1 for result */ |
1046 | return 1; |
1047 | } |
1048 | |
1049 | static int getref (lua_State *L) { |
1050 | int level = lua_gettop(L); |
1051 | lua_rawgeti(L, LUA_REGISTRYINDEX, luaL_checkinteger(L, 1)); |
1052 | lua_assert(lua_gettop(L) == level+1); |
1053 | return 1; |
1054 | } |
1055 | |
1056 | static int unref (lua_State *L) { |
1057 | int level = lua_gettop(L); |
1058 | luaL_unref(L, LUA_REGISTRYINDEX, cast_int(luaL_checkinteger(L, 1))); |
1059 | lua_assert(lua_gettop(L) == level); |
1060 | return 0; |
1061 | } |
1062 | |
1063 | |
1064 | static int upvalue (lua_State *L) { |
1065 | int n = cast_int(luaL_checkinteger(L, 2)); |
1066 | luaL_checktype(L, 1, LUA_TFUNCTION); |
1067 | if (lua_isnone(L, 3)) { |
1068 | const char *name = lua_getupvalue(L, 1, n); |
1069 | if (name == NULL) return 0; |
1070 | lua_pushstring(L, name); |
1071 | return 2; |
1072 | } |
1073 | else { |
1074 | const char *name = lua_setupvalue(L, 1, n); |
1075 | lua_pushstring(L, name); |
1076 | return 1; |
1077 | } |
1078 | } |
1079 | |
1080 | |
1081 | static int newuserdata (lua_State *L) { |
1082 | size_t size = cast_sizet(luaL_optinteger(L, 1, 0)); |
1083 | int nuv = luaL_optinteger(L, 2, 0); |
1084 | char *p = cast_charp(lua_newuserdatauv(L, size, nuv)); |
1085 | while (size--) *p++ = '\0'; |
1086 | return 1; |
1087 | } |
1088 | |
1089 | |
1090 | static int pushuserdata (lua_State *L) { |
1091 | lua_Integer u = luaL_checkinteger(L, 1); |
1092 | lua_pushlightuserdata(L, cast_voidp(cast_sizet(u))); |
1093 | return 1; |
1094 | } |
1095 | |
1096 | |
1097 | static int udataval (lua_State *L) { |
1098 | lua_pushinteger(L, cast(long, lua_touserdata(L, 1))); |
1099 | return 1; |
1100 | } |
1101 | |
1102 | |
1103 | static int doonnewstack (lua_State *L) { |
1104 | lua_State *L1 = lua_newthread(L); |
1105 | size_t l; |
1106 | const char *s = luaL_checklstring(L, 1, &l); |
1107 | int status = luaL_loadbuffer(L1, s, l, s); |
1108 | if (status == LUA_OK) |
1109 | status = lua_pcall(L1, 0, 0, 0); |
1110 | lua_pushinteger(L, status); |
1111 | return 1; |
1112 | } |
1113 | |
1114 | |
1115 | static int s2d (lua_State *L) { |
1116 | lua_pushnumber(L, cast_num(*cast(const double *, luaL_checkstring(L, 1)))); |
1117 | return 1; |
1118 | } |
1119 | |
1120 | |
1121 | static int d2s (lua_State *L) { |
1122 | double d = cast(double, luaL_checknumber(L, 1)); |
1123 | lua_pushlstring(L, cast_charp(&d), sizeof(d)); |
1124 | return 1; |
1125 | } |
1126 | |
1127 | |
1128 | static int num2int (lua_State *L) { |
1129 | lua_pushinteger(L, lua_tointeger(L, 1)); |
1130 | return 1; |
1131 | } |
1132 | |
1133 | |
1134 | static int newstate (lua_State *L) { |
1135 | void *ud; |
1136 | lua_Alloc f = lua_getallocf(L, &ud); |
1137 | lua_State *L1 = lua_newstate(f, ud); |
1138 | if (L1) { |
1139 | lua_atpanic(L1, tpanic); |
1140 | lua_pushlightuserdata(L, L1); |
1141 | } |
1142 | else |
1143 | lua_pushnil(L); |
1144 | return 1; |
1145 | } |
1146 | |
1147 | |
1148 | static lua_State *getstate (lua_State *L) { |
1149 | lua_State *L1 = cast(lua_State *, lua_touserdata(L, 1)); |
1150 | luaL_argcheck(L, L1 != NULL, 1, "state expected" ); |
1151 | return L1; |
1152 | } |
1153 | |
1154 | |
1155 | static int loadlib (lua_State *L) { |
1156 | static const luaL_Reg libs[] = { |
1157 | {LUA_GNAME, luaopen_base}, |
1158 | {"coroutine" , luaopen_coroutine}, |
1159 | {"debug" , luaopen_debug}, |
1160 | {"io" , luaopen_io}, |
1161 | {"os" , luaopen_os}, |
1162 | {"math" , luaopen_math}, |
1163 | {"string" , luaopen_string}, |
1164 | {"table" , luaopen_table}, |
1165 | {"T" , luaB_opentests}, |
1166 | {NULL, NULL} |
1167 | }; |
1168 | lua_State *L1 = getstate(L); |
1169 | int i; |
1170 | luaL_requiref(L1, "package" , luaopen_package, 0); |
1171 | lua_assert(lua_type(L1, -1) == LUA_TTABLE); |
1172 | /* 'requiref' should not reload module already loaded... */ |
1173 | luaL_requiref(L1, "package" , NULL, 1); /* seg. fault if it reloads */ |
1174 | /* ...but should return the same module */ |
1175 | lua_assert(lua_compare(L1, -1, -2, LUA_OPEQ)); |
1176 | luaL_getsubtable(L1, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); |
1177 | for (i = 0; libs[i].name; i++) { |
1178 | lua_pushcfunction(L1, libs[i].func); |
1179 | lua_setfield(L1, -2, libs[i].name); |
1180 | } |
1181 | return 0; |
1182 | } |
1183 | |
1184 | static int closestate (lua_State *L) { |
1185 | lua_State *L1 = getstate(L); |
1186 | lua_close(L1); |
1187 | return 0; |
1188 | } |
1189 | |
1190 | static int doremote (lua_State *L) { |
1191 | lua_State *L1 = getstate(L); |
1192 | size_t lcode; |
1193 | const char *code = luaL_checklstring(L, 2, &lcode); |
1194 | int status; |
1195 | lua_settop(L1, 0); |
1196 | status = luaL_loadbuffer(L1, code, lcode, code); |
1197 | if (status == LUA_OK) |
1198 | status = lua_pcall(L1, 0, LUA_MULTRET, 0); |
1199 | if (status != LUA_OK) { |
1200 | lua_pushnil(L); |
1201 | lua_pushstring(L, lua_tostring(L1, -1)); |
1202 | lua_pushinteger(L, status); |
1203 | return 3; |
1204 | } |
1205 | else { |
1206 | int i = 0; |
1207 | while (!lua_isnone(L1, ++i)) |
1208 | lua_pushstring(L, lua_tostring(L1, i)); |
1209 | lua_pop(L1, i-1); |
1210 | return i-1; |
1211 | } |
1212 | } |
1213 | |
1214 | |
1215 | static int log2_aux (lua_State *L) { |
1216 | unsigned int x = (unsigned int)luaL_checkinteger(L, 1); |
1217 | lua_pushinteger(L, luaO_ceillog2(x)); |
1218 | return 1; |
1219 | } |
1220 | |
1221 | |
1222 | struct Aux { jmp_buf jb; const char *paniccode; lua_State *L; }; |
1223 | |
1224 | /* |
1225 | ** does a long-jump back to "main program". |
1226 | */ |
1227 | static int panicback (lua_State *L) { |
1228 | struct Aux *b; |
1229 | lua_checkstack(L, 1); /* open space for 'Aux' struct */ |
1230 | lua_getfield(L, LUA_REGISTRYINDEX, "_jmpbuf" ); /* get 'Aux' struct */ |
1231 | b = (struct Aux *)lua_touserdata(L, -1); |
1232 | lua_pop(L, 1); /* remove 'Aux' struct */ |
1233 | runC(b->L, L, b->paniccode); /* run optional panic code */ |
1234 | longjmp(b->jb, 1); |
1235 | return 1; /* to avoid warnings */ |
1236 | } |
1237 | |
1238 | static int checkpanic (lua_State *L) { |
1239 | struct Aux b; |
1240 | void *ud; |
1241 | lua_State *L1; |
1242 | const char *code = luaL_checkstring(L, 1); |
1243 | lua_Alloc f = lua_getallocf(L, &ud); |
1244 | b.paniccode = luaL_optstring(L, 2, "" ); |
1245 | b.L = L; |
1246 | L1 = lua_newstate(f, ud); /* create new state */ |
1247 | if (L1 == NULL) { /* error? */ |
1248 | lua_pushnil(L); |
1249 | return 1; |
1250 | } |
1251 | lua_atpanic(L1, panicback); /* set its panic function */ |
1252 | lua_pushlightuserdata(L1, &b); |
1253 | lua_setfield(L1, LUA_REGISTRYINDEX, "_jmpbuf" ); /* store 'Aux' struct */ |
1254 | if (setjmp(b.jb) == 0) { /* set jump buffer */ |
1255 | runC(L, L1, code); /* run code unprotected */ |
1256 | lua_pushliteral(L, "no errors" ); |
1257 | } |
1258 | else { /* error handling */ |
1259 | /* move error message to original state */ |
1260 | lua_pushstring(L, lua_tostring(L1, -1)); |
1261 | } |
1262 | lua_close(L1); |
1263 | return 1; |
1264 | } |
1265 | |
1266 | |
1267 | |
1268 | /* |
1269 | ** {==================================================================== |
1270 | ** function to test the API with C. It interprets a kind of assembler |
1271 | ** language with calls to the API, so the test can be driven by Lua code |
1272 | ** ===================================================================== |
1273 | */ |
1274 | |
1275 | |
1276 | static void sethookaux (lua_State *L, int mask, int count, const char *code); |
1277 | |
1278 | static const char *const delimits = " \t\n,;" ; |
1279 | |
1280 | static void skip (const char **pc) { |
1281 | for (;;) { |
1282 | if (**pc != '\0' && strchr(delimits, **pc)) (*pc)++; |
1283 | else if (**pc == '#') { /* comment? */ |
1284 | while (**pc != '\n' && **pc != '\0') (*pc)++; /* until end-of-line */ |
1285 | } |
1286 | else break; |
1287 | } |
1288 | } |
1289 | |
1290 | static int getnum_aux (lua_State *L, lua_State *L1, const char **pc) { |
1291 | int res = 0; |
1292 | int sig = 1; |
1293 | skip(pc); |
1294 | if (**pc == '.') { |
1295 | res = cast_int(lua_tointeger(L1, -1)); |
1296 | lua_pop(L1, 1); |
1297 | (*pc)++; |
1298 | return res; |
1299 | } |
1300 | else if (**pc == '*') { |
1301 | res = lua_gettop(L1); |
1302 | (*pc)++; |
1303 | return res; |
1304 | } |
1305 | else if (**pc == '-') { |
1306 | sig = -1; |
1307 | (*pc)++; |
1308 | } |
1309 | if (!lisdigit(cast_uchar(**pc))) |
1310 | luaL_error(L, "number expected (%s)" , *pc); |
1311 | while (lisdigit(cast_uchar(**pc))) res = res*10 + (*(*pc)++) - '0'; |
1312 | return sig*res; |
1313 | } |
1314 | |
1315 | static const char *getstring_aux (lua_State *L, char *buff, const char **pc) { |
1316 | int i = 0; |
1317 | skip(pc); |
1318 | if (**pc == '"' || **pc == '\'') { /* quoted string? */ |
1319 | int quote = *(*pc)++; |
1320 | while (**pc != quote) { |
1321 | if (**pc == '\0') luaL_error(L, "unfinished string in C script" ); |
1322 | buff[i++] = *(*pc)++; |
1323 | } |
1324 | (*pc)++; |
1325 | } |
1326 | else { |
1327 | while (**pc != '\0' && !strchr(delimits, **pc)) |
1328 | buff[i++] = *(*pc)++; |
1329 | } |
1330 | buff[i] = '\0'; |
1331 | return buff; |
1332 | } |
1333 | |
1334 | |
1335 | static int getindex_aux (lua_State *L, lua_State *L1, const char **pc) { |
1336 | skip(pc); |
1337 | switch (*(*pc)++) { |
1338 | case 'R': return LUA_REGISTRYINDEX; |
1339 | case 'G': return luaL_error(L, "deprecated index 'G'" ); |
1340 | case 'U': return lua_upvalueindex(getnum_aux(L, L1, pc)); |
1341 | default: (*pc)--; return getnum_aux(L, L1, pc); |
1342 | } |
1343 | } |
1344 | |
1345 | |
1346 | static const char *const statcodes[] = {"OK" , "YIELD" , "ERRRUN" , |
1347 | "ERRSYNTAX" , MEMERRMSG, "ERRGCMM" , "ERRERR" }; |
1348 | |
1349 | /* |
1350 | ** Avoid these stat codes from being collected, to avoid possible |
1351 | ** memory error when pushing them. |
1352 | */ |
1353 | static void regcodes (lua_State *L) { |
1354 | unsigned int i; |
1355 | for (i = 0; i < sizeof(statcodes) / sizeof(statcodes[0]); i++) { |
1356 | lua_pushboolean(L, 1); |
1357 | lua_setfield(L, LUA_REGISTRYINDEX, statcodes[i]); |
1358 | } |
1359 | } |
1360 | |
1361 | |
1362 | #define EQ(s1) (strcmp(s1, inst) == 0) |
1363 | |
1364 | #define getnum (getnum_aux(L, L1, &pc)) |
1365 | #define getstring (getstring_aux(L, buff, &pc)) |
1366 | #define getindex (getindex_aux(L, L1, &pc)) |
1367 | |
1368 | |
1369 | static int testC (lua_State *L); |
1370 | static int Cfunck (lua_State *L, int status, lua_KContext ctx); |
1371 | |
1372 | /* |
1373 | ** arithmetic operation encoding for 'arith' instruction |
1374 | ** LUA_OPIDIV -> \ |
1375 | ** LUA_OPSHL -> < |
1376 | ** LUA_OPSHR -> > |
1377 | ** LUA_OPUNM -> _ |
1378 | ** LUA_OPBNOT -> ! |
1379 | */ |
1380 | static const char ops[] = "+-*%^/\\&|~<>_!" ; |
1381 | |
1382 | static int runC (lua_State *L, lua_State *L1, const char *pc) { |
1383 | char buff[300]; |
1384 | int status = 0; |
1385 | if (pc == NULL) return luaL_error(L, "attempt to runC null script" ); |
1386 | for (;;) { |
1387 | const char *inst = getstring; |
1388 | if EQ("" ) return 0; |
1389 | else if EQ("absindex" ) { |
1390 | lua_pushnumber(L1, lua_absindex(L1, getindex)); |
1391 | } |
1392 | else if EQ("append" ) { |
1393 | int t = getindex; |
1394 | int i = lua_rawlen(L1, t); |
1395 | lua_rawseti(L1, t, i + 1); |
1396 | } |
1397 | else if EQ("arith" ) { |
1398 | int op; |
1399 | skip(&pc); |
1400 | op = strchr(ops, *pc++) - ops; |
1401 | lua_arith(L1, op); |
1402 | } |
1403 | else if EQ("call" ) { |
1404 | int narg = getnum; |
1405 | int nres = getnum; |
1406 | lua_call(L1, narg, nres); |
1407 | } |
1408 | else if EQ("callk" ) { |
1409 | int narg = getnum; |
1410 | int nres = getnum; |
1411 | int i = getindex; |
1412 | lua_callk(L1, narg, nres, i, Cfunck); |
1413 | } |
1414 | else if EQ("checkstack" ) { |
1415 | int sz = getnum; |
1416 | const char *msg = getstring; |
1417 | if (*msg == '\0') |
1418 | msg = NULL; /* to test 'luaL_checkstack' with no message */ |
1419 | luaL_checkstack(L1, sz, msg); |
1420 | } |
1421 | else if EQ("rawcheckstack" ) { |
1422 | int sz = getnum; |
1423 | lua_pushboolean(L1, lua_checkstack(L1, sz)); |
1424 | } |
1425 | else if EQ("compare" ) { |
1426 | const char *opt = getstring; /* EQ, LT, or LE */ |
1427 | int op = (opt[0] == 'E') ? LUA_OPEQ |
1428 | : (opt[1] == 'T') ? LUA_OPLT : LUA_OPLE; |
1429 | int a = getindex; |
1430 | int b = getindex; |
1431 | lua_pushboolean(L1, lua_compare(L1, a, b, op)); |
1432 | } |
1433 | else if EQ("concat" ) { |
1434 | lua_concat(L1, getnum); |
1435 | } |
1436 | else if EQ("copy" ) { |
1437 | int f = getindex; |
1438 | lua_copy(L1, f, getindex); |
1439 | } |
1440 | else if EQ("func2num" ) { |
1441 | lua_CFunction func = lua_tocfunction(L1, getindex); |
1442 | lua_pushnumber(L1, cast_sizet(func)); |
1443 | } |
1444 | else if EQ("getfield" ) { |
1445 | int t = getindex; |
1446 | lua_getfield(L1, t, getstring); |
1447 | } |
1448 | else if EQ("getglobal" ) { |
1449 | lua_getglobal(L1, getstring); |
1450 | } |
1451 | else if EQ("getmetatable" ) { |
1452 | if (lua_getmetatable(L1, getindex) == 0) |
1453 | lua_pushnil(L1); |
1454 | } |
1455 | else if EQ("gettable" ) { |
1456 | lua_gettable(L1, getindex); |
1457 | } |
1458 | else if EQ("gettop" ) { |
1459 | lua_pushinteger(L1, lua_gettop(L1)); |
1460 | } |
1461 | else if EQ("gsub" ) { |
1462 | int a = getnum; int b = getnum; int c = getnum; |
1463 | luaL_gsub(L1, lua_tostring(L1, a), |
1464 | lua_tostring(L1, b), |
1465 | lua_tostring(L1, c)); |
1466 | } |
1467 | else if EQ("insert" ) { |
1468 | lua_insert(L1, getnum); |
1469 | } |
1470 | else if EQ("iscfunction" ) { |
1471 | lua_pushboolean(L1, lua_iscfunction(L1, getindex)); |
1472 | } |
1473 | else if EQ("isfunction" ) { |
1474 | lua_pushboolean(L1, lua_isfunction(L1, getindex)); |
1475 | } |
1476 | else if EQ("isnil" ) { |
1477 | lua_pushboolean(L1, lua_isnil(L1, getindex)); |
1478 | } |
1479 | else if EQ("isnull" ) { |
1480 | lua_pushboolean(L1, lua_isnone(L1, getindex)); |
1481 | } |
1482 | else if EQ("isnumber" ) { |
1483 | lua_pushboolean(L1, lua_isnumber(L1, getindex)); |
1484 | } |
1485 | else if EQ("isstring" ) { |
1486 | lua_pushboolean(L1, lua_isstring(L1, getindex)); |
1487 | } |
1488 | else if EQ("istable" ) { |
1489 | lua_pushboolean(L1, lua_istable(L1, getindex)); |
1490 | } |
1491 | else if EQ("isudataval" ) { |
1492 | lua_pushboolean(L1, lua_islightuserdata(L1, getindex)); |
1493 | } |
1494 | else if EQ("isuserdata" ) { |
1495 | lua_pushboolean(L1, lua_isuserdata(L1, getindex)); |
1496 | } |
1497 | else if EQ("len" ) { |
1498 | lua_len(L1, getindex); |
1499 | } |
1500 | else if EQ("Llen" ) { |
1501 | lua_pushinteger(L1, luaL_len(L1, getindex)); |
1502 | } |
1503 | else if EQ("loadfile" ) { |
1504 | luaL_loadfile(L1, luaL_checkstring(L1, getnum)); |
1505 | } |
1506 | else if EQ("loadstring" ) { |
1507 | const char *s = luaL_checkstring(L1, getnum); |
1508 | luaL_loadstring(L1, s); |
1509 | } |
1510 | else if EQ("newmetatable" ) { |
1511 | lua_pushboolean(L1, luaL_newmetatable(L1, getstring)); |
1512 | } |
1513 | else if EQ("newtable" ) { |
1514 | lua_newtable(L1); |
1515 | } |
1516 | else if EQ("newthread" ) { |
1517 | lua_newthread(L1); |
1518 | } |
1519 | else if EQ("resetthread" ) { |
1520 | lua_pushinteger(L1, lua_resetthread(L1)); |
1521 | } |
1522 | else if EQ("newuserdata" ) { |
1523 | lua_newuserdata(L1, getnum); |
1524 | } |
1525 | else if EQ("next" ) { |
1526 | lua_next(L1, -2); |
1527 | } |
1528 | else if EQ("objsize" ) { |
1529 | lua_pushinteger(L1, lua_rawlen(L1, getindex)); |
1530 | } |
1531 | else if EQ("pcall" ) { |
1532 | int narg = getnum; |
1533 | int nres = getnum; |
1534 | status = lua_pcall(L1, narg, nres, getnum); |
1535 | } |
1536 | else if EQ("pcallk" ) { |
1537 | int narg = getnum; |
1538 | int nres = getnum; |
1539 | int i = getindex; |
1540 | status = lua_pcallk(L1, narg, nres, 0, i, Cfunck); |
1541 | } |
1542 | else if EQ("pop" ) { |
1543 | lua_pop(L1, getnum); |
1544 | } |
1545 | else if EQ("printstack" ) { |
1546 | int n = getnum; |
1547 | if (n != 0) { |
1548 | printf("%s\n" , luaL_tolstring(L1, n, NULL)); |
1549 | lua_pop(L1, 1); |
1550 | } |
1551 | else printstack(L1); |
1552 | } |
1553 | else if EQ("print" ) { |
1554 | const char *msg = getstring; |
1555 | printf("%s\n" , msg); |
1556 | } |
1557 | else if EQ("warningC" ) { |
1558 | const char *msg = getstring; |
1559 | lua_warning(L1, msg, 1); |
1560 | } |
1561 | else if EQ("warning" ) { |
1562 | const char *msg = getstring; |
1563 | lua_warning(L1, msg, 0); |
1564 | } |
1565 | else if EQ("pushbool" ) { |
1566 | lua_pushboolean(L1, getnum); |
1567 | } |
1568 | else if EQ("pushcclosure" ) { |
1569 | lua_pushcclosure(L1, testC, getnum); |
1570 | } |
1571 | else if EQ("pushint" ) { |
1572 | lua_pushinteger(L1, getnum); |
1573 | } |
1574 | else if EQ("pushnil" ) { |
1575 | lua_pushnil(L1); |
1576 | } |
1577 | else if EQ("pushnum" ) { |
1578 | lua_pushnumber(L1, (lua_Number)getnum); |
1579 | } |
1580 | else if EQ("pushstatus" ) { |
1581 | lua_pushstring(L1, statcodes[status]); |
1582 | } |
1583 | else if EQ("pushstring" ) { |
1584 | lua_pushstring(L1, getstring); |
1585 | } |
1586 | else if EQ("pushupvalueindex" ) { |
1587 | lua_pushinteger(L1, lua_upvalueindex(getnum)); |
1588 | } |
1589 | else if EQ("pushvalue" ) { |
1590 | lua_pushvalue(L1, getindex); |
1591 | } |
1592 | else if EQ("pushfstringI" ) { |
1593 | lua_pushfstring(L1, lua_tostring(L, -2), (int)lua_tointeger(L, -1)); |
1594 | } |
1595 | else if EQ("pushfstringS" ) { |
1596 | lua_pushfstring(L1, lua_tostring(L, -2), lua_tostring(L, -1)); |
1597 | } |
1598 | else if EQ("pushfstringP" ) { |
1599 | lua_pushfstring(L1, lua_tostring(L, -2), lua_topointer(L, -1)); |
1600 | } |
1601 | else if EQ("rawget" ) { |
1602 | int t = getindex; |
1603 | lua_rawget(L1, t); |
1604 | } |
1605 | else if EQ("rawgeti" ) { |
1606 | int t = getindex; |
1607 | lua_rawgeti(L1, t, getnum); |
1608 | } |
1609 | else if EQ("rawgetp" ) { |
1610 | int t = getindex; |
1611 | lua_rawgetp(L1, t, cast_voidp(cast_sizet(getnum))); |
1612 | } |
1613 | else if EQ("rawset" ) { |
1614 | int t = getindex; |
1615 | lua_rawset(L1, t); |
1616 | } |
1617 | else if EQ("rawseti" ) { |
1618 | int t = getindex; |
1619 | lua_rawseti(L1, t, getnum); |
1620 | } |
1621 | else if EQ("rawsetp" ) { |
1622 | int t = getindex; |
1623 | lua_rawsetp(L1, t, cast_voidp(cast_sizet(getnum))); |
1624 | } |
1625 | else if EQ("remove" ) { |
1626 | lua_remove(L1, getnum); |
1627 | } |
1628 | else if EQ("replace" ) { |
1629 | lua_replace(L1, getindex); |
1630 | } |
1631 | else if EQ("resume" ) { |
1632 | int i = getindex; |
1633 | int nres; |
1634 | status = lua_resume(lua_tothread(L1, i), L, getnum, &nres); |
1635 | } |
1636 | else if EQ("return" ) { |
1637 | int n = getnum; |
1638 | if (L1 != L) { |
1639 | int i; |
1640 | for (i = 0; i < n; i++) { |
1641 | int idx = -(n - i); |
1642 | switch (lua_type(L1, idx)) { |
1643 | case LUA_TBOOLEAN: |
1644 | lua_pushboolean(L, lua_toboolean(L1, idx)); |
1645 | break; |
1646 | default: |
1647 | lua_pushstring(L, lua_tostring(L1, idx)); |
1648 | break; |
1649 | } |
1650 | } |
1651 | } |
1652 | return n; |
1653 | } |
1654 | else if EQ("rotate" ) { |
1655 | int i = getindex; |
1656 | lua_rotate(L1, i, getnum); |
1657 | } |
1658 | else if EQ("setfield" ) { |
1659 | int t = getindex; |
1660 | const char *s = getstring; |
1661 | lua_setfield(L1, t, s); |
1662 | } |
1663 | else if EQ("seti" ) { |
1664 | int t = getindex; |
1665 | lua_seti(L1, t, getnum); |
1666 | } |
1667 | else if EQ("setglobal" ) { |
1668 | const char *s = getstring; |
1669 | lua_setglobal(L1, s); |
1670 | } |
1671 | else if EQ("sethook" ) { |
1672 | int mask = getnum; |
1673 | int count = getnum; |
1674 | const char *s = getstring; |
1675 | sethookaux(L1, mask, count, s); |
1676 | } |
1677 | else if EQ("setmetatable" ) { |
1678 | int idx = getindex; |
1679 | lua_setmetatable(L1, idx); |
1680 | } |
1681 | else if EQ("settable" ) { |
1682 | lua_settable(L1, getindex); |
1683 | } |
1684 | else if EQ("settop" ) { |
1685 | lua_settop(L1, getnum); |
1686 | } |
1687 | else if EQ("testudata" ) { |
1688 | int i = getindex; |
1689 | lua_pushboolean(L1, luaL_testudata(L1, i, getstring) != NULL); |
1690 | } |
1691 | else if EQ("error" ) { |
1692 | lua_error(L1); |
1693 | } |
1694 | else if EQ("abort" ) { |
1695 | abort(); |
1696 | } |
1697 | else if EQ("throw" ) { |
1698 | #if defined(__cplusplus) |
1699 | static struct X { int x; } x; |
1700 | throw x; |
1701 | #else |
1702 | luaL_error(L1, "C++" ); |
1703 | #endif |
1704 | break; |
1705 | } |
1706 | else if EQ("tobool" ) { |
1707 | lua_pushboolean(L1, lua_toboolean(L1, getindex)); |
1708 | } |
1709 | else if EQ("tocfunction" ) { |
1710 | lua_pushcfunction(L1, lua_tocfunction(L1, getindex)); |
1711 | } |
1712 | else if EQ("tointeger" ) { |
1713 | lua_pushinteger(L1, lua_tointeger(L1, getindex)); |
1714 | } |
1715 | else if EQ("tonumber" ) { |
1716 | lua_pushnumber(L1, lua_tonumber(L1, getindex)); |
1717 | } |
1718 | else if EQ("topointer" ) { |
1719 | lua_pushlightuserdata(L1, cast_voidp(lua_topointer(L1, getindex))); |
1720 | } |
1721 | else if EQ("touserdata" ) { |
1722 | lua_pushlightuserdata(L1, lua_touserdata(L1, getindex)); |
1723 | } |
1724 | else if EQ("tostring" ) { |
1725 | const char *s = lua_tostring(L1, getindex); |
1726 | const char *s1 = lua_pushstring(L1, s); |
1727 | lua_longassert((s == NULL && s1 == NULL) || strcmp(s, s1) == 0); |
1728 | } |
1729 | else if EQ("type" ) { |
1730 | lua_pushstring(L1, luaL_typename(L1, getnum)); |
1731 | } |
1732 | else if EQ("xmove" ) { |
1733 | int f = getindex; |
1734 | int t = getindex; |
1735 | lua_State *fs = (f == 0) ? L1 : lua_tothread(L1, f); |
1736 | lua_State *ts = (t == 0) ? L1 : lua_tothread(L1, t); |
1737 | int n = getnum; |
1738 | if (n == 0) n = lua_gettop(fs); |
1739 | lua_xmove(fs, ts, n); |
1740 | } |
1741 | else if EQ("isyieldable" ) { |
1742 | lua_pushboolean(L1, lua_isyieldable(lua_tothread(L1, getindex))); |
1743 | } |
1744 | else if EQ("yield" ) { |
1745 | return lua_yield(L1, getnum); |
1746 | } |
1747 | else if EQ("yieldk" ) { |
1748 | int nres = getnum; |
1749 | int i = getindex; |
1750 | return lua_yieldk(L1, nres, i, Cfunck); |
1751 | } |
1752 | else if EQ("toclose" ) { |
1753 | lua_toclose(L1, getnum); |
1754 | } |
1755 | else luaL_error(L, "unknown instruction %s" , buff); |
1756 | } |
1757 | return 0; |
1758 | } |
1759 | |
1760 | |
1761 | static int testC (lua_State *L) { |
1762 | lua_State *L1; |
1763 | const char *pc; |
1764 | if (lua_isuserdata(L, 1)) { |
1765 | L1 = getstate(L); |
1766 | pc = luaL_checkstring(L, 2); |
1767 | } |
1768 | else if (lua_isthread(L, 1)) { |
1769 | L1 = lua_tothread(L, 1); |
1770 | pc = luaL_checkstring(L, 2); |
1771 | } |
1772 | else { |
1773 | L1 = L; |
1774 | pc = luaL_checkstring(L, 1); |
1775 | } |
1776 | return runC(L, L1, pc); |
1777 | } |
1778 | |
1779 | |
1780 | static int Cfunc (lua_State *L) { |
1781 | return runC(L, L, lua_tostring(L, lua_upvalueindex(1))); |
1782 | } |
1783 | |
1784 | |
1785 | static int Cfunck (lua_State *L, int status, lua_KContext ctx) { |
1786 | lua_pushstring(L, statcodes[status]); |
1787 | lua_setglobal(L, "status" ); |
1788 | lua_pushinteger(L, ctx); |
1789 | lua_setglobal(L, "ctx" ); |
1790 | return runC(L, L, lua_tostring(L, ctx)); |
1791 | } |
1792 | |
1793 | |
1794 | static int makeCfunc (lua_State *L) { |
1795 | luaL_checkstring(L, 1); |
1796 | lua_pushcclosure(L, Cfunc, lua_gettop(L)); |
1797 | return 1; |
1798 | } |
1799 | |
1800 | |
1801 | /* }====================================================== */ |
1802 | |
1803 | |
1804 | /* |
1805 | ** {====================================================== |
1806 | ** tests for C hooks |
1807 | ** ======================================================= |
1808 | */ |
1809 | |
1810 | /* |
1811 | ** C hook that runs the C script stored in registry.C_HOOK[L] |
1812 | */ |
1813 | static void Chook (lua_State *L, lua_Debug *ar) { |
1814 | const char *scpt; |
1815 | const char *const events [] = {"call" , "ret" , "line" , "count" , "tailcall" }; |
1816 | lua_getfield(L, LUA_REGISTRYINDEX, "C_HOOK" ); |
1817 | lua_pushlightuserdata(L, L); |
1818 | lua_gettable(L, -2); /* get C_HOOK[L] (script saved by sethookaux) */ |
1819 | scpt = lua_tostring(L, -1); /* not very religious (string will be popped) */ |
1820 | lua_pop(L, 2); /* remove C_HOOK and script */ |
1821 | lua_pushstring(L, events[ar->event]); /* may be used by script */ |
1822 | lua_pushinteger(L, ar->currentline); /* may be used by script */ |
1823 | runC(L, L, scpt); /* run script from C_HOOK[L] */ |
1824 | } |
1825 | |
1826 | |
1827 | /* |
1828 | ** sets 'registry.C_HOOK[L] = scpt' and sets 'Chook' as a hook |
1829 | */ |
1830 | static void sethookaux (lua_State *L, int mask, int count, const char *scpt) { |
1831 | if (*scpt == '\0') { /* no script? */ |
1832 | lua_sethook(L, NULL, 0, 0); /* turn off hooks */ |
1833 | return; |
1834 | } |
1835 | lua_getfield(L, LUA_REGISTRYINDEX, "C_HOOK" ); /* get C_HOOK table */ |
1836 | if (!lua_istable(L, -1)) { /* no hook table? */ |
1837 | lua_pop(L, 1); /* remove previous value */ |
1838 | lua_newtable(L); /* create new C_HOOK table */ |
1839 | lua_pushvalue(L, -1); |
1840 | lua_setfield(L, LUA_REGISTRYINDEX, "C_HOOK" ); /* register it */ |
1841 | } |
1842 | lua_pushlightuserdata(L, L); |
1843 | lua_pushstring(L, scpt); |
1844 | lua_settable(L, -3); /* C_HOOK[L] = script */ |
1845 | lua_sethook(L, Chook, mask, count); |
1846 | } |
1847 | |
1848 | |
1849 | static int sethook (lua_State *L) { |
1850 | if (lua_isnoneornil(L, 1)) |
1851 | lua_sethook(L, NULL, 0, 0); /* turn off hooks */ |
1852 | else { |
1853 | const char *scpt = luaL_checkstring(L, 1); |
1854 | const char *smask = luaL_checkstring(L, 2); |
1855 | int count = cast_int(luaL_optinteger(L, 3, 0)); |
1856 | int mask = 0; |
1857 | if (strchr(smask, 'c')) mask |= LUA_MASKCALL; |
1858 | if (strchr(smask, 'r')) mask |= LUA_MASKRET; |
1859 | if (strchr(smask, 'l')) mask |= LUA_MASKLINE; |
1860 | if (count > 0) mask |= LUA_MASKCOUNT; |
1861 | sethookaux(L, mask, count, scpt); |
1862 | } |
1863 | return 0; |
1864 | } |
1865 | |
1866 | |
1867 | static int coresume (lua_State *L) { |
1868 | int status, nres; |
1869 | lua_State *co = lua_tothread(L, 1); |
1870 | luaL_argcheck(L, co, 1, "coroutine expected" ); |
1871 | status = lua_resume(co, L, 0, &nres); |
1872 | if (status != LUA_OK && status != LUA_YIELD) { |
1873 | lua_pushboolean(L, 0); |
1874 | lua_insert(L, -2); |
1875 | return 2; /* return false + error message */ |
1876 | } |
1877 | else { |
1878 | lua_pushboolean(L, 1); |
1879 | return 1; |
1880 | } |
1881 | } |
1882 | |
1883 | /* }====================================================== */ |
1884 | |
1885 | |
1886 | |
1887 | static const struct luaL_Reg tests_funcs[] = { |
1888 | {"checkmemory" , lua_checkmemory}, |
1889 | {"closestate" , closestate}, |
1890 | {"d2s" , d2s}, |
1891 | {"doonnewstack" , doonnewstack}, |
1892 | {"doremote" , doremote}, |
1893 | {"gccolor" , gc_color}, |
1894 | {"gcage" , gc_age}, |
1895 | {"gcstate" , gc_state}, |
1896 | {"pobj" , gc_printobj}, |
1897 | {"getref" , getref}, |
1898 | {"hash" , hash_query}, |
1899 | {"log2" , log2_aux}, |
1900 | {"limits" , get_limits}, |
1901 | {"listcode" , listcode}, |
1902 | {"printcode" , printcode}, |
1903 | {"listk" , listk}, |
1904 | {"listabslineinfo" , listabslineinfo}, |
1905 | {"listlocals" , listlocals}, |
1906 | {"loadlib" , loadlib}, |
1907 | {"checkpanic" , checkpanic}, |
1908 | {"newstate" , newstate}, |
1909 | {"newuserdata" , newuserdata}, |
1910 | {"num2int" , num2int}, |
1911 | {"pushuserdata" , pushuserdata}, |
1912 | {"querystr" , string_query}, |
1913 | {"querytab" , table_query}, |
1914 | {"ref" , tref}, |
1915 | {"resume" , coresume}, |
1916 | {"s2d" , s2d}, |
1917 | {"sethook" , sethook}, |
1918 | {"stacklevel" , stacklevel}, |
1919 | {"testC" , testC}, |
1920 | {"makeCfunc" , makeCfunc}, |
1921 | {"totalmem" , mem_query}, |
1922 | {"alloccount" , alloc_count}, |
1923 | {"allocfailnext" , alloc_failnext}, |
1924 | {"trick" , settrick}, |
1925 | {"udataval" , udataval}, |
1926 | {"unref" , unref}, |
1927 | {"upvalue" , upvalue}, |
1928 | {NULL, NULL} |
1929 | }; |
1930 | |
1931 | |
1932 | static void checkfinalmem (void) { |
1933 | lua_assert(l_memcontrol.numblocks == 0); |
1934 | lua_assert(l_memcontrol.total == 0); |
1935 | } |
1936 | |
1937 | |
1938 | int luaB_opentests (lua_State *L) { |
1939 | void *ud; |
1940 | lua_atpanic(L, &tpanic); |
1941 | lua_setwarnf(L, &warnf, L); |
1942 | lua_pushboolean(L, 0); |
1943 | lua_setglobal(L, "_WARN" ); /* _WARN = false */ |
1944 | regcodes(L); |
1945 | atexit(checkfinalmem); |
1946 | lua_assert(lua_getallocf(L, &ud) == debug_realloc); |
1947 | lua_assert(ud == cast_voidp(&l_memcontrol)); |
1948 | lua_setallocf(L, lua_getallocf(L, NULL), ud); |
1949 | luaL_newlib(L, tests_funcs); |
1950 | return 1; |
1951 | } |
1952 | |
1953 | #endif |
1954 | |
1955 | |