first version of empty entries in tables

(so that, in the future, tables can contain regular nil entries)
This commit is contained in:
Roberto Ierusalimschy 2018-02-23 10:16:18 -03:00
parent 477ca2fe8c
commit 9243c414d9
11 changed files with 146 additions and 99 deletions

30
lapi.c
View File

@ -1,5 +1,5 @@
/*
** $Id: lapi.c,v 2.283 2018/02/05 17:10:52 roberto Exp roberto $
** $Id: lapi.c,v 2.285 2018/02/20 16:52:50 roberto Exp roberto $
** Lua API
** See Copyright Notice in lua.h
*/
@ -657,14 +657,26 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
}
static int finishrawget (lua_State *L, const TValue *val) {
if (isempty(val)) /* avoid copying empty items to the stack */
setnilvalue(s2v(L->top));
else
setobj2s(L, L->top, val);
api_incr_top(L);
lua_unlock(L);
return ttnov(s2v(L->top - 1));
}
LUA_API int lua_rawget (lua_State *L, int idx) {
TValue *t;
const TValue *val;
lua_lock(L);
t = index2value(L, idx);
api_check(L, ttistable(t), "table expected");
setobj2s(L, L->top - 1, luaH_get(hvalue(t), s2v(L->top - 1)));
lua_unlock(L);
return ttnov(s2v(L->top - 1));
val = luaH_get(hvalue(t), s2v(L->top - 1));
L->top--; /* remove key */
return finishrawget(L, val);
}
@ -673,10 +685,7 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) {
lua_lock(L);
t = index2value(L, idx);
api_check(L, ttistable(t), "table expected");
setobj2s(L, L->top, luaH_getint(hvalue(t), n));
api_incr_top(L);
lua_unlock(L);
return ttnov(s2v(L->top - 1));
return finishrawget(L, luaH_getint(hvalue(t), n));
}
@ -687,10 +696,7 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
t = index2value(L, idx);
api_check(L, ttistable(t), "table expected");
setpvalue(&k, cast_voidp(p));
setobj2s(L, L->top, luaH_get(hvalue(t), &k));
api_incr_top(L);
lua_unlock(L);
return ttnov(s2v(L->top - 1));
return finishrawget(L, luaH_get(hvalue(t), &k));
}

58
lgc.c
View File

@ -145,12 +145,13 @@ static GCObject **getgclist (GCObject *o) {
** Clear keys for empty entries in tables. If entry is empty
** and its key is not marked, mark its entry as dead. This allows the
** collection of the key, but keeps its entry in the table (its removal
** could break a chain). Other places never manipulate dead keys,
** because its associated nil value is enough to signal that the entry
** is logically empty.
** could break a chain). The main feature of a dead key is that it must
** be different from any other value, to do not disturb searches.
** Other places never manipulate dead keys, because its associated empty
** value is enough to signal that the entry is logically empty.
*/
static void removeentry (Node *n) {
lua_assert(ttisnil(gval(n)));
static void clearkey (Node *n) {
lua_assert(isempty(gval(n)));
if (keyiswhite(n))
setdeadkey(n); /* unused and unmarked key; remove it */
}
@ -386,8 +387,8 @@ static void traverseweakvalue (global_State *g, Table *h) {
worth traversing it now just to check) */
int hasclears = (h->sizearray > 0);
for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */
if (isempty(gval(n))) /* entry is empty? */
clearkey(n); /* clear its key */
else {
lua_assert(!keyisnil(n));
markkey(g, n);
@ -428,8 +429,8 @@ static int traverseephemeron (global_State *g, Table *h) {
}
/* traverse hash part */
for (n = gnode(h, 0); n < limit; n++) {
if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */
if (isempty(gval(n))) /* entry is empty? */
clearkey(n); /* clear its key */
else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
hasclears = 1; /* table must be cleared */
if (valiswhite(gval(n))) /* value not marked yet? */
@ -461,8 +462,8 @@ static void traversestrongtable (global_State *g, Table *h) {
for (i = 0; i < h->sizearray; i++) /* traverse array part */
markvalue(g, &h->array[i]);
for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
if (ttisnil(gval(n))) /* entry is empty? */
removeentry(n); /* remove it */
if (isempty(gval(n))) /* entry is empty? */
clearkey(n); /* clear its key */
else {
lua_assert(!keyisnil(n));
markkey(g, n);
@ -681,15 +682,16 @@ static void clearprotolist (global_State *g) {
/*
** clear entries with unmarked keys from all weaktables in list 'l'
*/
static void clearkeys (global_State *g, GCObject *l) {
static void clearbykeys (global_State *g, GCObject *l) {
for (; l; l = gco2t(l)->gclist) {
Table *h = gco2t(l);
Node *n, *limit = gnodelast(h);
Node *limit = gnodelast(h);
Node *n;
for (n = gnode(h, 0); n < limit; n++) {
if (!ttisnil(gval(n)) && (iscleared(g, gckeyN(n)))) /* unmarked key? */
setnilvalue(gval(n)); /* clear value */
if (ttisnil(gval(n))) /* is entry empty? */
removeentry(n); /* remove it from table */
if (isempty(gval(n))) /* is entry empty? */
clearkey(n); /* clear its key */
else if (iscleared(g, gckeyN(n))) /* unmarked key? */
setempty(gval(n)); /* remove entry */
}
}
}
@ -699,7 +701,7 @@ static void clearkeys (global_State *g, GCObject *l) {
** clear entries with unmarked values from all weaktables in list 'l' up
** to element 'f'
*/
static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
for (; l != f; l = gco2t(l)->gclist) {
Table *h = gco2t(l);
Node *n, *limit = gnodelast(h);
@ -707,13 +709,13 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
for (i = 0; i < h->sizearray; i++) {
TValue *o = &h->array[i];
if (iscleared(g, gcvalueN(o))) /* value was collected? */
setnilvalue(o); /* remove value */
setempty(o); /* remove entry */
}
for (n = gnode(h, 0); n < limit; n++) {
if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */
setnilvalue(gval(n)); /* clear value */
if (ttisnil(gval(n))) /* is entry empty? */
removeentry(n); /* remove it from table */
setempty(gval(n)); /* remove entry */
if (isempty(gval(n))) /* is entry empty? */
clearkey(n); /* clear its key */
}
}
}
@ -1372,8 +1374,8 @@ static lu_mem atomic (lua_State *L) {
convergeephemerons(g);
/* at this point, all strongly accessible objects are marked. */
/* Clear values from weak tables, before checking finalizers */
clearvalues(g, g->weak, NULL);
clearvalues(g, g->allweak, NULL);
clearbyvalues(g, g->weak, NULL);
clearbyvalues(g, g->allweak, NULL);
origweak = g->weak; origall = g->allweak;
separatetobefnz(g, 0); /* separate objects to be finalized */
work += markbeingfnz(g); /* mark objects that will be finalized */
@ -1381,11 +1383,11 @@ static lu_mem atomic (lua_State *L) {
convergeephemerons(g);
/* at this point, all resurrected objects are marked. */
/* remove dead objects from weak tables */
clearkeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
clearkeys(g, g->allweak); /* clear keys from all 'allweak' tables */
clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */
/* clear values from resurrected weak tables */
clearvalues(g, g->weak, origweak);
clearvalues(g, g->allweak, origall);
clearbyvalues(g, g->weak, origweak);
clearbyvalues(g, g->allweak, origall);
luaS_clearcache(g);
clearprotolist(g);
g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */

4
llex.c
View File

@ -1,5 +1,5 @@
/*
** $Id: llex.c,v 2.98 2017/06/29 15:06:44 roberto Exp roberto $
** $Id: llex.c,v 2.99 2018/01/28 15:13:26 roberto Exp roberto $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
@ -130,7 +130,7 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
TString *ts = luaS_newlstr(L, str, l); /* create new string */
setsvalue2s(L, L->top++, ts); /* temporarily anchor it in stack */
o = luaH_set(L, ls->h, s2v(L->top - 1));
if (ttisnil(o)) { /* not in use yet? */
if (isempty(o)) { /* not in use yet? */
/* boolean value does not need GC barrier;
table is not a metatable, so it does not need to invalidate cache */
setbvalue(o, 1); /* t[string] = true */

View File

@ -1,5 +1,5 @@
/*
** $Id: lobject.h,v 2.135 2018/02/21 16:28:12 roberto Exp roberto $
** $Id: lobject.h,v 2.136 2018/02/22 17:28:10 roberto Exp roberto $
** Type definitions for Lua objects
** See Copyright Notice in lua.h
*/
@ -100,7 +100,7 @@ typedef struct TValue {
#define setobj(L,obj1,obj2) \
{ TValue *io1=(obj1); const TValue *io2=(obj2); \
io1->value_ = io2->value_; io1->tt_ = io2->tt_; \
(void)L; checkliveness(L,io1); }
(void)L; checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
/*
** different types of assignments, according to destination
@ -148,6 +148,30 @@ typedef StackValue *StkId; /* index to stack elements */
/* (address of) a fixed nil value */
#define luaO_nilobject (&luaO_nilobject_)
/*
** Variant tag, used only in tables to signal an empty slot
** (which might be different from a slot containing nil)
*/
#define LUA_TEMPTY (LUA_TNIL | (1 << 4))
#define ttisnilorempty(v) checktype((v), LUA_TNIL)
/*
** By default, entries with any kind of nil are considered empty
*/
#define isempty(v) ttisnilorempty(v)
#define isreallyempty(v) checktag((v), LUA_TEMPTY)
/* macro defining an empty value */
#define EMPTYCONSTANT {NULL}, LUA_TEMPTY
/* mark an entry as empty */
#define setempty(v) settt_(v, LUA_TEMPTY)
/* }================================================================== */
@ -644,7 +668,7 @@ typedef struct Table {
/*
** Use a "nil table" to mark dead keys in a table. Those keys serve
** only to keep space for removed entries, which may still be part of
** to keep space for removed entries, which may still be part of
** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE
** set, so these values are considered not collectable and are different
** from any valid value.

View File

@ -1,5 +1,5 @@
/*
** $Id: ltable.c,v 2.132 2018/02/19 20:06:56 roberto Exp roberto $
** $Id: ltable.c,v 2.133 2018/02/21 12:54:26 roberto Exp roberto $
** Lua tables (hash)
** See Copyright Notice in lua.h
*/
@ -88,11 +88,14 @@
#define dummynode (&dummynode_)
static const Node dummynode_ = {
{{NULL}, LUA_TNIL, /* value's value and type */
{{NULL}, LUA_TEMPTY, /* value's value and type */
LUA_TNIL, 0, {NULL}} /* key type, next, and key value */
};
LUAI_DDEF const TValue luaH_emptyobject_ = {EMPTYCONSTANT};
/*
** Hash for floating-point numbers.
** The main computation should be just
@ -200,7 +203,7 @@ static const TValue *getgeneric (Table *t, const TValue *key) {
else {
int nx = gnext(n);
if (nx == 0)
return luaO_nilobject; /* not found */
return luaH_emptyobject; /* not found */
n += nx;
}
}
@ -232,7 +235,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key) {
return i; /* yes; that's the index */
else {
const TValue *n = getgeneric(t, key);
if (n == luaO_nilobject)
if (n == luaH_emptyobject)
luaG_runerror(L, "invalid key to 'next'"); /* key not found */
i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
/* hash elements are numbered after array ones */
@ -244,14 +247,14 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key) {
int luaH_next (lua_State *L, Table *t, StkId key) {
unsigned int i = findindex(L, t, s2v(key)); /* find original element */
for (; i < t->sizearray; i++) { /* try first array part */
if (!ttisnil(&t->array[i])) { /* a non-nil value? */
if (!isempty(&t->array[i])) { /* a non-empty entry? */
setivalue(s2v(key), i + 1);
setobj2s(L, key + 1, &t->array[i]);
return 1;
}
}
for (i -= t->sizearray; cast_int(i) < sizenode(t); i++) { /* hash part */
if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */
if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */
Node *n = gnode(t, i);
getnodekey(L, s2v(key), n);
setobj2s(L, key + 1, gval(n));
@ -336,7 +339,7 @@ static unsigned int numusearray (const Table *t, unsigned int *nums) {
}
/* count elements in range (2^(lg - 1), 2^lg] */
for (; i <= lim; i++) {
if (!ttisnil(&t->array[i-1]))
if (!isempty(&t->array[i-1]))
lc++;
}
nums[lg] += lc;
@ -352,7 +355,7 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
int i = sizenode(t);
while (i--) {
Node *n = &t->node[i];
if (!ttisnil(gval(n))) {
if (!isempty(gval(n))) {
if (keyisinteger(n))
ause += countint(keyival(n), nums);
totaluse++;
@ -387,7 +390,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) {
Node *n = gnode(t, i);
gnext(n) = 0;
setnilkey(n);
setnilvalue(gval(n));
setempty(gval(n));
}
t->lsizenode = cast_byte(lsize);
t->lastfree = gnode(t, size); /* all positions are free */
@ -403,7 +406,7 @@ static void reinsert (lua_State *L, Table *ot, Table *t) {
int size = sizenode(ot);
for (j = 0; j < size; j++) {
Node *old = gnode(ot, j);
if (!ttisnil(gval(old))) {
if (!isempty(gval(old))) {
/* doesn't need barrier/invalidate cache, as entry was
already present in the table */
TValue k;
@ -456,7 +459,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
exchangehashpart(t, &newt); /* and new hash */
/* re-insert into the new hash the elements from vanishing slice */
for (i = newasize; i < oldasize; i++) {
if (!ttisnil(&t->array[i]))
if (!isempty(&t->array[i]))
luaH_setint(L, t, i + 1, &t->array[i]);
}
t->sizearray = oldasize; /* restore current size... */
@ -473,7 +476,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
t->array = newarray; /* set new array part */
t->sizearray = newasize;
for (i = oldasize; i < newasize; i++) /* clear new slice of the array */
setnilvalue(&t->array[i]);
setempty(&t->array[i]);
/* re-insert elements from old hash part into new parts */
reinsert(L, &newt, t); /* 'newt' now has the old hash */
freehash(L, &newt); /* free old hash part */
@ -569,7 +572,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
luaG_runerror(L, "table index is NaN");
}
mp = mainpositionTV(t, key);
if (!ttisnil(gval(mp)) || isdummy(t)) { /* main position is taken? */
if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
Node *othern;
Node *f = getfreepos(t); /* get a free place */
if (f == NULL) { /* cannot find a free place? */
@ -589,7 +592,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
gnext(f) += cast_int(mp - f); /* correct 'next' */
gnext(mp) = 0; /* now 'mp' is free */
}
setnilvalue(gval(mp));
setempty(gval(mp));
}
else { /* colliding node is in its own main position */
/* new node will go into free position */
@ -602,7 +605,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
}
setnodekey(L, mp, key);
luaC_barrierback(L, obj2gco(t), key);
lua_assert(ttisnil(gval(mp)));
lua_assert(isempty(gval(mp)));
return gval(mp);
}
@ -625,7 +628,7 @@ const TValue *luaH_getint (Table *t, lua_Integer key) {
n += nx;
}
}
return luaO_nilobject;
return luaH_emptyobject;
}
}
@ -642,7 +645,7 @@ const TValue *luaH_getshortstr (Table *t, TString *key) {
else {
int nx = gnext(n);
if (nx == 0)
return luaO_nilobject; /* not found */
return luaH_emptyobject; /* not found */
n += nx;
}
}
@ -667,7 +670,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
switch (ttype(key)) {
case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key));
case LUA_TNUMINT: return luaH_getint(t, ivalue(key));
case LUA_TNIL: return luaO_nilobject;
case LUA_TNIL: return luaH_emptyobject;
case LUA_TNUMFLT: {
lua_Integer k;
if (luaV_flttointeger(fltvalue(key), &k, 0)) /* index is an integral? */
@ -686,7 +689,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
*/
TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
const TValue *p = luaH_get(t, key);
if (p != luaO_nilobject)
if (p != luaH_emptyobject)
return cast(TValue *, p);
else return luaH_newkey(L, t, key);
}
@ -695,7 +698,7 @@ TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
const TValue *p = luaH_getint(t, key);
TValue *cell;
if (p != luaO_nilobject)
if (p != luaH_emptyobject)
cell = cast(TValue *, p);
else {
TValue k;
@ -728,16 +731,16 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
j *= 2;
else {
j = LUA_MAXINTEGER;
if (ttisnil(luaH_getint(t, j))) /* t[j] == nil? */
if (isempty(luaH_getint(t, j))) /* t[j] not present? */
break; /* 'j' now is an absent index */
else /* weird case */
return j; /* well, max integer is a boundary... */
}
} while (!ttisnil(luaH_getint(t, j))); /* repeat until t[j] == nil */
/* i < j && t[i] !≃ nil && t[j] == nil */
} while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */
/* i < j && t[i] present && t[j] absent */
while (j - i > 1u) { /* do a binary search between them */
lua_Unsigned m = (i + j) / 2;
if (ttisnil(luaH_getint(t, m))) j = m;
if (isempty(luaH_getint(t, m))) j = m;
else i = m;
}
return i;
@ -746,27 +749,27 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
/*
** Try to find a boundary in table 't'. (A 'boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil, plus 0 if t[1] is nil
** and 'maxinteger' if t[maxinteger] is not nil.)
** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent
** and 'maxinteger' if t[maxinteger] is present.)
** First, try the array part: if there is an array part and its last
** element is nil, there must be a boundary there; a binary search
** element is absent, there must be a boundary there; a binary search
** finds that boundary. Otherwise, if the hash part is empty or does not
** contain 'j + 1', 'j' is a boundary. Otherwize, call 'hash_search'
** to find a boundary in the hash part.
*/
lua_Unsigned luaH_getn (Table *t) {
unsigned int j = t->sizearray;
if (j > 0 && ttisnil(&t->array[j - 1])) {
if (j > 0 && isempty(&t->array[j - 1])) {
unsigned int i = 0;
while (j - i > 1u) { /* binary search */
unsigned int m = (i + j) / 2;
if (ttisnil(&t->array[m - 1])) j = m;
if (isempty(&t->array[m - 1])) j = m;
else i = m;
}
return i;
}
else { /* 'j' is zero or present in table */
if (isdummy(t) || ttisnil(luaH_getint(t, l_castU2S(j + 1))))
if (isdummy(t) || isempty(luaH_getint(t, l_castU2S(j + 1))))
return j; /* 'j + 1' is absent... */
else /* 'j + 1' is also present */
return hash_search(t, j);

View File

@ -1,5 +1,5 @@
/*
** $Id: ltable.h,v 2.24 2017/05/19 12:48:15 roberto Exp roberto $
** $Id: ltable.h,v 2.25 2017/06/09 16:48:44 roberto Exp roberto $
** Lua tables (hash)
** See Copyright Notice in lua.h
*/
@ -21,6 +21,8 @@
/* true when 't' is using 'dummynode' as its hash part */
#define isdummy(t) ((t)->lastfree == NULL)
#define luaH_emptyobject (&luaH_emptyobject_)
/* allocated size for hash nodes */
#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t))
@ -30,6 +32,9 @@
#define nodefromval(v) cast(Node *, (v))
LUAI_DDEC const TValue luaH_emptyobject_;
LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
TValue *value);

View File

@ -1,5 +1,5 @@
/*
** $Id: ltests.c,v 2.240 2018/01/28 15:13:26 roberto Exp roberto $
** $Id: ltests.c,v 2.241 2018/02/20 16:52:50 roberto Exp roberto $
** Internal Module for Debugging of the Lua Implementation
** See Copyright Notice in lua.h
*/
@ -254,7 +254,7 @@ static void checktable (global_State *g, Table *h) {
for (i = 0; i < h->sizearray; i++)
checkvalref(g, hgc, &h->array[i]);
for (n = gnode(h, 0); n < limit; n++) {
if (!ttisnil(gval(n))) {
if (!isempty(gval(n))) {
TValue k;
getnodekey(g->mainthread, &k, n);
lua_assert(!keyisnil(n));
@ -842,7 +842,7 @@ static int table_query (lua_State *L) {
else if ((i -= t->sizearray) < sizenode(t)) {
TValue k;
getnodekey(L, &k, gnode(t, i));
if (!ttisnil(gval(gnode(t, i))) ||
if (!isempty(gval(gnode(t, i))) ||
ttisnil(&k) ||
ttisnumber(&k)) {
pushobject(L, &k);

8
ltm.c
View File

@ -1,5 +1,5 @@
/*
** $Id: ltm.c,v 2.62 2018/02/17 19:20:00 roberto Exp roberto $
** $Id: ltm.c,v 2.63 2018/02/21 15:49:32 roberto Exp roberto $
** Tag methods
** See Copyright Notice in lua.h
*/
@ -60,7 +60,7 @@ void luaT_init (lua_State *L) {
const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
const TValue *tm = luaH_getshortstr(events, ename);
lua_assert(event <= TM_EQ);
if (ttisnil(tm)) { /* no tag method? */
if (notm(tm)) { /* no tag method? */
events->flags |= cast_byte(1u<<event); /* cache this fact */
return NULL;
}
@ -137,9 +137,9 @@ void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
StkId res, TMS event) {
const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
if (ttisnil(tm))
if (notm(tm))
tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
if (ttisnil(tm)) return 0;
if (notm(tm)) return 0;
luaT_callTMres(L, tm, p1, p2, res);
return 1;
}

8
ltm.h
View File

@ -1,5 +1,5 @@
/*
** $Id: ltm.h,v 2.31 2018/02/09 15:16:06 roberto Exp roberto $
** $Id: ltm.h,v 2.32 2018/02/17 19:20:00 roberto Exp roberto $
** Tag methods
** See Copyright Notice in lua.h
*/
@ -45,6 +45,12 @@ typedef enum {
} TMS;
/*
** Test whether there is no tagmethod.
** (Because tagmethods use raw accesses, the result may be an "empty" nil.)
*/
#define notm(tm) ttisnilorempty(tm)
#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))

21
lvm.c
View File

@ -1,5 +1,5 @@
/*
** $Id: lvm.c,v 2.345 2018/02/21 15:49:32 roberto Exp roberto $
** $Id: lvm.c,v 2.346 2018/02/21 19:43:44 roberto Exp roberto $
** Lua virtual machine
** See Copyright Notice in lua.h
*/
@ -168,7 +168,7 @@ static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step,
/*
** Finish the table access 'val = t[key]'.
** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to
** t[k] entry (which must be nil).
** t[k] entry (which must be empty).
*/
void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
const TValue *slot) {
@ -178,12 +178,12 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
if (slot == NULL) { /* 't' is not a table? */
lua_assert(!ttistable(t));
tm = luaT_gettmbyobj(L, t, TM_INDEX);
if (ttisnil(tm))
if (notm(tm))
luaG_typeerror(L, t, "index"); /* no metamethod */
/* else will try the metamethod */
}
else { /* 't' is a table */
lua_assert(ttisnil(slot));
lua_assert(isempty(slot));
tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */
if (tm == NULL) { /* no metamethod? */
setnilvalue(s2v(val)); /* result is nil */
@ -209,8 +209,8 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
/*
** Finish a table assignment 't[key] = val'.
** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points
** to the entry 't[key]', or to 'luaO_nilobject' if there is no such
** entry. (The value at 'slot' must be nil, otherwise 'luaV_fastget'
** to the entry 't[key]', or to 'luaH_emptyobject' if there is no such
** entry. (The value at 'slot' must be empty, otherwise 'luaV_fastget'
** would have done the job.)
*/
void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
@ -220,10 +220,10 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
const TValue *tm; /* '__newindex' metamethod */
if (slot != NULL) { /* is 't' a table? */
Table *h = hvalue(t); /* save 't' table */
lua_assert(ttisnil(slot)); /* old value must be nil */
lua_assert(isempty(slot)); /* slot must be empty */
tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
if (tm == NULL) { /* no metamethod? */
if (slot == luaO_nilobject) /* no previous entry? */
if (slot == luaH_emptyobject) /* no previous entry? */
slot = luaH_newkey(L, h, key); /* create one */
/* no metamethod and (now) there is an entry with given key */
setobj2t(L, cast(TValue *, slot), val); /* set its new value */
@ -234,7 +234,8 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
/* else will try the metamethod */
}
else { /* not a table; check metamethod */
if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
if (notm(tm))
luaG_typeerror(L, t, "index");
}
/* try the metamethod */
@ -586,7 +587,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
}
default: { /* try metamethod */
tm = luaT_gettmbyobj(L, rb, TM_LEN);
if (ttisnil(tm)) /* no metamethod? */
if (notm(tm)) /* no metamethod? */
luaG_typeerror(L, rb, "get length of");
break;
}

10
lvm.h
View File

@ -1,5 +1,5 @@
/*
** $Id: lvm.h,v 2.49 2018/02/19 20:06:56 roberto Exp roberto $
** $Id: lvm.h,v 2.50 2018/02/21 12:54:26 roberto Exp roberto $
** Lua virtual machine
** See Copyright Notice in lua.h
*/
@ -64,17 +64,17 @@
/*
** fast track for 'gettable': if 't' is a table and 't[k]' is not nil,
** fast track for 'gettable': if 't' is a table and 't[k]' is present,
** return 1 with 'slot' pointing to 't[k]' (position of final result).
** Otherwise, return 0 (meaning it will have to check metamethod)
** with 'slot' pointing to a nil 't[k]' (if 't' is a table) or NULL
** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL
** (otherwise). 'f' is the raw get function to use.
*/
#define luaV_fastget(L,t,k,slot,f) \
(!ttistable(t) \
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
: (slot = f(hvalue(t), k), /* else, do raw access */ \
!ttisnil(slot))) /* result not nil? */
!isempty(slot))) /* result not empty? */
/*
@ -86,7 +86,7 @@
? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
: (slot = (l_castS2U(k) - 1u < hvalue(t)->sizearray) \
? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
!ttisnil(slot))) /* result not nil? */
!isempty(slot))) /* result not empty? */
/*