Example #1
0
        private void V_Execute()
        {
            ExecuteEnvironment env;
            CallInfo           ci = CI;

newframe:
            Utl.Assert(ci == CI);
            LuaLClosureValue cl = Stack[ci.FuncIndex].V.ClLValue();

            env.Stack = Stack;
            env.K     = cl.Proto.K;
            env.Base  = ci.BaseIndex;

#if DEBUG_NEW_FRAME
            ULDebug.Log("#### NEW FRAME #########################################################################");
            ULDebug.Log("## cl:" + cl);
            ULDebug.Log("## Base:" + env.Base);
            ULDebug.Log("########################################################################################");
#endif

            while (true)
            {
                Instruction i = ci.SavedPc.ValueInc;
                env.I = i;

#if DEBUG_SRC_INFO
                int    line = 0;
                string src  = "";
                if (ci.IsLua)
                {
                    line = GetCurrentLine(ci);
                    src  = GetCurrentLuaFunc(ci).Proto.Source;
                }
#endif

                StkId ra = env.RA;

#if DEBUG_DUMP_INS_STACK
#if DEBUG_DUMP_INS_STACK_EX
                DumpStack(env.Base, i.ToString());
#else
                DumpStack(env.Base);
#endif
#endif

#if DEBUG_INSTRUCTION
                ULDebug.Log(System.DateTime.Now + " [VM] ======================================================================== Instruction: " + i
#if DEBUG_INSTRUCTION_WITH_STACK
                            + "\n" + DumpStackToString(env.Base.Index)
#endif
                            );
#endif

#if DEBUG_RECORD_INS
                InstructionHistory.Enqueue(i);
                if (InstructionHistory.Count > 100)
                {
                    InstructionHistory.Dequeue();
                }
#endif

                switch (i.GET_OPCODE())
                {
                case OpCode.OP_MOVE:
                {
                    StkId rb = env.RB;

#if DEBUG_OP_MOVE
                    ULDebug.Log("[VM] ==== OP_MOVE rb:" + rb);
                    ULDebug.Log("[VM] ==== OP_MOVE ra:" + ra);
#endif

                    ra.V.SetObj(ref rb.V);
                    break;
                }

                case OpCode.OP_LOADK:
                {
                    StkId rb = env.K[i.GETARG_Bx()];
                    ra.V.SetObj(ref rb.V);
                    break;
                }

                case OpCode.OP_LOADKX:
                {
                    Utl.Assert(ci.SavedPc.Value.GET_OPCODE() == OpCode.OP_EXTRAARG);
                    StkId rb = env.K[ci.SavedPc.ValueInc.GETARG_Ax()];
                    ra.V.SetObj(ref rb.V);
                    break;
                }

                case OpCode.OP_LOADBOOL:
                {
                    ra.V.SetBValue(i.GETARG_B() != 0);
                    if (i.GETARG_C() != 0)
                    {
                        ci.SavedPc.Index += 1;                                 // skip next instruction (if C)
                    }
                    break;
                }

                case OpCode.OP_LOADNIL:
                {
                    int b     = i.GETARG_B();
                    int index = ra.Index;
                    do
                    {
                        Stack[index++].V.SetNilValue();
                    } while (b-- > 0);
                    break;
                }

                case OpCode.OP_GETUPVAL:
                {
                    int b = i.GETARG_B();
                    ra.V.SetObj(ref cl.Upvals[b].V.V);

#if DEBUG_OP_GETUPVAL
                    // for( var j=0; j<cl.Upvals.Length; ++j)
                    // {
                    //  ULDebug.Log("[VM] ==== GETUPVAL upval:" + cl.Upvals[j] );
                    // }
                    ULDebug.Log("[VM] ==== GETUPVAL b:" + b);
                    ULDebug.Log("[VM] ==== GETUPVAL ra:" + ra);
#endif
                    break;
                }

                case OpCode.OP_GETTABUP:
                {
                    int   b   = i.GETARG_B();
                    StkId key = env.RKC;
                    V_GetTable(cl.Upvals[b].V, key, ra);
#if DEBUG_OP_GETTABUP
                    ULDebug.Log("[VM] ==== OP_GETTABUP key:" + key);
                    ULDebug.Log("[VM] ==== OP_GETTABUP val:" + ra);
#endif
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_GETTABLE:
                {
                    StkId tbl = env.RB;
                    StkId key = env.RKC;
                    StkId val = ra;
                    V_GetTable(tbl, key, val);
#if DEBUG_OP_GETTABLE
                    ULDebug.Log("[VM] ==== OP_GETTABLE key:" + key.ToString());
                    ULDebug.Log("[VM] ==== OP_GETTABLE val:" + val.ToString());
#endif
                    break;
                }

                case OpCode.OP_SETTABUP:
                {
                    int a = i.GETARG_A();

                    StkId key = env.RKB;
                    StkId val = env.RKC;
                    V_SetTable(cl.Upvals[a].V, key, val);
#if DEBUG_OP_SETTABUP
                    ULDebug.Log("[VM] ==== OP_SETTABUP key:" + key.Value);
                    ULDebug.Log("[VM] ==== OP_SETTABUP val:" + val.Value);
#endif
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_SETUPVAL:
                {
                    int        b  = i.GETARG_B();
                    LuaUpvalue uv = cl.Upvals[b];
                    uv.V.V.SetObj(ref ra.V);
#if DEBUG_OP_SETUPVAL
                    ULDebug.Log("[VM] ==== SETUPVAL b:" + b);
                    ULDebug.Log("[VM] ==== SETUPVAL ra:" + ra);
#endif
                    break;
                }

                case OpCode.OP_SETTABLE:
                {
                    StkId key = env.RKB;
                    StkId val = env.RKC;
#if DEBUG_OP_SETTABLE
                    ULDebug.Log("[VM] ==== OP_SETTABLE key:" + key.ToString());
                    ULDebug.Log("[VM] ==== OP_SETTABLE val:" + val.ToString());
#endif
                    V_SetTable(ra, key, val);
                    break;
                }

                case OpCode.OP_NEWTABLE:
                {
                    int      b   = i.GETARG_B();
                    int      c   = i.GETARG_C();
                    LuaTable tbl = new LuaTable(this);
                    ra.V.SetHValue(tbl);
                    if (b > 0 || c > 0)
                    {
                        tbl.Resize(b, c);
                    }
                    break;
                }

                case OpCode.OP_SELF:
                {
                    // OP_SELF put function referenced by a table on ra
                    // and the table on ra+1
                    //
                    // RB:  table
                    // RKC: key
                    StkId ra1 = Stack[ra.Index + 1];
                    StkId rb  = env.RB;
                    ra1.V.SetObj(ref rb.V);
                    V_GetTable(rb, env.RKC, ra);
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_ADD:
                {
                    StkId rkb = env.RKB;
                    StkId rkc = env.RKC;
                    if (rkb.V.TtIsNumber() && rkc.V.TtIsNumber())
                    {
                        ra.V.SetNValue(rkb.V.NValue + rkc.V.NValue);
                    }
                    else
                    {
                        V_Arith(ra, rkb, rkc, TMS.TM_ADD);
                    }

                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_SUB:
                {
                    StkId rkb = env.RKB;
                    StkId rkc = env.RKC;
                    if (rkb.V.TtIsNumber() && rkc.V.TtIsNumber())
                    {
                        ra.V.SetNValue(rkb.V.NValue - rkc.V.NValue);
                    }
                    else
                    {
                        V_Arith(ra, rkb, rkc, TMS.TM_SUB);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_MUL:
                {
                    StkId rkb = env.RKB;
                    StkId rkc = env.RKC;
                    if (rkb.V.TtIsNumber() && rkc.V.TtIsNumber())
                    {
                        ra.V.SetNValue(rkb.V.NValue * rkc.V.NValue);
                    }
                    else
                    {
                        V_Arith(ra, rkb, rkc, TMS.TM_MUL);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_DIV:
                {
                    StkId rkb = env.RKB;
                    StkId rkc = env.RKC;
                    if (rkb.V.TtIsNumber() && rkc.V.TtIsNumber())
                    {
                        ra.V.SetNValue(rkb.V.NValue / rkc.V.NValue);
                    }
                    else
                    {
                        V_Arith(ra, rkb, rkc, TMS.TM_DIV);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_MOD:
                {
                    StkId rkb = env.RKB;
                    StkId rkc = env.RKC;
                    if (rkb.V.TtIsNumber() && rkc.V.TtIsNumber())
                    {
                        ra.V.SetNValue(rkb.V.NValue % rkc.V.NValue);
                    }
                    else
                    {
                        V_Arith(ra, rkb, rkc, TMS.TM_MOD);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_POW:
                {
                    StkId rkb = env.RKB;
                    StkId rkc = env.RKC;
                    if (rkb.V.TtIsNumber() && rkc.V.TtIsNumber())
                    {
                        ra.V.SetNValue(Math.Pow(rkb.V.NValue, rkc.V.NValue));
                    }
                    else
                    {
                        V_Arith(ra, rkb, rkc, TMS.TM_POW);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_UNM:
                {
                    StkId rb = env.RB;
                    if (rb.V.TtIsNumber())
                    {
                        ra.V.SetNValue(-rb.V.NValue);
                    }
                    else
                    {
                        V_Arith(ra, rb, rb, TMS.TM_UNM);
                        env.Base = ci.BaseIndex;
                    }
                    break;
                }

                case OpCode.OP_NOT:
                {
                    StkId rb = env.RB;
                    ra.V.SetBValue(IsFalse(ref rb.V));
                    break;
                }

                case OpCode.OP_LEN:
                {
                    V_ObjLen(ra, env.RB);
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_CONCAT:
                {
                    int b = i.GETARG_B();
                    int c = i.GETARG_C();
                    Top = Stack[env.Base + c + 1];
                    V_Concat(c - b + 1);
                    env.Base = ci.BaseIndex;

                    ra = env.RA;                             // 'V_Concat' may invoke TMs and move the stack
                    StkId rb = env.RB;
                    ra.V.SetObj(ref rb.V);

                    Top = Stack[ci.TopIndex];                             // restore top
                    break;
                }

                case OpCode.OP_JMP:
                {
                    V_DoJump(ci, i, 0);
                    break;
                }

                case OpCode.OP_EQ:
                {
                    StkId lhs      = env.RKB;
                    StkId rhs      = env.RKC;
                    bool  expectEq = i.GETARG_A() != 0;
#if DEBUG_OP_EQ
                    ULDebug.Log("[VM] ==== OP_EQ lhs:" + lhs);
                    ULDebug.Log("[VM] ==== OP_EQ rhs:" + rhs);
                    ULDebug.Log("[VM] ==== OP_EQ expectEq:" + expectEq);
                    ULDebug.Log("[VM] ==== OP_EQ (lhs.V == rhs.V):" + (lhs.V == rhs.V));
#endif
                    if ((lhs.V == rhs.V) != expectEq)
                    {
                        ci.SavedPc.Index += 1;                                 // skip next jump instruction
                    }
                    else
                    {
                        V_DoNextJump(ci);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_LT:
                {
                    bool expectCmpResult = i.GETARG_A() != 0;
                    if (V_LessThan(env.RKB, env.RKC) != expectCmpResult)
                    {
                        ci.SavedPc.Index += 1;
                    }
                    else
                    {
                        V_DoNextJump(ci);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_LE:
                {
                    bool expectCmpResult = i.GETARG_A() != 0;
                    if (V_LessEqual(env.RKB, env.RKC) != expectCmpResult)
                    {
                        ci.SavedPc.Index += 1;
                    }
                    else
                    {
                        V_DoNextJump(ci);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_TEST:
                {
                    if ((i.GETARG_C() != 0) ?
                        IsFalse(ref ra.V) : !IsFalse(ref ra.V))
                    {
                        ci.SavedPc.Index += 1;
                    }
                    else
                    {
                        V_DoNextJump(ci);
                    }

                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_TESTSET:
                {
                    StkId rb = env.RB;
                    if ((i.GETARG_C() != 0) ?
                        IsFalse(ref rb.V) : !IsFalse(ref rb.V))
                    {
                        ci.SavedPc.Index += 1;
                    }
                    else
                    {
                        ra.V.SetObj(ref rb.V);
                        V_DoNextJump(ci);
                    }
                    env.Base = ci.BaseIndex;
                    break;
                }

                case OpCode.OP_CALL:
                {
                    int b        = i.GETARG_B();
                    int nresults = i.GETARG_C() - 1;
                    if (b != 0)
                    {
                        Top = Stack[ra.Index + b];
                    }                                                             // else previous instruction set top
                    if (D_PreCall(ra, nresults))                                  // C# function?
                    {
                        if (nresults >= 0)
                        {
                            Top = Stack[ci.TopIndex];
                        }
                        env.Base = ci.BaseIndex;
                    }
                    else                               // Lua function
                    {
                        ci             = CI;
                        ci.CallStatus |= CallStatus.CIST_REENTRY;
                        goto newframe;
                    }
                    break;
                }

                case OpCode.OP_TAILCALL:
                {
                    int b = i.GETARG_B();
                    if (b != 0)
                    {
                        Top = Stack[ra.Index + b];
                    }                                                                           // else previous instruction set top

                    Utl.Assert(i.GETARG_C() - 1 == LuaDef.LUA_MULTRET);

                    bool called = D_PreCall(ra, LuaDef.LUA_MULTRET);

                    // C# function ?
                    if (called)
                    {
                        env.Base = ci.BaseIndex;
                    }

                    // LuaFunciton
                    else
                    {
                        CallInfo         nci   = CI;                                       // called frame
                        CallInfo         oci   = BaseCI[CI.Index - 1];                     // caller frame
                        StkId            nfunc = Stack[nci.FuncIndex];                     // called function
                        StkId            ofunc = Stack[oci.FuncIndex];                     // caller function
                        LuaLClosureValue ncl   = nfunc.V.ClLValue();
                        LuaLClosureValue ocl   = ofunc.V.ClLValue();

                        // last stack slot filled by 'precall'
                        int lim = nci.BaseIndex + ncl.Proto.NumParams;

                        if (cl.Proto.P.Count > 0)
                        {
                            F_Close(Stack[env.Base]);
                        }

                        // move new frame into old one
                        int nindex = nfunc.Index;
                        int oindex = ofunc.Index;
                        while (nindex < lim)
                        {
                            Stack[oindex++].V.SetObj(ref Stack[nindex++].V);
                        }

                        oci.BaseIndex   = ofunc.Index + (nci.BaseIndex - nfunc.Index);
                        oci.TopIndex    = ofunc.Index + (Top.Index - nfunc.Index);
                        Top             = Stack[oci.TopIndex];
                        oci.SavedPc     = nci.SavedPc;
                        oci.CallStatus |= CallStatus.CIST_TAIL;
                        ci              = CI = oci;

                        ocl = ofunc.V.ClLValue();
                        Utl.Assert(Top.Index == oci.BaseIndex + ocl.Proto.MaxStackSize);

                        goto newframe;
                    }

                    break;
                }

                case OpCode.OP_RETURN:
                {
                    int b = i.GETARG_B();
                    if (b != 0)
                    {
                        Top = Stack[ra.Index + b - 1];
                    }
                    if (cl.Proto.P.Count > 0)
                    {
                        F_Close(Stack[env.Base]);
                    }
                    b = D_PosCall(ra.Index);
                    if ((ci.CallStatus & CallStatus.CIST_REENTRY) == 0)
                    {
                        return;
                    }
                    else
                    {
                        ci = CI;
                        if (b != 0)
                        {
                            Top = Stack[ci.TopIndex];
                        }
                        goto newframe;
                    }
                }

                case OpCode.OP_FORLOOP:
                {
                    StkId ra1 = Stack[ra.Index + 1];
                    StkId ra2 = Stack[ra.Index + 2];
                    StkId ra3 = Stack[ra.Index + 3];

                    double step  = ra2.V.NValue;
                    double idx   = ra.V.NValue + step;                                  // increment index
                    double limit = ra1.V.NValue;

                    if ((0 < step) ? idx <= limit
                                                                           : limit <= idx)
                    {
                        ci.SavedPc.Index += i.GETARG_sBx();                 // jump back
                        ra.V.SetNValue(idx);                                // updateinternal index...
                        ra3.V.SetNValue(idx);                               // ... and external index
                    }

                    break;
                }

                case OpCode.OP_FORPREP:
                {
                    TValue init  = new TValue();
                    TValue limit = new TValue();
                    TValue step  = new TValue();

                    StkId ra1 = Stack[ra.Index + 1];
                    StkId ra2 = Stack[ra.Index + 2];

                    // WHY: why limit is not used ?

                    if (!V_ToNumber(ra, ref init))
                    {
                        G_RunError("'for' initial value must be a number");
                    }
                    if (!V_ToNumber(ra1, ref limit))
                    {
                        G_RunError("'for' limit must be a number");
                    }
                    if (!V_ToNumber(ra2, ref step))
                    {
                        G_RunError("'for' step must be a number");
                    }

                    ra.V.SetNValue(init.NValue - step.NValue);
                    ci.SavedPc.Index += i.GETARG_sBx();

                    break;
                }

                case OpCode.OP_TFORCALL:
                {
                    int rai = ra.Index;
                    int cbi = ra.Index + 3;
                    Stack[cbi + 2].V.SetObj(ref Stack[rai + 2].V);
                    Stack[cbi + 1].V.SetObj(ref Stack[rai + 1].V);
                    Stack[cbi].V.SetObj(ref Stack[rai].V);

                    StkId callBase = Stack[cbi];
                    Top = Stack[cbi + 3];                           // func. +2 args (state and index)

                    D_Call(callBase, i.GETARG_C(), true);

                    env.Base = ci.BaseIndex;

                    Top   = Stack[ci.TopIndex];
                    i     = ci.SavedPc.ValueInc;                                // go to next instruction
                    env.I = i;
                    ra    = env.RA;

                    DumpStack(env.Base);
#if DEBUG_INSTRUCTION
                    ULDebug.Log("[VM] ============================================================ OP_TFORCALL Instruction: " + i);
#endif

                    Utl.Assert(i.GET_OPCODE() == OpCode.OP_TFORLOOP);
                    goto l_tforloop;
                }

                case OpCode.OP_TFORLOOP:
l_tforloop:
                    {
                        StkId ra1 = Stack[ra.Index + 1];
                        if (!ra1.V.TtIsNil())                           // continue loop?
                        {
                            ra.V.SetObj(ref ra1.V);
                            ci.SavedPc += i.GETARG_sBx();
                        }
                        break;
                    }

                // sets the values for a range of array elements in a table(RA)
                // RA -> table
                // RB -> number of elements to set
                // C  -> encodes the block number of the table to be initialized
                // the values used to initialize the table are located in
                //   R(A+1), R(A+2) ...
                case OpCode.OP_SETLIST:
                {
                    int n = i.GETARG_B();
                    int c = i.GETARG_C();
                    if (n == 0)
                    {
                        n = (Top.Index - ra.Index) - 1;
                    }
                    if (c == 0)
                    {
                        Utl.Assert(ci.SavedPc.Value.GET_OPCODE() == OpCode.OP_EXTRAARG);
                        c = ci.SavedPc.ValueInc.GETARG_Ax();
                    }

                    LuaTable tbl = ra.V.HValue();
                    Utl.Assert(tbl != null);

                    int last = ((c - 1) * LuaDef.LFIELDS_PER_FLUSH) + n;
                    int rai  = ra.Index;
                    for (; n > 0; --n)
                    {
                        tbl.SetInt(last--, ref Stack[rai + n].V);
                    }
#if DEBUG_OP_SETLIST
                    ULDebug.Log("[VM] ==== OP_SETLIST ci.Top:" + ci.Top.Index);
                    ULDebug.Log("[VM] ==== OP_SETLIST Top:" + Top.Index);
#endif
                    Top = Stack[ci.TopIndex];                             // correct top (in case of previous open call)
                    break;
                }

                case OpCode.OP_CLOSURE:
                {
                    LuaProto p = cl.Proto.P[i.GETARG_Bx()];
                    V_PushClosure(p, cl.Upvals, env.Base, ra);
#if DEBUG_OP_CLOSURE
                    ULDebug.Log("OP_CLOSURE:" + ra.Value);
                    var racl = ra.Value as LuaLClosure;
                    if (racl != null)
                    {
                        for (int ii = 0; ii < racl.Upvals.Count; ++ii)
                        {
                            ULDebug.Log(ii + " ) " + racl.Upvals[ii]);
                        }
                    }
#endif
                    break;
                }

                /// <summary>
                /// VARARG implements the vararg operator `...' in expressions.
                /// VARARG copies B-1 parameters into a number of registers
                /// starting from R(A), padding with nils if there aren't enough values.
                /// If B is 0, VARARG copies as many values as it can based on
                /// the number of parameters passed.
                /// If a fixed number of values is required, B is a value greater than 1.
                /// If any number of values is required, B is 0.
                /// </summary>
                case OpCode.OP_VARARG:
                {
                    int b = i.GETARG_B() - 1;
                    int n = (env.Base - ci.FuncIndex) - cl.Proto.NumParams - 1;
                    if (b < 0)                              // B == 0?
                    {
                        b = n;
                        D_CheckStack(n);
                        ra  = env.RA;                                // previous call may change the stack
                        Top = Stack[ra.Index + n];
                    }

                    int p = ra.Index;
                    int q = env.Base - n;
                    for (int j = 0; j < b; ++j)
                    {
                        if (j < n)
                        {
                            Stack[p++].V.SetObj(ref Stack[q++].V);
                        }
                        else
                        {
                            Stack[p++].V.SetNilValue();
                        }
                    }
                    break;
                }

                case OpCode.OP_EXTRAARG:
                {
                    Utl.Assert(false);
                    V_NotImplemented(i);
                    break;
                }

                default:
                    V_NotImplemented(i);
                    break;
                }
            }
        }