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

New implementation for 'tbclist'

- Fixes a bug, by removing dummy nodes together with the node
itself. (The previous implementation could leave dummy nodes in frames
which otherwise had no tbc variables, and therefore would not close
variables; that could leave 'tbclist' pointing higher than 'top', which
could dangle if the stack shrank.)

- Computes MAXDELTA based on the type of delta, to ease changing its
type if needed.

- Instead of 'isdummy', uses 'delta==0' to signal dummy nodes. (Dummy
nodes always have MAXDELTA for their real delta.)
Roberto Ierusalimschy 4 жил өмнө
parent
commit
81c6021fb4
2 өөрчлөгдсөн 32 нэмэгдсэн , 13 устгасан
  1. 29 11
      lfunc.c
  2. 3 2
      lobject.h

+ 29 - 11
lfunc.c

@@ -154,6 +154,15 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
 }
 
 
+/*
+** Maximum value for deltas in 'tbclist', dependent on the type
+** of delta. (This macro assumes that an 'L' is in scope where it
+** is used.)
+*/
+#define MAXDELTA  \
+	((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1)
+
+
 /*
 ** Insert a variable in the list of to-be-closed variables.
 */
@@ -162,13 +171,11 @@ void luaF_newtbcupval (lua_State *L, StkId level) {
   if (l_isfalse(s2v(level)))
     return;  /* false doesn't need to be closed */
   checkclosemth(L, level);  /* value must have a close method */
-  while (level - L->tbclist > USHRT_MAX) {  /* is delta too large? */
-    L->tbclist += USHRT_MAX;  /* create a dummy node at maximum delta */
-    L->tbclist->tbclist.delta = USHRT_MAX;
-    L->tbclist->tbclist.isdummy = 1;
+  while (cast_uint(level - L->tbclist) > MAXDELTA) {
+    L->tbclist += MAXDELTA;  /* create a dummy node at maximum delta */
+    L->tbclist->tbclist.delta = 0;
   }
-  level->tbclist.delta = level - L->tbclist;
-  level->tbclist.isdummy = 0;
+  level->tbclist.delta = cast(unsigned short, level - L->tbclist);
   L->tbclist = level;
 }
 
@@ -201,6 +208,19 @@ void luaF_closeupval (lua_State *L, StkId level) {
 }
 
 
+/*
+** Remove firt element from the tbclist plus its dummy nodes.
+*/
+static void poptbclist (lua_State *L) {
+  StkId tbc = L->tbclist;
+  lua_assert(tbc->tbclist.delta > 0);  /* first element cannot be dummy */
+  tbc -= tbc->tbclist.delta;
+  while (tbc > L->stack && tbc->tbclist.delta == 0)
+    tbc -= MAXDELTA;  /* remove dummy nodes */
+  L->tbclist = tbc;
+}
+
+
 /*
 ** Close all upvalues and to-be-closed variables up to the given stack
 ** level.
@@ -210,11 +230,9 @@ void luaF_close (lua_State *L, StkId level, int status, int yy) {
   luaF_closeupval(L, level);  /* first, close the upvalues */
   while (L->tbclist >= level) {  /* traverse tbc's down to that level */
     StkId tbc = L->tbclist;  /* get variable index */
-    L->tbclist -= tbc->tbclist.delta;  /* remove it from list */
-    if (!tbc->tbclist.isdummy) {  /* not a dummy entry? */
-      prepcallclosemth(L, tbc, status, yy);  /* close variable */
-      level = restorestack(L, levelrel);
-    }
+    poptbclist(L);  /* remove it from list */
+    prepcallclosemth(L, tbc, status, yy);  /* close variable */
+    level = restorestack(L, levelrel);
   }
 }
 

+ 3 - 2
lobject.h

@@ -139,13 +139,14 @@ typedef struct TValue {
 ** Entries in a Lua stack. Field 'tbclist' forms a list of all
 ** to-be-closed variables active in this stack. Dummy entries are
 ** used when the distance between two tbc variables does not fit
-** in an unsigned short.
+** in an unsigned short. They are represented by delta==0, and
+** their real delta is always the maximum value that fits in
+** that field.
 */
 typedef union StackValue {
   TValue val;
   struct {
     TValuefields;
-    lu_byte isdummy;
     unsigned short delta;
   } tbclist;
 } StackValue;