From b0f2b288a6b860374b9578d4c51329fc9cd43022 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 25 Nov 2009 13:27:51 -0200 Subject: [PATCH] new scheme for debug info about tail calls: no more 'fake' stack entries, but stack entry knows whether it was tail called --- lauxlib.c | 8 +++++--- lbaselib.c | 4 +--- ldblib.c | 10 +++++++--- ldebug.c | 42 ++++++++++-------------------------------- ldo.c | 54 ++++++++++++++++++++++++++---------------------------- ldo.h | 4 ++-- lstate.h | 4 ++-- lua.h | 5 +++-- lvm.c | 8 ++++---- 9 files changed, 60 insertions(+), 79 deletions(-) diff --git a/lauxlib.c b/lauxlib.c index 2d33adf6..7611db7f 100644 --- a/lauxlib.c +++ b/lauxlib.c @@ -1,5 +1,5 @@ /* -** $Id: lauxlib.c,v 1.192 2009/09/28 12:36:40 roberto Exp roberto $ +** $Id: lauxlib.c,v 1.193 2009/10/05 16:44:33 roberto Exp roberto $ ** Auxiliary functions for building Lua libraries ** See Copyright Notice in lua.h */ @@ -97,7 +97,7 @@ static void pushfuncname (lua_State *L, lua_Debug *ar) { lua_remove(L, -2); /* remove name */ } else - lua_pushliteral(L, "?"); /* C function or tail call */ + lua_pushliteral(L, "?"); } else lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); @@ -133,12 +133,14 @@ LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, level = numlevels - LEVELS2; /* and skip to last ones */ } else { - lua_getinfo(L1, "Sln", &ar); + lua_getinfo(L1, "Slnt", &ar); lua_pushfstring(L, "\n\t%s:", ar.short_src); if (ar.currentline > 0) lua_pushfstring(L, "%d:", ar.currentline); lua_pushliteral(L, " in "); pushfuncname(L, &ar); + if (ar.istailcall) + lua_pushliteral(L, "\n\t(...tail calls...)"); lua_concat(L, lua_gettop(L) - top); } } diff --git a/lbaselib.c b/lbaselib.c index 16b58cf5..4b4fe6c5 100644 --- a/lbaselib.c +++ b/lbaselib.c @@ -1,5 +1,5 @@ /* -** $Id: lbaselib.c,v 1.225 2009/11/19 16:26:29 roberto Exp roberto $ +** $Id: lbaselib.c,v 1.226 2009/11/24 12:05:44 roberto Exp roberto $ ** Basic library ** See Copyright Notice in lua.h */ @@ -119,8 +119,6 @@ static void getfunc (lua_State *L, int opt) { if (lua_getstack(L, level, &ar) == 0) luaL_argerror(L, 1, "invalid level"); lua_getinfo(L, "f", &ar); - if (lua_isnil(L, -1)) - luaL_error(L, "no function environment for tail call at level %d", level); } } diff --git a/ldblib.c b/ldblib.c index c2294613..ce14d7dc 100644 --- a/ldblib.c +++ b/ldblib.c @@ -1,5 +1,5 @@ /* -** $Id: ldblib.c,v 1.116 2009/11/18 15:50:18 roberto Exp roberto $ +** $Id: ldblib.c,v 1.117 2009/11/24 12:05:44 roberto Exp roberto $ ** Interface from Lua to its debug API ** See Copyright Notice in lua.h */ @@ -100,7 +100,7 @@ static int db_getinfo (lua_State *L) { lua_Debug ar; int arg; lua_State *L1 = getthread(L, &arg); - const char *options = luaL_optstring(L, arg+2, "flnSu"); + const char *options = luaL_optstring(L, arg+2, "flnStu"); if (lua_isnumber(L, arg+1)) { if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { lua_pushnil(L); /* level out of range */ @@ -133,6 +133,10 @@ static int db_getinfo (lua_State *L) { settabss(L, "name", ar.name); settabss(L, "namewhat", ar.namewhat); } + if (strchr(options, 't')) { + lua_pushboolean(L, ar.istailcall); + lua_setfield(L, -2, "istailcall"); + } if (strchr(options, 'L')) treatstackoption(L, L1, "activelines"); if (strchr(options, 'f')) @@ -232,7 +236,7 @@ static const char KEY_HOOK = 'h'; static void hookf (lua_State *L, lua_Debug *ar) { static const char *const hooknames[] = - {"call", "return", "line", "count", "tail return"}; + {"call", "return", "line", "count", "tail call"}; lua_pushlightuserdata(L, (void *)&KEY_HOOK); lua_rawget(L, LUA_REGISTRYINDEX); lua_pushlightuserdata(L, L); diff --git a/ldebug.c b/ldebug.c index dd6f67a6..209986c5 100644 --- a/ldebug.c +++ b/ldebug.c @@ -1,5 +1,5 @@ /* -** $Id: ldebug.c,v 2.56 2009/09/28 16:32:50 roberto Exp roberto $ +** $Id: ldebug.c,v 2.57 2009/10/13 19:07:40 roberto Exp roberto $ ** Debug Interface ** See Copyright Notice in lua.h */ @@ -83,23 +83,14 @@ LUA_API int lua_gethookcount (lua_State *L) { 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) { + for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous) level--; - if (isLua(ci)) /* Lua function? */ - level -= ci->u.l.tailcalls; /* skip lost tail calls */ - } if (level == 0 && ci != &L->base_ci) { /* level found? */ status = 1; ar->i_ci = ci; } - else if (level < 0) { - if (ci == L->ci) status = 0; /* level was negative? */ - else { /* level is of a lost tail call */ - status = 1; - ar->i_ci = NULL; - } - } else status = 0; /* no such level */ lua_unlock(L); return status; @@ -110,7 +101,6 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { const char *name = NULL; StkId base; - if (ci == NULL) return NULL; /* tail call? */ if (isLua(ci)) { base = ci->u.l.base; name = luaF_getlocalname(ci_func(ci)->l.p, n, currentpc(ci)); @@ -170,17 +160,6 @@ static void funcinfo (lua_Debug *ar, Closure *cl) { } -static void info_tailcall (lua_Debug *ar) { - ar->name = NULL; - ar->namewhat = ""; - ar->what = "tail"; - ar->lastlinedefined = ar->linedefined = ar->currentline = -1; - ar->source = "=(tail call)"; - luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); - ar->nups = 0; -} - - static void collectvalidlines (lua_State *L, Closure *f) { if (f == NULL || f->c.isC) { setnilvalue(L->top); @@ -201,10 +180,6 @@ static void collectvalidlines (lua_State *L, Closure *f) { static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, Closure *f, CallInfo *ci) { int status = 1; - if (f == NULL) { - info_tailcall(ar); - return status; - } for (; *what; what++) { switch (*what) { case 'S': { @@ -219,6 +194,10 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, ar->nups = f->c.nupvalues; break; } + case 't': { + ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0; + break; + } case 'n': { ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL; if (ar->namewhat == NULL) { @@ -249,15 +228,14 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { f = clvalue(func); L->top--; /* pop function */ } - else if (ar->i_ci != NULL) { /* no tail call? */ + else { ci = ar->i_ci; lua_assert(ttisfunction(ci->func)); f = clvalue(ci->func); } status = auxgetinfo(L, what, ar, f, ci); if (strchr(what, 'f')) { - if (f == NULL) setnilvalue(L->top); - else setclvalue(L, L->top, f); + setclvalue(L, L->top, f); incr_top(L); } if (strchr(what, 'L')) @@ -379,7 +357,7 @@ static const char *getobjname (lua_State *L, CallInfo *ci, int reg, static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { TMS tm = 0; Instruction i; - if ((isLua(ci) && ci->u.l.tailcalls > 0) || !isLua(ci->previous)) + if ((ci->callstatus & CIST_TAIL) || !isLua(ci->previous)) return NULL; /* calling function is not Lua (or is unknown) */ ci = ci->previous; /* calling function */ i = ci_func(ci)->l.p->code[currentpc(ci)]; diff --git a/ldo.c b/ldo.c index 72c92e21..7e8f2cdb 100644 --- a/ldo.c +++ b/ldo.c @@ -1,5 +1,5 @@ /* -** $Id: ldo.c,v 2.71 2009/11/17 16:33:38 roberto Exp roberto $ +** $Id: ldo.c,v 2.72 2009/11/17 16:46:44 roberto Exp roberto $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -184,7 +184,7 @@ void luaD_shrinkstack (lua_State *L) { } -void luaD_callhook (lua_State *L, int event, int line) { +void luaD_hook (lua_State *L, int event, int line) { lua_Hook hook = L->hook; if (hook && L->allowhook) { CallInfo *ci = L->ci; @@ -193,10 +193,7 @@ void luaD_callhook (lua_State *L, int event, int line) { lua_Debug ar; ar.event = event; ar.currentline = line; - if (event == LUA_HOOKTAILRET) - ar.i_ci = NULL; /* tail call; no debug information about it */ - else - ar.i_ci = ci; + ar.i_ci = ci; luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ ci->top = L->top + LUA_MINSTACK; lua_assert(ci->top <= L->stack_last); @@ -214,6 +211,19 @@ void luaD_callhook (lua_State *L, int event, int line) { } +static void callhook (lua_State *L, CallInfo *ci) { + int hook = LUA_HOOKCALL; + ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ + if (isLua(ci->previous) && + GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) { + ci->callstatus |= CIST_TAIL; + hook = LUA_HOOKTAILCALL; + } + luaD_hook(L, hook, -1); + ci->u.l.savedpc--; /* correct 'pc' */ +} + + static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { int i; int nfixargs = p->numparams; @@ -281,14 +291,10 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { ci->top = base + p->maxstacksize; lua_assert(ci->top <= L->stack_last); ci->u.l.savedpc = p->code; /* starting point */ - ci->u.l.tailcalls = 0; ci->callstatus = CIST_LUA; L->top = ci->top; - if (L->hookmask & LUA_MASKCALL) { - ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ - luaD_callhook(L, LUA_HOOKCALL, -1); - ci->u.l.savedpc--; /* correct 'pc' */ - } + if (L->hookmask & LUA_MASKCALL) + callhook(L, ci); return 0; } else { /* if is a C function, call it */ @@ -301,7 +307,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { lua_assert(ci->top <= L->stack_last); ci->callstatus = 0; if (L->hookmask & LUA_MASKCALL) - luaD_callhook(L, LUA_HOOKCALL, -1); + luaD_hook(L, LUA_HOOKCALL, -1); lua_unlock(L); n = (*curr_func(L)->c.f)(L); /* do the actual call */ lua_lock(L); @@ -311,25 +317,17 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { } -static StkId callrethooks (lua_State *L, StkId firstResult) { - ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */ - luaD_callhook(L, LUA_HOOKRET, -1); - if (isLua(L->ci)) { /* Lua function? */ - while ((L->hookmask & LUA_MASKRET) && L->ci->u.l.tailcalls--) - luaD_callhook(L, LUA_HOOKTAILRET, -1); /* ret. hooks for tail calls */ - } - return restorestack(L, fr); -} - - int luaD_poscall (lua_State *L, StkId firstResult) { StkId res; int wanted, i; CallInfo *ci = L->ci; if (L->hookmask & (LUA_MASKRET | LUA_MASKLINE)) { - if (L->hookmask & LUA_MASKRET) - firstResult = callrethooks(L, firstResult); - L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for returning function */ + if (L->hookmask & LUA_MASKRET) { + ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */ + luaD_hook(L, LUA_HOOKRET, -1); + firstResult = restorestack(L, fr); + } + L->oldpc = ci->previous->u.l.savedpc; /* 'oldpc' for caller function */ } res = ci->func; /* res == final position of 1st result */ L->ci = ci = ci->previous; /* back to caller */ @@ -529,7 +527,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, int ctx, lua_CFunction k) { } lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ lua_unlock(L); - return 0; /* otherwise, return to 'luaD_callhook' */ + return 0; /* return to 'luaD_hook' */ } diff --git a/ldo.h b/ldo.h index f59c045d..834e6dd8 100644 --- a/ldo.h +++ b/ldo.h @@ -1,5 +1,5 @@ /* -** $Id: ldo.h,v 2.15 2009/07/15 17:26:14 roberto Exp roberto $ +** $Id: ldo.h,v 2.16 2009/11/19 16:24:41 roberto Exp roberto $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -27,7 +27,7 @@ typedef void (*Pfunc) (lua_State *L, void *ud); LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name); -LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line); +LUAI_FUNC void luaD_hook (lua_State *L, int event, int line); LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults, int allowyield); diff --git a/lstate.h b/lstate.h index 84847eae..74b11e1e 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.47 2009/10/23 19:12:19 roberto Exp roberto $ +** $Id: lstate.h,v 2.48 2009/11/18 13:13:47 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -80,7 +80,6 @@ typedef struct CallInfo { struct { /* only for Lua functions */ StkId base; /* base for this function */ const Instruction *savedpc; - int tailcalls; /* number of tail calls lost under this entry */ } l; struct { /* only for C functions */ int ctx; /* context info. in case of yields */ @@ -104,6 +103,7 @@ typedef struct CallInfo { #define CIST_YIELDED (1<<3) /* call reentered after suspension */ #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ #define CIST_STAT (1<<5) /* call has an error status (pcall) */ +#define CIST_TAIL (1<<6) /* call was tail called */ #define curr_func(L) (clvalue(L->ci->func)) diff --git a/lua.h b/lua.h index 2ac5bd87..156fb875 100644 --- a/lua.h +++ b/lua.h @@ -1,5 +1,5 @@ /* -** $Id: lua.h,v 1.249 2009/11/09 18:55:17 roberto Exp roberto $ +** $Id: lua.h,v 1.250 2009/11/09 19:10:48 roberto Exp roberto $ ** Lua - A Scripting Language ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) ** See Copyright Notice at the end of this file @@ -356,7 +356,7 @@ LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); #define LUA_HOOKRET 1 #define LUA_HOOKLINE 2 #define LUA_HOOKCOUNT 3 -#define LUA_HOOKTAILRET 4 +#define LUA_HOOKTAILCALL 4 /* @@ -401,6 +401,7 @@ struct lua_Debug { int nups; /* (u) number of upvalues */ int linedefined; /* (S) */ int lastlinedefined; /* (S) */ + char istailcall; /* (t) */ char short_src[LUA_IDSIZE]; /* (S) */ /* private part */ struct CallInfo *i_ci; /* active function */ diff --git a/lvm.c b/lvm.c index 53965be0..0b9eab47 100644 --- a/lvm.c +++ b/lvm.c @@ -1,5 +1,5 @@ /* -** $Id: lvm.c,v 2.99 2009/09/30 15:38:37 roberto Exp roberto $ +** $Id: lvm.c,v 2.100 2009/10/28 12:20:07 roberto Exp roberto $ ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -62,7 +62,7 @@ static void traceexec (lua_State *L) { lu_byte mask = L->hookmask; if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { resethookcount(L); - luaD_callhook(L, LUA_HOOKCOUNT, -1); + luaD_hook(L, LUA_HOOKCOUNT, -1); } if (mask & LUA_MASKLINE) { Proto *p = ci_func(ci)->l.p; @@ -71,7 +71,7 @@ static void traceexec (lua_State *L) { if (npc == 0 || /* call linehook when enter a new function, */ ci->u.l.savedpc <= L->oldpc || /* when jump back (loop), or when */ newline != getfuncline(p, pcRel(L->oldpc, p))) /* enter a new line */ - luaD_callhook(L, LUA_HOOKLINE, newline); + luaD_hook(L, LUA_HOOKLINE, newline); } L->oldpc = ci->u.l.savedpc; } @@ -679,7 +679,7 @@ void luaV_execute (lua_State *L) { oci->u.l.base = ofunc + (nci->u.l.base - nfunc); /* correct base */ oci->top = L->top = ofunc + (L->top - nfunc); /* correct top */ oci->u.l.savedpc = nci->u.l.savedpc; - oci->u.l.tailcalls++; /* one more call lost */ + oci->callstatus |= CIST_TAIL; /* function was tail called */ ci = L->ci = oci; /* remove new frame */ lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize); break; /* restart luaV_execute over new Lua function */