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.)
diff --git a/lfunc.c b/lfunc.c
index b4c04bd..f5889a2 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -155,6 +155,15 @@
/*
+** 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.
*/
void luaF_newtbcupval (lua_State *L, StkId level) {
@@ -162,13 +171,11 @@
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;
}
@@ -202,6 +209,19 @@
/*
+** 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 @@
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);
}
}
diff --git a/lobject.h b/lobject.h
index 1a7a737..950bebb 100644
--- a/lobject.h
+++ b/lobject.h
@@ -139,13 +139,14 @@
** 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;