aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-08 09:14:35 +0100
committerBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-08 09:45:55 +0100
commit20846070c7809ac30a0aed3dbd4d04716e1ef1be (patch)
tree166a65df27fe78be34fdda1224eeadc8789872f1
parenta2031547aafbb07f6284cd10704435db23d9db60 (diff)
downloadfengari-20846070c7809ac30a0aed3dbd4d04716e1ef1be.tar.gz
fengari-20846070c7809ac30a0aed3dbd4d04716e1ef1be.tar.bz2
fengari-20846070c7809ac30a0aed3dbd4d04716e1ef1be.zip
OP_LT, OP_EQ, fixed bad sBx
-rw-r--r--README.md4
-rw-r--r--src/lopcodes.js110
-rw-r--r--src/lundump.js4
-rw-r--r--src/lvm.js91
-rw-r--r--tests/lvm.js50
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) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
- "OP_FORPREP", /* A sBx R(A)-=R(A+2); pc+=sBx */
-
- "OP_TFORCALL", /* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
- "OP_TFORLOOP", /* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/
-
- "OP_SETLIST", /* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
-
- "OP_CLOSURE", /* A Bx R(A) := closure(KPROTO[Bx]) */
-
- "OP_VARARG", /* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
-
- "OP_EXTRAARG" /* Ax extra (larger) argument for previous opcode */
+ "OP_MOVE",
+ "OP_LOADK",
+ "OP_LOADKX",
+ "OP_LOADBOOL",
+ "OP_LOADNIL",
+ "OP_GETUPVAL",
+ "OP_GETTABUP",
+ "OP_GETTABLE",
+ "OP_SETTABUP",
+ "OP_SETUPVAL",
+ "OP_SETTABLE",
+ "OP_NEWTABLE",
+ "OP_SELF",
+ "OP_ADD",
+ "OP_SUB",
+ "OP_MUL",
+ "OP_MOD",
+ "OP_POW",
+ "OP_DIV",
+ "OP_IDIV",
+ "OP_BAND",
+ "OP_BOR",
+ "OP_BXOR",
+ "OP_SHL",
+ "OP_SHR",
+ "OP_UNM",
+ "OP_BNOT",
+ "OP_NOT",
+ "OP_LEN",
+ "OP_CONCAT",
+ "OP_JMP",
+ "OP_EQ",
+ "OP_LT",
+ "OP_LE",
+ "OP_TEST",
+ "OP_TESTSET",
+ "OP_CALL",
+ "OP_TAILCALL",
+ "OP_RETURN",
+ "OP_FORLOOP",
+ "OP_FORPREP",
+ "OP_TFORCALL",
+ "OP_TFORLOOP",
+ "OP_SETLIST",
+ "OP_CLOSURE",
+ "OP_VARARG",
+ "OP_EXTRAARG"
];
const SIZE_C = 9;
diff --git a/src/lundump.js b/src/lundump.js
index f2f91f1..b104e32 100644
--- a/src/lundump.js
+++ b/src/lundump.js
@@ -83,7 +83,7 @@ class BytecodeParser {
readString(n) {
let size = typeof n !== 'undefined' ? n : this.readByte() - 1;
- if (size === 0xFF) // TODO test
+ if (size === 0xFF) // TODO: test
this.offset += this.size_tSize;
if (size === 0) {
@@ -131,7 +131,7 @@ class BytecodeParser {
C: (ins >> 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;
@@ -291,7 +291,55 @@ test('LE, JMP', function (t) {
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