/* ** $Id: ldebug.c $ ** Debug Interface ** See Copyright Notice in lua.h */ #define ldebug_c #define LUA_CORE #include "lprefix.h" #include #include #include #include "lua.h" #include "lapi.h" #include "lcode.h" #include "ldebug.h" #include "ldo.h" #include "lfunc.h" #include "lobject.h" #include "lopcodes.h" #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "ltm.h" #include "lvm.h" #define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL) /* Active Lua function (given call info) */ #define ci_func(ci) (clLvalue(s2v((ci)->func))) static const char *funcnamefromcode (lua_State *L, CallInfo *ci, const char **name); static int currentpc (CallInfo *ci) { lua_assert(isLua(ci)); return pcRel(ci->u.l.savedpc, ci_func(ci)->p); } /* ** Get a "base line" to find the line corresponding to an instruction. ** For that, search the array of absolute line info for the largest saved ** instruction smaller or equal to the wanted instruction. A special ** case is when there is no absolute info or the instruction is before ** the first absolute one. */ static int getbaseline (const Proto *f, int pc, int *basepc) { if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) { *basepc = -1; /* start from the beginning */ return f->linedefined; } else { unsigned int i; if (pc >= f->abslineinfo[f->sizeabslineinfo - 1].pc) i = f->sizeabslineinfo - 1; /* instruction is after last saved one */ else { /* binary search */ unsigned int j = f->sizeabslineinfo - 1; /* pc < anchorlines[j] */ i = 0; /* abslineinfo[i] <= pc */ while (i < j - 1) { unsigned int m = (j + i) / 2; if (pc >= f->abslineinfo[m].pc) i = m; else j = m; } } *basepc = f->abslineinfo[i].pc; return f->abslineinfo[i].line; } } /* ** Get the line corresponding to instruction 'pc' in function 'f'; ** first gets a base line and from there does the increments until ** the desired instruction. */ int luaG_getfuncline (const Proto *f, int pc) { if (f->lineinfo == NULL) /* no debug information? */ return -1; else { int basepc; int baseline = getbaseline(f, pc, &basepc); while (basepc++ < pc) { /* walk until given instruction */ lua_assert(f->lineinfo[basepc] != ABSLINEINFO); baseline += f->lineinfo[basepc]; /* correct line */ } return baseline; } } static int currentline (CallInfo *ci) { return luaG_getfuncline(ci_func(ci)->p, currentpc(ci)); } /* ** This function can be called asynchronously (e.g. during a signal), ** under "reasonable" assumptions. A new 'ci' is completely linked ** in the list before it becomes part of the "active" list, and ** we assume that pointers are atomic (see comment in next function). ** (If we traverse one more item, there is no problem. If we traverse ** one less item, the worst that can happen is that the signal will ** not interrupt the script.) */ static void settraps (CallInfo *ci) { for (; ci != NULL; ci = ci->previous) if (isLua(ci)) ci->u.l.trap = 1; } /* ** This function can be called asynchronously (e.g. during a signal), ** under "reasonable" assumptions. ** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by ** 'resethookcount') are for debug only, and it is no problem if they ** get arbitrary values (causes at most one wrong hook call). 'hookmask' ** is an atomic value. We assume that pointers are atomic too (e.g., gcc ** ensures that for all platforms where it runs). Moreover, 'hook' is ** always checked before being called (see 'luaD_hook'). */ LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { if (func == NULL || mask == 0) { /* turn off hooks? */ mask = 0; func = NULL; } if (isLua(L->ci)) L->oldpc = L->ci->u.l.savedpc; L->hook = func; L->basehookcount = count; resethookcount(L); L->hookmask = cast_byte(mask); if (mask) settraps(L->ci); /* to trace inside 'luaV_execute' */ } LUA_API lua_Hook lua_gethook (lua_State *L) { return L->hook; } LUA_API int lua_gethookmask (lua_State *L) { return L->hookmask; } LUA_API int lua_gethookcount (lua_State *L) { return L->basehookcount; } LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { int status; CallInfo *ci; if (level < 0) return 0; /* invalid (negative) level */ lua_lock(L); for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous) level--; if (level == 0 && ci != &L->base_ci) { /* level found? */ status = 1; ar->i_ci = ci; } else status = 0; /* no such level */ lua_unlock(L); return status; } static const char *upvalname (const Proto *p, int uv) { TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name); if (s == NULL) return "?"; else return getstr(s); } static const char *findvararg (CallInfo *ci, int n, StkId *pos) { if (clLvalue(s2v(ci->func))->p->is_vararg) { int nextra = ci->u.l.nextraargs; if (n <= nextra) { *pos = ci->func - nextra + (n - 1); return "(vararg)"; /* generic name for any vararg */ } } return NULL; /* no such vararg */ } const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { StkId base = ci->func + 1; const char *name = NULL; if (isLua(ci)) { if (n < 0) /* access to vararg values? */ return findvararg(ci, -n, pos); else name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); } if (name == NULL) { /* no 'standard' name? */ StkId limit = (ci == L->ci) ? L->top : ci->next->func; if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ /* generic name for any valid slot */ name = isLua(ci) ? "(temporary)" : "(C temporary)"; } else return NULL; /* no name */ } if (pos) *pos = base + (n - 1); return name; } LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { const char *name; lua_lock(L); if (ar == NULL) { /* information about non-active function? */ if (!isLfunction(s2v(L->top - 1))) /* not a Lua function? */ name = NULL; else /* consider live variables at function start (parameters) */ name = luaF_getlocalname(clLvalue(s2v(L->top - 1))->p, n, 0); } else { /* active function; get information through 'ar' */ StkId pos = NULL; /* to avoid warnings */ name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { setobjs2s(L, L->top, pos); api_incr_top(L); } } lua_unlock(L); return name; } LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { StkId pos = NULL; /* to avoid warnings */ const char *name; lua_lock(L); name = luaG_findlocal(L, ar->i_ci, n, &pos); if (name) { setobjs2s(L, pos, L->top - 1); L->top--; /* pop value */ } lua_unlock(L); return name; } static void funcinfo (lua_Debug *ar, Closure *cl) { if (noLuaClosure(cl)) { ar->source = "=[C]"; ar->srclen = LL("=[C]"); ar->linedefined = -1; ar->lastlinedefined = -1; ar->what = "C"; } else { const Proto *p = cl->l.p; if (p->source) { ar->source = getstr(p->source); ar->srclen = tsslen(p->source); } else { ar->source = "=?"; ar->srclen = LL("=?"); } ar->linedefined = p->linedefined; ar->lastlinedefined = p->lastlinedefined; ar->what = (ar->linedefined == 0) ? "main" : "Lua"; } luaO_chunkid(ar->short_src, ar->source, ar->srclen); } static int nextline (const Proto *p, int currentline, int pc) { if (p->lineinfo[pc] != ABSLINEINFO) return currentline + p->lineinfo[pc]; else return luaG_getfuncline(p, pc); } static void collectvalidlines (lua_State *L, Closure *f) { if (noLuaClosure(f)) { setnilvalue(s2v(L->top)); api_incr_top(L); } else { int i; TValue v; const Proto *p = f->l.p; int currentline = p->linedefined; Table *t = luaH_new(L); /* new table to store active lines */ sethvalue2s(L, L->top, t); /* push it on stack */ api_incr_top(L); setbvalue(&v, 1); /* boolean 'true' to be the value of all indices */ for (i = 0; i < p->sizelineinfo; i++) { /* for all lines with code */ currentline = nextline(p, currentline, i); luaH_setint(L, t, currentline, &v); /* table[line] = true */ } } } static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { if (ci == NULL) /* no 'ci'? */ return NULL; /* no info */ else if (ci->callstatus & CIST_FIN) { /* is this a finalizer? */ *name = "__gc"; return "metamethod"; /* report it as such */ } /* calling function is a known Lua function? */ else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous)) return funcnamefromcode(L, ci->previous, name); else return NULL; /* no way to find a name */ } static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, Closure *f, CallInfo *ci) { int status = 1; for (; *what; what++) { switch (*what) { case 'S': { funcinfo(ar, f); break; } case 'l': { ar->currentline = (ci && isLua(ci)) ? currentline(ci) : -1; break; } case 'u': { ar->nups = (f == NULL) ? 0 : f->c.nupvalues; if (noLuaClosure(f)) { ar->isvararg = 1; ar->nparams = 0; } else { ar->isvararg = f->l.p->is_vararg; ar->nparams = f->l.p->numparams; } break; } case 't': { ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0; break; } case 'n': { ar->namewhat = getfuncname(L, ci, &ar->name); if (ar->namewhat == NULL) { ar->namewhat = ""; /* not found */ ar->name = NULL; } break; } case 'r': { if (ci == NULL || !(ci->callstatus & CIST_TRAN)) ar->ftransfer = ar->ntransfer = 0; else { ar->ftransfer = ci->u2.transferinfo.ftransfer; ar->ntransfer = ci->u2.transferinfo.ntransfer; } break; } case 'L': case 'f': /* handled by lua_getinfo */ break; default: status = 0; /* invalid option */ } } return status; } LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { int status; Closure *cl; CallInfo *ci; TValue *func; lua_lock(L); if (*what == '>') { ci = NULL; func = s2v(L->top - 1); api_check(L, ttisfunction(func), "function expected"); what++; /* skip the '>' */ L->top--; /* pop function */ } else { ci = ar->i_ci; func = s2v(ci->func); lua_assert(ttisfunction(func)); } cl = ttisclosure(func) ? clvalue(func) : NULL; status = auxgetinfo(L, what, ar, cl, ci); if (strchr(what, 'f')) { setobj2s(L, L->top, func); api_incr_top(L); } if (strchr(what, 'L')) collectvalidlines(L, cl); lua_unlock(L); return status; } /* ** {====================================================== ** Symbolic Execution ** ======================================================= */ static const char *getobjname (const Proto *p, int lastpc, int reg, const char **name); /* ** Find a "name" for the constant 'c'. */ static void kname (const Proto *p, int c, const char **name) { TValue *kvalue = &p->k[c]; *name = (ttisstring(kvalue)) ? svalue(kvalue) : "?"; } /* ** Find a "name" for the register 'c'. */ static void rname (const Proto *p, int pc, int c, const char **name) { const char *what = getobjname(p, pc, c, name); /* search for 'c' */ if (!(what && *what == 'c')) /* did not find a constant name? */ *name = "?"; } /* ** Find a "name" for a 'C' value in an RK instruction. */ static void rkname (const Proto *p, int pc, Instruction i, const char **name) { int c = GETARG_C(i); /* key index */ if (GETARG_k(i)) /* is 'c' a constant? */ kname(p, c, name); else /* 'c' is a register */ rname(p, pc, c, name); } static int filterpc (int pc, int jmptarget) { if (pc < jmptarget) /* is code conditional (inside a jump)? */ return -1; /* cannot know who sets that register */ else return pc; /* current position sets that register */ } /* ** try to find last instruction before 'lastpc' that modified register 'reg' */ static int findsetreg (const Proto *p, int lastpc, int reg) { int pc; int setreg = -1; /* keep last instruction that changed 'reg' */ int jmptarget = 0; /* any code before this address is conditional */ for (pc = 0; pc < lastpc; pc++) { Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); int a = GETARG_A(i); int change; /* true if current instruction changed 'reg' */ switch (op) { case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */ int b = GETARG_B(i); change = (a <= reg && reg <= a + b); break; } case OP_TFORCALL: { /* affect all regs above its base */ change = (reg >= a + 2); break; } case OP_CALL: case OP_TAILCALL: { /* affect all registers above base */ change = (reg >= a); break; } case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */ int b = GETARG_sJ(i); int dest = pc + 1 + b; /* jump does not skip 'lastpc' and is larger than current one? */ if (dest <= lastpc && dest > jmptarget) jmptarget = dest; /* update 'jmptarget' */ change = 0; break; } default: /* any instruction that sets A */ change = (testAMode(op) && reg == a); break; } if (change) setreg = filterpc(pc, jmptarget); } return setreg; } /* ** Check whether table being indexed by instruction 'i' is the ** environment '_ENV' */ static const char *gxf (const Proto *p, int pc, Instruction i, int isup) { int t = GETARG_B(i); /* table index */ const char *name; /* name of indexed variable */ if (isup) /* is an upvalue? */ name = upvalname(p, t); else getobjname(p, pc, t, &name); return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; } static const char *getobjname (const Proto *p, int lastpc, int reg, const char **name) { int pc; *name = luaF_getlocalname(p, reg + 1, lastpc); if (*name) /* is a local? */ return "local"; /* else try symbolic execution */ pc = findsetreg(p, lastpc, reg); if (pc != -1) { /* could find instruction? */ Instruction i = p->code[pc]; OpCode op = GET_OPCODE(i); switch (op) { case OP_MOVE: { int b = GETARG_B(i); /* move from 'b' to 'a' */ if (b < GETARG_A(i)) return getobjname(p, pc, b, name); /* get name for 'b' */ break; } case OP_GETTABUP: { int k = GETARG_C(i); /* key index */ kname(p, k, name); return gxf(p, pc, i, 1); } case OP_GETTABLE: { int k = GETARG_C(i); /* key index */ rname(p, pc, k, name); return gxf(p, pc, i, 0); } case OP_GETI: { *name = "integer index"; return "field"; } case OP_GETFIELD: { int k = GETARG_C(i); /* key index */ kname(p, k, name); return gxf(p, pc, i, 0); } case OP_GETUPVAL: { *name = upvalname(p, GETARG_B(i)); return "upvalue"; } case OP_LOADK: case OP_LOADKX: { int b = (op == OP_LOADK) ? GETARG_Bx(i) : GETARG_Ax(p->code[pc + 1]); if (ttisstring(&p->k[b])) { *name = svalue(&p->k[b]); return "constant"; } break; } case OP_SELF: { rkname(p, pc, i, name); return "method"; } default: break; /* go through to return NULL */ } } return NULL; /* could not find reasonable name */ } /* ** Try to find a name for a function based on the code that called it. ** (Only works when function was called by a Lua function.) ** Returns what the name is (e.g., "for iterator", "method", ** "metamethod") and sets '*name' to point to the name. */ static const char *funcnamefromcode (lua_State *L, CallInfo *ci, const char **name) { TMS tm = (TMS)0; /* (initial value avoids warnings) */ const Proto *p = ci_func(ci)->p; /* calling function */ int pc = currentpc(ci); /* calling instruction index */ Instruction i = p->code[pc]; /* calling instruction */ if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ *name = "?"; return "hook"; } switch (GET_OPCODE(i)) { case OP_CALL: case OP_TAILCALL: return getobjname(p, pc, GETARG_A(i), name); /* get function name */ case OP_TFORCALL: { /* for iterator */ *name = "for iterator"; return "for iterator"; } /* other instructions can do calls through metamethods */ case OP_SELF: case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: case OP_GETFIELD: tm = TM_INDEX; break; case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD: tm = TM_NEWINDEX; break; case OP_ADDI: case OP_SUBI: case OP_MULI: case OP_MODI: case OP_POWI: case OP_DIVI: case OP_IDIVI: { int offset = GET_OPCODE(i) - OP_ADDI; /* ORDER OP */ tm = cast(TMS, offset + TM_ADD); /* ORDER TM */ break; } case OP_ADDK: case OP_SUBK: case OP_MULK: case OP_MODK: case OP_POWK: case OP_DIVK: case OP_IDIVK: case OP_BANDK: case OP_BORK: case OP_BXORK: { int offset = GET_OPCODE(i) - OP_ADDK; /* ORDER OP */ tm = cast(TMS, offset + TM_ADD); /* ORDER TM */ break; } case OP_ADD: case OP_SUB: case OP_MUL: case OP_MOD: case OP_POW: case OP_DIV: case OP_IDIV: case OP_BAND: case OP_BOR: case OP_BXOR: case OP_SHL: case OP_SHR: { int offset = GET_OPCODE(i) - OP_ADD; /* ORDER OP */ tm = cast(TMS, offset + TM_ADD); /* ORDER TM */ break; } case OP_UNM: tm = TM_UNM; break; case OP_BNOT: tm = TM_BNOT; break; case OP_LEN: tm = TM_LEN; break; case OP_CONCAT: tm = TM_CONCAT; break; case OP_EQ: tm = TM_EQ; break; case OP_LT: case OP_LE: case OP_LTI: case OP_LEI: *name = "order"; /* '<=' can call '__lt', etc. */ return "metamethod"; case OP_SHRI: case OP_SHLI: *name = "shift"; return "metamethod"; default: return NULL; /* cannot find a reasonable name */ } *name = getstr(G(L)->tmname[tm]) + 2; return "metamethod"; } /* }====================================================== */ /* ** The subtraction of two potentially unrelated pointers is ** not ISO C, but it should not crash a program; the subsequent ** checks are ISO C and ensure a correct result. */ static int isinstack (CallInfo *ci, const TValue *o) { StkId base = ci->func + 1; ptrdiff_t i = cast(StkId, o) - base; return (0 <= i && i < (ci->top - base) && s2v(base + i) == o); } /* ** Checks whether value 'o' came from an upvalue. (That can only happen ** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on ** upvalues.) */ static const char *getupvalname (CallInfo *ci, const TValue *o, const char **name) { LClosure *c = ci_func(ci); int i; for (i = 0; i < c->nupvalues; i++) { if (c->upvals[i]->v == o) { *name = upvalname(c->p, i); return "upvalue"; } } return NULL; } static const char *varinfo (lua_State *L, const TValue *o) { const char *name = NULL; /* to avoid warnings */ CallInfo *ci = L->ci; const char *kind = NULL; if (isLua(ci)) { kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ if (!kind && isinstack(ci, o)) /* no? try a register */ kind = getobjname(ci_func(ci)->p, currentpc(ci), cast_int(cast(StkId, o) - (ci->func + 1)), &name); } return (kind) ? luaO_pushfstring(L, " (%s '%s')", kind, name) : ""; } l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { const char *t = luaT_objtypename(L, o); luaG_runerror(L, "attempt to %s a %s value%s", op, t, varinfo(L, o)); } l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) { luaG_runerror(L, "bad 'for' %s (number expected, got %s)", what, luaT_objtypename(L, o)); } l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { if (ttisstring(p1) || cvt2str(p1)) p1 = p2; luaG_typeerror(L, p1, "concatenate"); } l_noret luaG_opinterror (lua_State *L, const TValue *p1, const TValue *p2, const char *msg) { if (!ttisnumber(p1)) /* first operand is wrong? */ p2 = p1; /* now second is wrong */ luaG_typeerror(L, p2, msg); } /* ** Error when both values are convertible to numbers, but not to integers */ l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { lua_Integer temp; if (!tointegerns(p1, &temp)) p2 = p1; luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); } l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { const char *t1 = luaT_objtypename(L, p1); const char *t2 = luaT_objtypename(L, p2); if (strcmp(t1, t2) == 0) luaG_runerror(L, "attempt to compare two %s values", t1); else luaG_runerror(L, "attempt to compare %s with %s", t1, t2); } /* add src:line information to 'msg' */ const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, int line) { char buff[LUA_IDSIZE]; if (src) luaO_chunkid(buff, getstr(src), tsslen(src)); else { /* no source available; use "?" instead */ buff[0] = '?'; buff[1] = '\0'; } return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); } l_noret luaG_errormsg (lua_State *L) { if (L->errfunc != 0) { /* is there an error handling function? */ StkId errfunc = restorestack(L, L->errfunc); lua_assert(ttisfunction(s2v(errfunc))); setobjs2s(L, L->top, L->top - 1); /* move argument */ setobjs2s(L, L->top - 1, errfunc); /* push function */ L->top++; /* assume EXTRA_STACK */ luaD_callnoyield(L, L->top - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); } l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { CallInfo *ci = L->ci; const char *msg; va_list argp; luaC_checkGC(L); /* error message uses memory */ va_start(argp, fmt); msg = luaO_pushvfstring(L, fmt, argp); /* format message */ va_end(argp); if (isLua(ci)) /* if Lua function, add source:line information */ luaG_addinfo(L, msg, ci_func(ci)->p->source, currentline(ci)); luaG_errormsg(L); } /* ** Check whether new instruction 'newpc' is in a different line from ** previous instruction 'oldpc'. */ static int changedline (const Proto *p, int oldpc, int newpc) { while (oldpc++ < newpc) { if (p->lineinfo[oldpc] != 0) return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc)); } return 0; /* no line changes in the way */ } int luaG_traceexec (lua_State *L, const Instruction *pc) { CallInfo *ci = L->ci; lu_byte mask = L->hookmask; int counthook; if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ ci->u.l.trap = 0; /* don't need to stop again */ return 0; /* turn off 'trap' */ } pc++; /* reference is always next instruction */ ci->u.l.savedpc = pc; /* save 'pc' */ counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT)); if (counthook) resethookcount(L); /* reset count */ else if (!(mask & LUA_MASKLINE)) return 1; /* no line hook and count != 0; nothing to be done now */ if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */ ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ return 1; /* do not call hook again (VM yielded, so it did not move) */ } if (!isIT(*(ci->u.l.savedpc - 1))) L->top = ci->top; /* prepare top */ if (counthook) luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ if (mask & LUA_MASKLINE) { const Proto *p = ci_func(ci)->p; int npci = pcRel(pc, p); if (npci == 0 || /* call linehook when enter a new function, */ pc <= L->oldpc || /* when jump back (loop), or when */ changedline(p, pcRel(L->oldpc, p), npci)) { /* enter new line */ int newline = luaG_getfuncline(p, npci); luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */ } L->oldpc = pc; /* 'pc' of last call to line hook */ } if (L->status == LUA_YIELD) { /* did hook yield? */ if (counthook) L->hookcount = 1; /* undo decrement to zero */ ci->u.l.savedpc--; /* undo increment (resume will increment it again) */ ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ luaD_throw(L, LUA_YIELD); } return 1; /* keep 'trap' on */ }