2
0
Эх сурвалжийг харах

new scheme for debug info about tail calls: no more 'fake' stack entries,
but stack entry knows whether it was tail called

Roberto Ierusalimschy 15 жил өмнө
parent
commit
b0f2b288a6
9 өөрчлөгдсөн 60 нэмэгдсэн , 79 устгасан
  1. 5 3
      lauxlib.c
  2. 1 3
      lbaselib.c
  3. 7 3
      ldblib.c
  4. 10 32
      ldebug.c
  5. 26 28
      ldo.c
  6. 2 2
      ldo.h
  7. 2 2
      lstate.h
  8. 3 2
      lua.h
  9. 4 4
      lvm.c

+ 5 - 3
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);
     }
   }

+ 1 - 3
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);
   }
 }
 

+ 7 - 3
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);

+ 10 - 32
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)];

+ 26 - 28
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' */
 }
 
 

+ 2 - 2
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);

+ 2 - 2
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))

+ 3 - 2
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 */

+ 4 - 4
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 */