From 20846070c7809ac30a0aed3dbd4d04716e1ef1be Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Wed, 8 Feb 2017 09:14:35 +0100 Subject: OP_LT, OP_EQ, fixed bad sBx --- README.md | 4 +-- src/lopcodes.js | 110 ++++++++++++++++++++++++-------------------------------- src/lundump.js | 4 +-- src/lvm.js | 91 +++++++++++++++++++++++++++++++++++++++++----- tests/lvm.js | 50 +++++++++++++++++++++++++- 5 files changed, 182 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 84844ec..6126241 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,8 @@ - [ ] OP_LEN - [ ] OP_CONCAT - [x] OP_JMP - - [ ] OP_EQ - - [ ] OP_LT + - [x] OP_EQ + - [x] OP_LT - [x] OP_LE - [ ] OP_TEST - [ ] OP_TESTSET diff --git a/src/lopcodes.js b/src/lopcodes.js index 8df67a3..05eb0d5 100644 --- a/src/lopcodes.js +++ b/src/lopcodes.js @@ -2,69 +2,53 @@ "use strict"; const OpCodes = [ - "OP_MOVE", /* A B R(A) := R(B) */ - "OP_LOADK", /* A Bx R(A) := Kst(Bx) */ - "OP_LOADKX", /* A R(A) := Kst(extra arg) */ - "OP_LOADBOOL", /* A B C R(A) := (Bool)B; if (C) pc++ */ - "OP_LOADNIL", /* A B R(A), R(A+1), ..., R(A+B) := nil */ - "OP_GETUPVAL", /* A B R(A) := UpValue[B] */ - - "OP_GETTABUP", /* A B C R(A) := UpValue[B][RK(C)] */ - "OP_GETTABLE", /* A B C R(A) := R(B)[RK(C)] */ - - "OP_SETTABUP", /* A B C UpValue[A][RK(B)] := RK(C) */ - "OP_SETUPVAL", /* A B UpValue[B] := R(A) */ - "OP_SETTABLE", /* A B C R(A)[RK(B)] := RK(C) */ - - "OP_NEWTABLE", /* A B C R(A) := {} (size = B,C) */ - - "OP_SELF", /* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */ - - "OP_ADD", /* A B C R(A) := RK(B) + RK(C) */ - "OP_SUB", /* A B C R(A) := RK(B) - RK(C) */ - "OP_MUL", /* A B C R(A) := RK(B) * RK(C) */ - "OP_MOD", /* A B C R(A) := RK(B) % RK(C) */ - "OP_POW", /* A B C R(A) := RK(B) ^ RK(C) */ - "OP_DIV", /* A B C R(A) := RK(B) / RK(C) */ - "OP_IDIV", /* A B C R(A) := RK(B) // RK(C) */ - "OP_BAND", /* A B C R(A) := RK(B) & RK(C) */ - "OP_BOR", /* A B C R(A) := RK(B) | RK(C) */ - "OP_BXOR", /* A B C R(A) := RK(B) ~ RK(C) */ - "OP_SHL", /* A B C R(A) := RK(B) << RK(C) */ - "OP_SHR", /* A B C R(A) := RK(B) >> RK(C) */ - "OP_UNM", /* A B R(A) := -R(B) */ - "OP_BNOT", /* A B R(A) := ~R(B) */ - "OP_NOT", /* A B R(A) := not R(B) */ - "OP_LEN", /* A B R(A) := length of R(B) */ - - "OP_CONCAT", /* A B C R(A) := R(B).. ... ..R(C) */ - - "OP_JMP", /* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ - "OP_EQ", /* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ - "OP_LT", /* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ - "OP_LE", /* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ - - "OP_TEST", /* A C if not (R(A) <=> C) then pc++ */ - "OP_TESTSET", /* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ - - "OP_CALL", /* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ - "OP_TAILCALL", /* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ - "OP_RETURN", /* A B return R(A), ... ,R(A+B-2) (see note) */ - - "OP_FORLOOP", /* A sBx R(A)+=R(A+2); - if R(A) > o.POS_C) & p.MASK1(o.SIZE_C, 0), Bx: (ins >> o.POS_Bx) & p.MASK1(o.SIZE_Bx, 0), Ax: (ins >> o.POS_Ax) & p.MASK1(o.SIZE_Ax, 0), - sBx: (ins >> o.POS_Bx) & p.MASK1(o.SIZE_Bx, 0) - o.MAXARG_sBx + sBx: ((ins >> o.POS_Bx) & p.MASK1(o.SIZE_Bx, 0)) - o.MAXARG_sBx }; console.log(` [${i}] Op: ${o.OpCodes[f.code[i].opcode]} A: ${f.code[i].A} B: ${f.code[i].B} C: ${f.code[i].C} Ax: ${f.code[i].Ax} Bx: ${f.code[i].Bx} sBx: ${f.code[i].sBx}`); diff --git a/src/lvm.js b/src/lvm.js index 2574a97..1673db7 100644 --- a/src/lvm.js +++ b/src/lvm.js @@ -53,7 +53,7 @@ class LuaVM { return new TValue(CT.LUA_TNUMFLT, v.value); if (v.type === CT.LUA_TSHRSTR || v.type === CT.LUA_TLNGSTR) - return new TValue(CT.LUA_TNUMFLT, parseFloat(v.value)); // TODO 0x or other exotic form + return new TValue(CT.LUA_TNUMFLT, parseFloat(v.value)); // TODO: 0x or other exotic form return false; } @@ -347,9 +347,19 @@ class LuaVM { break; } case "OP_EQ": { + if (LuaVM.luaV_equalobj(this.RKB(base, k, i), this.RKC(base, k, i)) !== i.A) + ci.pcOff++; + else + this.donextjump(ci); + base = ci.u.l.base; break; } case "OP_LT": { + if (LuaVM.luaV_lessthan(this.RKB(base, k, i), this.RKC(base, k, i)) !== i.A) + ci.pcOff++; + else + this.donextjump(ci); + base = ci.u.l.base; break; } case "OP_LE": { @@ -397,7 +407,7 @@ class LuaVM { let ofunc = oci.func; let ofuncOff = oci.funcOff; let lim = nci.u.l.base + nfunc.p.numparams; - // TODO close upvalues ? + // TODO: close upvalues ? for (let aux = 0; nfuncOff + aux < lim; aux++) L.stack[ofuncOff + aux] = L.stack[nfuncOff + aux]; @@ -631,7 +641,7 @@ class LuaVM { dojump(ci, i, e) { let a = i.A; - // TODO if (a != 0) luaF_close(L, ci.u.l.base + a - 1); + // TODO: if (a != 0) luaF_close(L, ci.u.l.base + a - 1); ci.pcOff += i.sBx + e; } @@ -644,19 +654,82 @@ class LuaVM { return LuaVM.LEnum(l, r); else if (l.ttisstring() && r.ttisstring()) return LuaVM.l_strcmp(l, r) <= 0; - // TODO metatable - // else if (l.metatable.__le || r.metatable.__le) - // return l.metatable.__le ? l.metatable.__le(l, r) : r.metatable.__le(l, r); - // else { + // TODO: metatable + // else if (l.metatable.__le || r.metatable.__le) { + // let res = l.metatable.__le ? l.metatable.__le(l, r) : r.metatable.__le(l, r); + // if (res >= 0) + // return res; + // } else { // L.ci.callstatus |= lstate.CIST_LEQ; // let res = l.metatable.__lt ? l.metatable.__lt(r, l) : r.metatable.__lt(r, l); // L.ci.callstatus ^= lstate.CIST_LEQ; // if (res < 0) - // throw new Error("attempt to compare two string values"); + // throw new Error("attempt to compare ..."); // return !res; // } } + static luaV_lessthan(l, r) { + if (l.ttisnumber() && r.ttisnumber()) + return LuaVM.LTnum(l, r); + else if (l.ttisstring() && r.ttisstring()) + return LuaVM.l_strcmp(l, r) < 0; + // TODO: metatable + // else if (l.metatable.__lt || r.metatable.__lt) { + // let res = l.metatable.__lt ? l.metatable.__lt(l, r) : r.metatable.__lt(l, r); + // if (res < 0) + // throw new Error("attempt to compare ...") + // return res; + // } + } + + static luaV_equalobj(t1, t2) { + if (t1.ttype() !== t2.ttype()) { /* not the same variant? */ + if (t1.ttnov() !== t2.ttnov() || t1.ttnov() !== CT.LUA_NUMBER) + return 0; /* only numbers can be equal with different variants */ + else { /* two numbers with different variants */ + /* compare them as integers */ + return Math.floor(t1.value) === Math.floor(t2.value) // TODO: tointeger + } + } + + /* values have same type and same variant */ + switch(t1.ttype()) { + case CT.LUA_TNIL: + return 1; + case CT.LUA_TNUMINT: + case CT.LUA_TNUMFLT: + case CT.LUA_TBOOLEAN: + case CT.LUA_TLIGHTUSERDATA: + case CT.LUA_TLCF: + case CT.LUA_TSHRSTR: + case CT.LUA_TLNGSTR: + return t1.value === t2.value ? 1 : 0; + case CT.LUA_TUSERDATA: + case CT.LUA_TTABLE: + if (t1 === t2) return 1; + // TODO: __eq + default: + return t1.value === t2.value ? 1 : 0; + } + } + + static LTnum(l, r) { + if (l.ttisinteger()) { + if (r.ttisinteger()) + return l.value < r.value ? 1 : 0; + else + return LuaVM.LTintfloat(r.value, l.value); + } else { + if (r.ttisfloat()) + return l.value < r.value ? 1 : 0; + else if (isNan(l.value)) + return 0; + else + return !LuaVM.LEintfloat(r.value, l.value); + } + } + static LEnum(l, r) { if (l.ttisinteger()) { if (r.ttisinteger()) @@ -684,7 +757,7 @@ class LuaVM { } static l_strcmp(ls, rs) { - // TODO lvm.c:248 static int l_strcmp (const TString *ls, const TString *rs) + // TODO: lvm.c:248 static int l_strcmp (const TString *ls, const TString *rs) return ls.value === rs.value ? 0 : (ls.value < rs.value ? -1 : 1); } diff --git a/tests/lvm.js b/tests/lvm.js index f826091..e2454eb 100644 --- a/tests/lvm.js +++ b/tests/lvm.js @@ -275,7 +275,7 @@ test('VARARG', function (t) { test('LE, JMP', function (t) { let luaCode = ` - local a, b = 1, 2 + local a, b = 1, 1 return a >= b `, vm; @@ -289,9 +289,57 @@ test('LE, JMP', function (t) { vm.execute(); }, "Program executed without errors"); + t.strictEqual( + vm.L.stack[vm.L.top - 1].value, + true, + "Program output is correct" + ); +}); + + +test('LT', function (t) { + let luaCode = ` + local a, b = 1, 1 + + return a > b + `, vm; + + t.plan(2); + + t.comment("Running following code: \n" + luaCode); + + t.doesNotThrow(function () { + vm = getVM(luaCode); + vm.execute(); + }, "Program executed without errors"); + t.strictEqual( vm.L.stack[vm.L.top - 1].value, false, "Program output is correct" ); +}); + + +test('EQ', function (t) { + let luaCode = ` + local a, b = 1, 1 + + return a == b + `, vm; + + t.plan(2); + + t.comment("Running following code: \n" + luaCode); + + t.doesNotThrow(function () { + vm = getVM(luaCode); + vm.execute(); + }, "Program executed without errors"); + + t.strictEqual( + vm.L.stack[vm.L.top - 1].value, + true, + "Program output is correct" + ); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2