Lua5.3 VM 分析(二)表处理

来源:互联网 发布:网络春晚2016完整版 编辑:程序博客网 时间:2024/05/29 09:08

Lua5.3 VM 分析(二)表处理

luaV_gettable

luaV_gettable 函数实现了Table类型的读操作,可能触发元方法。

/* ** Main function for table access (invoking metamethods if needed). ** Compute 'val = t[key]' */voidluaV_gettable(lua_State *L, const TValue *t, TValue *key, StkId val){    int loop;  /* counter to avoid infinite loops */    for (loop = 0; loop < MAXTAGLOOP; loop++) {        const TValue *tm;        if (ttistable(t)) {  /* 't' is a table? */            Table *h = hvalue(t);            const TValue *res = luaH_get(h, key); /* do a primitive get */            if (!ttisnil(res) ||  /* result is not nil? */                (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */                setobj2s(L, val, res);  /* result is the raw get */                return;            }            /* else will try metamethod */        }        else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))            luaG_typeerror(L, t, "index");  /* no metamethod */        if (ttisfunction(tm)) {  /* metamethod is a function */            luaT_callTM(L, tm, t, key, val, 1);            return;        }        t = tm;  /* else repeat access over 'tm' */    }    luaG_runerror(L, "gettable chain too long; possible loop");}

OP_GETTABUP、OP_GETTABLE、OP_SELF 这三种指令会调用 luaV_gettable 函数对表做读操作。

处理元表的深度最大为 MAXTAGLOOP (2000)层。越深的嵌套性能越低下。

当操作对象不可以按照 表的模式去索引时,利用luaG_typeerror 抛出异常,中断死循环执行。

如果元表中的index 并不对应一张表,而是一个 函数的时候,就会引发一次元方法调用。它由luaT_callTM 函数实现。

voidluaT_callTM(lua_State *L, const TValue *f, const TValue *p1,                  const TValue *p2, TValue *p3, int hasres){  ptrdiff_t result = savestack(L, p3);  setobj2s(L, L->top++, f);  /* push function (assume EXTRA_STACK) */  setobj2s(L, L->top++, p1);  /* 1st argument */  setobj2s(L, L->top++, p2);  /* 2nd argument */  if (!hasres)  /* no result? 'p3' is third argument */    setobj2s(L, L->top++, p3);  /* 3rd argument */  /* metamethod may yield only when called from Lua code */  luaD_call(L, L->top - (4 - hasres), hasres, isLua(L->ci));  if (hasres) {  /* if has result, move it to its place */    p3 = restorestack(L, result);    setobjs2s(L, p3, --L->top);  }}

callTM 的hasres 参数表示是否需要输出。有输出时,元方法只有两个输入参数(参数一 p1 和 参数二 p2),第三个参数 p3 作为输出。
所有的元方法都有三个参数:
1. 参数一一定是对象本身。 是输入值,只读。
2. 参数二则根据元方法的不同而不同。对于表操作,参数二为Key 值;而对于二元运算操作则是第二个参数的数值。是输入值,只读。
3. 参数三则可以是输入也可以是输出使用。

luaV_settable

luaV_settable 函数实现了Table 类型的写操作,可能触发元方法。

/* ** Main function for table assignment (invoking metamethods if needed). ** Compute 't[key] = val' */voidluaV_settable(lua_State *L, const TValue *t, TValue *key, StkId val){    int loop;  /* counter to avoid infinite loops */    for (loop = 0; loop < MAXTAGLOOP; loop++) {        const TValue *tm;        if (ttistable(t)) {  /* 't' is a table? */            Table *h = hvalue(t);            TValue *oldval = cast(TValue *, luaH_get(h, key));            /* if previous value is not nil, there must be a previous entry             in the table; a metamethod has no relevance */            if (!ttisnil(oldval) ||                /* previous value is nil; must check the metamethod */                ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL &&                 /* no metamethod; is there a previous entry in the table? */                 (oldval != luaO_nilobject ||                  /* no previous entry; must create one. (The next test is                   always true; we only need the assignment.) */                  (oldval = luaH_newkey(L, h, key), 1)))) {                     /* no metamethod and (now) there is an entry with given key */                     setobj2t(L, oldval, val);  /* assign new value to that entry */                     invalidateTMcache(h);                     luaC_barrierback(L, h, val);                     return;                 }            /* else will try the metamethod */        }        else  /* not a table; check metamethod */            if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))                luaG_typeerror(L, t, "index");        /* try the metamethod */        if (ttisfunction(tm)) {            luaT_callTM(L, tm, t, key, val, 0);            return;        }        t = tm;  /* else repeat assignment over 'tm' */    }    luaG_runerror(L, "settable chain too long; possible loop");}

OP_SETTABUP、OP_SETTABLE 这两种指令会调用luaV_settable 函数对表做写操作。

由于Lua 表的删除操作使用 对应 键值设置为nil来实现,所以这里有可能会导致Lua 内其它对象的生命期变化,这涉及到了 GC 的工作。
invalidateTMcache(h);
luaC_barrierback(L, h, val);