1.在table中的末尾插入一个值有两种方式,我们来看有什么不同

a = {}
table.insert(a, 1)
a[#a + 1] = 1

2.将代码解析成token

285(TK_NAME) -> 61(=) -> 123({) -> 125(})
285(TK_NAME) -> 46(.) -> 285(TK_NAME) -> 40(() -> 285(TK_NAME) -> 44(,) ->284(TK_NUMBER)  -> 41())
285(TK_NAME) -> 91([) -> 35(#)-> 285(TK_NAME)-> 43(+)-> 284(TK_NUMBER)-> 93(])-> 61(=)-> 284(TK_NUMBER)

3.实际执行的指令

OP_NEWTABLE
OP_SETGLOBAL

OP_GETGLOBAL
OP_GETTABLE
OP_GETGLOBAL
OP_LOADK
OP_CALL

OP_GETGLOBAL
OP_GETGLOBAL
OP_LEN
OP_ADD
OP_SETTABLE

OP_RETURN

4.指令分析

指令分四部分看:
第一部分OP_NEWTABLE、OP_SETGLOBAL 执行了 a = {}的操作
第二部分 OP_GETGLOBAL、OP_GETTABLE、OP_GETGLOBAL、OP_LOADK、OP_CALL 执行了 table.insert(a, 1)的操作

        case OP_GETGLOBAL:
        {
            TValue g;
            TValue *rb = KBx(i); // 取出值 "table"  (char *)(&(rb)->value.gc->ts + 1)
            sethvalue(L, &g, cl->env);
            gafq_assert(ttisstring(rb));
            Protect(gafqV_gettable(L, &g, rb, ra)); // 取出 g(是一个table) 中rb的值 key 为 "table"得内容 放入 ra寄存器中
            continue;
        }
        case OP_GETTABLE:
        {
            Protect(gafqV_gettable(L, RB(i), RKC(i), ra));  // 上一步我们取出的table, 这时候的RB(i) 就是上个指令存取ra的值,
                                                            // 这时候 我们从RKC(i)的值为insert, 那么这里就是 容table中取出insert 放入ra寄存器中
            continue;
        }
        case OP_GETGLOBAL:
        {
            TValue g;
            TValue *rb = KBx(i); // 取出值 "a"  (char *)(&(rb)->value.gc->ts + 1)
            sethvalue(L, &g, cl->env);
            gafq_assert(ttisstring(rb));
            Protect(gafqV_gettable(L, &g, rb, ra)); // 从g中取出值是a的数据放入ra中
            continue;
        }
        case OP_LOADK:
        {
            setobj2s(L, ra, KBx(i)); // 从常量中KBx(i) 取出数值 1 放入ra中
            continue;
        }
        case OP_CALL:
        {
            //函数调用
            int b = GETARG_B(i);
            int nresults = GETARG_C(i) - 1;
            if (b != 0)
                L->top = ra + b;
            L->savedpc = pc;
            switch (gafqD_precall(L, ra, nresults))
            {
            case PCRC:
            {
                if (nresults >= 0)
                    L->top = L->ci->top;
                base = L->base;
                continue;
            }
            default:
            {
                return; /* yield */
            }
            }
        }
        // 执行gtablib.c中的tinsert方法

        static int tinsert(gafq_State *L)
        {
            int e = aux_getn(L, 1) + 1; /* first empty element */
            int pos;                    /* where to insert new element */
            switch (gafq_gettop(L))
            {
            case 2:
            {            
                pos = e;
                break;
            }
            }
            gafqL_setn(L, 1, e);     /* new size */
            gafq_rawseti(L, 1, pos); /* t[pos] = v */
            return 0;
        }
        GAFQ_API void gafq_rawseti(gafq_State *L, int idx, int n)
        {
            '''
            setobj2t(L, gafqH_setnum(L, hvalue(o), n), L->top - 1);
            '''
        }
        // table获取值
        TValue *gafqH_setnum(gafq_State *L, Table *t, int key)
        {
                TValue k;
                setnvalue(&k, cast_num(key));
                return newkey(L, t, &k);
        }

第三部分 OP_GETGLOBAL、OP_GETGLOBAL、OP_LEN、OP_ADD、OP_SETTABLE 执行了 a[#a + 1] = 1 的操作

        case OP_GETGLOBAL:
        {
            //获取全局
            TValue g;
            TValue *rb = KBx(i); // 取出值a
            sethvalue(L, &g, cl->env);
            gafq_assert(ttisstring(rb));
            Protect(gafqV_gettable(L, &g, rb, ra)); // 把g中的a的表 放入寄存器ra中
            continue;
        }
        case OP_GETGLOBAL:
        {
            //获取全局
            TValue g;
            TValue *rb = KBx(i); // 取出值a 也就是 a[#a + 1] = 2 中的第二个a
            sethvalue(L, &g, cl->env);
            gafq_assert(ttisstring(rb));
            Protect(gafqV_gettable(L, &g, rb, ra)); // 把g中的a的表 放入寄存器ra中
            continue;
        }
        case OP_LEN:
        {
            // 获取长度
            const TValue *rb = RB(i);
            switch (ttype(rb))
            {
            case GAFQ_TTABLE:
            {
                setnvalue(ra, cast_num(gafqH_getn(hvalue(rb)))); // 这里大概是一个二分取长度 与第二部分中的aux_getn是一样的
                break;
            }
            }
            continue;
        }
        case OP_ADD:
        {
            arith_op(gafqi_numadd, TM_ADD);
            continue;
        }
        case OP_SETTABLE:
        {
            Protect(gafqV_settable(L, ra, RKB(i), RKC(i))); // 设置值
            continue;
        }
        void gafqV_settable(gafq_State *L, const TValue *t, TValue *key, StkId val)
        {

            TValue *oldval = gafqH_set(L, h, key); /* do a primitive set */

        }

        TValue *gafqH_set(gafq_State *L, Table *t, const TValue *key)
        {
            return newkey(L, t, key);
        }

第四部分 OP_RETURN 返回结果

5. 结论

从4的分析看来,两者的差别 是最后创建newkey的时候的key是怎么得来的
table.insert(a, 1) 中 是调用了gtablib.c中的tinsert的c函数算来的
a[#a + 1] = 1则是使用OP_ADD计算得来的, 两者在计算当前a的长度 是使用了相同的方法
前者只用c函数 计算 长度 设置 newkey
后者 则使用两个指令 OP_ADD、OP_SETTABLE 完成相同的操作

发表评论