aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-10-14 15:46:58 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-10-14 15:46:58 -0300
commit52c86797608f1bf927be5bab1e9b97b7d35bdf2c (patch)
tree41165b997293675eeb2a40bffe8417939e76c95e
parent849b2ecbd28793408d21ebd734525ab56ae5ca1e (diff)
downloadlua-52c86797608f1bf927be5bab1e9b97b7d35bdf2c.tar.gz
lua-52c86797608f1bf927be5bab1e9b97b7d35bdf2c.tar.bz2
lua-52c86797608f1bf927be5bab1e9b97b7d35bdf2c.zip
Fixed bug of keys removed from tables vs 'next'
Fixed the bug that a key removed from a table might not be found again by 'next'. (This is needed to allow keys to be removed during a traversal.) This bug was introduced in commit 73ec04fc.
-rw-r--r--lgc.c17
-rw-r--r--lobject.h18
-rw-r--r--ltable.c27
-rw-r--r--testes/nextvar.lua32
4 files changed, 67 insertions, 27 deletions
diff --git a/lgc.c b/lgc.c
index 03326df3..5dba56fc 100644
--- a/lgc.c
+++ b/lgc.c
@@ -161,18 +161,17 @@ static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
161 161
162 162
163/* 163/*
164** Clear keys for empty entries in tables. If entry is empty 164** Clear keys for empty entries in tables. If entry is empty, mark its
165** and its key is not marked, mark its entry as dead. This allows the 165** entry as dead. This allows the collection of the key, but keeps its
166** collection of the key, but keeps its entry in the table (its removal 166** entry in the table: its removal could break a chain and could break
167** could break a chain). The main feature of a dead key is that it must 167** a table traversal. Other places never manipulate dead keys, because
168** be different from any other value, to do not disturb searches. 168** its associated empty value is enough to signal that the entry is
169** Other places never manipulate dead keys, because its associated empty 169** logically empty.
170** value is enough to signal that the entry is logically empty.
171*/ 170*/
172static void clearkey (Node *n) { 171static void clearkey (Node *n) {
173 lua_assert(isempty(gval(n))); 172 lua_assert(isempty(gval(n)));
174 if (keyiswhite(n)) 173 if (keyiscollectable(n))
175 setdeadkey(n); /* unused and unmarked key; remove it */ 174 setdeadkey(n); /* unused key; remove it */
176} 175}
177 176
178 177
diff --git a/lobject.h b/lobject.h
index a9d45785..1cc8e757 100644
--- a/lobject.h
+++ b/lobject.h
@@ -21,10 +21,12 @@
21*/ 21*/
22#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ 22#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */
23#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ 23#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */
24#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */
25
24 26
25 27
26/* 28/*
27** number of all possible types (including LUA_TNONE) 29** number of all possible types (including LUA_TNONE but excluding DEADKEY)
28*/ 30*/
29#define LUA_TOTALTYPES (LUA_TPROTO + 2) 31#define LUA_TOTALTYPES (LUA_TPROTO + 2)
30 32
@@ -555,7 +557,7 @@ typedef struct Proto {
555 557
556/* 558/*
557** {================================================================== 559** {==================================================================
558** Closures 560** Functions
559** =================================================================== 561** ===================================================================
560*/ 562*/
561 563
@@ -743,13 +745,13 @@ typedef struct Table {
743 745
744 746
745/* 747/*
746** Use a "nil table" to mark dead keys in a table. Those keys serve 748** Dead keys in tables have the tag DEADKEY but keep their original
747** to keep space for removed entries, which may still be part of 749** gcvalue. This distinguishes them from regular keys but allows them to
748** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE 750** be found when searched in a special way. ('next' needs that to find
749** set, so these values are considered not collectable and are different 751** keys removed from a table during a traversal.)
750** from any valid value.
751*/ 752*/
752#define setdeadkey(n) (keytt(n) = LUA_TTABLE, gckey(n) = NULL) 753#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY)
754#define keyisdead(node) (keytt(node) == LUA_TDEADKEY)
753 755
754/* }================================================================== */ 756/* }================================================================== */
755 757
diff --git a/ltable.c b/ltable.c
index 5a0d066f..38bee1dc 100644
--- a/ltable.c
+++ b/ltable.c
@@ -172,11 +172,17 @@ static Node *mainpositionTV (const Table *t, const TValue *key) {
172** be equal to floats. It is assumed that 'eqshrstr' is simply 172** be equal to floats. It is assumed that 'eqshrstr' is simply
173** pointer equality, so that short strings are handled in the 173** pointer equality, so that short strings are handled in the
174** default case. 174** default case.
175*/ 175** A true 'deadok' means to accept dead keys as equal to their original
176static int equalkey (const TValue *k1, const Node *n2) { 176** values, which can only happen if the original key was collectable.
177 if (rawtt(k1) != keytt(n2)) /* not the same variants? */ 177** All dead values are compared in the default case, by pointer
178** identity. (Note that dead long strings are also compared by
179** identity).
180*/
181static int equalkey (const TValue *k1, const Node *n2, int deadok) {
182 if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
183 !(deadok && keyisdead(n2) && iscollectable(k1)))
178 return 0; /* cannot be same key */ 184 return 0; /* cannot be same key */
179 switch (ttypetag(k1)) { 185 switch (keytt(n2)) {
180 case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: 186 case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
181 return 1; 187 return 1;
182 case LUA_VNUMINT: 188 case LUA_VNUMINT:
@@ -187,7 +193,7 @@ static int equalkey (const TValue *k1, const Node *n2) {
187 return pvalue(k1) == pvalueraw(keyval(n2)); 193 return pvalue(k1) == pvalueraw(keyval(n2));
188 case LUA_VLCF: 194 case LUA_VLCF:
189 return fvalue(k1) == fvalueraw(keyval(n2)); 195 return fvalue(k1) == fvalueraw(keyval(n2));
190 case LUA_VLNGSTR: 196 case ctb(LUA_VLNGSTR):
191 return luaS_eqlngstr(tsvalue(k1), keystrval(n2)); 197 return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
192 default: 198 default:
193 return gcvalue(k1) == gcvalueraw(keyval(n2)); 199 return gcvalue(k1) == gcvalueraw(keyval(n2));
@@ -251,11 +257,12 @@ static unsigned int setlimittosize (Table *t) {
251/* 257/*
252** "Generic" get version. (Not that generic: not valid for integers, 258** "Generic" get version. (Not that generic: not valid for integers,
253** which may be in array part, nor for floats with integral values.) 259** which may be in array part, nor for floats with integral values.)
260** See explanation about 'deadok' in function 'equalkey'.
254*/ 261*/
255static const TValue *getgeneric (Table *t, const TValue *key) { 262static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
256 Node *n = mainpositionTV(t, key); 263 Node *n = mainpositionTV(t, key);
257 for (;;) { /* check whether 'key' is somewhere in the chain */ 264 for (;;) { /* check whether 'key' is somewhere in the chain */
258 if (equalkey(key, n)) 265 if (equalkey(key, n, deadok))
259 return gval(n); /* that's it */ 266 return gval(n); /* that's it */
260 else { 267 else {
261 int nx = gnext(n); 268 int nx = gnext(n);
@@ -292,7 +299,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key,
292 if (i - 1u < asize) /* is 'key' inside array part? */ 299 if (i - 1u < asize) /* is 'key' inside array part? */
293 return i; /* yes; that's the index */ 300 return i; /* yes; that's the index */
294 else { 301 else {
295 const TValue *n = getgeneric(t, key); 302 const TValue *n = getgeneric(t, key, 1);
296 if (unlikely(isabstkey(n))) 303 if (unlikely(isabstkey(n)))
297 luaG_runerror(L, "invalid key to 'next'"); /* key not found */ 304 luaG_runerror(L, "invalid key to 'next'"); /* key not found */
298 i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ 305 i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
@@ -730,7 +737,7 @@ const TValue *luaH_getstr (Table *t, TString *key) {
730 else { /* for long strings, use generic case */ 737 else { /* for long strings, use generic case */
731 TValue ko; 738 TValue ko;
732 setsvalue(cast(lua_State *, NULL), &ko, key); 739 setsvalue(cast(lua_State *, NULL), &ko, key);
733 return getgeneric(t, &ko); 740 return getgeneric(t, &ko, 0);
734 } 741 }
735} 742}
736 743
@@ -750,7 +757,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
750 /* else... */ 757 /* else... */
751 } /* FALLTHROUGH */ 758 } /* FALLTHROUGH */
752 default: 759 default:
753 return getgeneric(t, key); 760 return getgeneric(t, key, 0);
754 } 761 }
755} 762}
756 763
diff --git a/testes/nextvar.lua b/testes/nextvar.lua
index a16d557b..29cb05d5 100644
--- a/testes/nextvar.lua
+++ b/testes/nextvar.lua
@@ -359,6 +359,38 @@ end
359assert(n == 5) 359assert(n == 5)
360 360
361 361
362do
363 print("testing next x GC of deleted keys")
364 -- bug in 5.4.1
365 local co = coroutine.wrap(function (t)
366 for k, v in pairs(t) do
367 local k1 = next(t) -- all previous keys were deleted
368 assert(k == k1) -- current key is the first in the table
369 t[k] = nil
370 local expected = (type(k) == "table" and k[1] or
371 type(k) == "function" and k() or
372 string.sub(k, 1, 1))
373 assert(expected == v)
374 coroutine.yield(v)
375 end
376 end)
377 local t = {}
378 t[{1}] = 1 -- add several unanchored, collectable keys
379 t[{2}] = 2
380 t[string.rep("a", 50)] = "a" -- long string
381 t[string.rep("b", 50)] = "b"
382 t[{3}] = 3
383 t[string.rep("c", 10)] = "c" -- short string
384 t[function () return 10 end] = 10
385 local count = 7
386 while co(t) do
387 collectgarbage("collect") -- collect dead keys
388 count = count - 1
389 end
390 assert(count == 0 and next(t) == nil) -- traversed the whole table
391end
392
393
362local function test (a) 394local function test (a)
363 assert(not pcall(table.insert, a, 2, 20)); 395 assert(not pcall(table.insert, a, 2, 20));
364 table.insert(a, 10); table.insert(a, 2, 20); 396 table.insert(a, 10); table.insert(a, 2, 20);