aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-08 07:38:03 +0100
committerBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-08 08:23:00 +0100
commita2031547aafbb07f6284cd10704435db23d9db60 (patch)
tree75677f2714f10cb6e7e298542af9d650e235c6d1
parent364326b97450564d41d37cdc7b2c4fbb8049c62f (diff)
downloadfengari-a2031547aafbb07f6284cd10704435db23d9db60.tar.gz
fengari-a2031547aafbb07f6284cd10704435db23d9db60.tar.bz2
fengari-a2031547aafbb07f6284cd10704435db23d9db60.zip
callstatus, OP_LE, OP_JMP, tests use L.top
-rw-r--r--README.md6
-rw-r--r--src/lobject.js90
-rw-r--r--src/lstate.js14
-rw-r--r--src/lvm.js85
-rw-r--r--tests/lvm.js29
5 files changed, 193 insertions, 31 deletions
diff --git a/README.md b/README.md
index 428f556..84844ec 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
- [ ] VM
- [x] OP_MOVE
- [x] OP_LOADK
- - [ ] OP_LOADKX
+ - [x] OP_LOADKX
- [x] OP_LOADBOOL
- [x] OP_LOADNIL
- [x] OP_GETUPVAL
@@ -35,10 +35,10 @@
- [x] OP_NOT
- [ ] OP_LEN
- [ ] OP_CONCAT
- - [ ] OP_JMP
+ - [x] OP_JMP
- [ ] OP_EQ
- [ ] OP_LT
- - [ ] OP_LE
+ - [x] OP_LE
- [ ] OP_TEST
- [ ] OP_TESTSET
- [x] OP_CALL
diff --git a/src/lobject.js b/src/lobject.js
index 4cc8c4e..3781623 100644
--- a/src/lobject.js
+++ b/src/lobject.js
@@ -12,6 +12,96 @@ class TValue {
this.metatable = null;
}
+ /* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
+ ttype() {
+ return this.type & 0x3F;
+ }
+
+ /* type tag of a TValue with no variants (bits 0-3) */
+ ttnov() {
+ return this.type & 0x0F;
+ }
+
+ checktag(t) {
+ return this.type === t;
+ }
+
+ checktype(t) {
+ return this.ttnov() === t;
+ }
+
+ ttisnumber() {
+ return this.checktype(CT.LUA_TNUMBER);
+ }
+
+ ttisfloat() {
+ return this.checktag(CT.LUA_TNUMFLT);
+ }
+
+ ttisinteger() {
+ return this.checktag(CT.LUA_TNUMINT);
+ }
+
+ ttisnil() {
+ return this.checktag(CT.LUA_TNIL);
+ }
+
+ ttisboolean() {
+ return this.checktag(CT.LUA_TBOOLEAN);
+ }
+
+ ttislightuserdata() {
+ return this.checktag(CT.LUA_TLIGHTUSERDATA);
+ }
+
+ ttisstring() {
+ return this.checktype(CT.LUA_TSTRING);
+ }
+
+ ttisshrstring() {
+ return this.checktag(ctb(CT.LUA_TSHRSTR));
+ }
+
+ ttislngstring() {
+ return this.checktag(ctb(CT.LUA_TLNGSTR));
+ }
+
+ ttistable() {
+ return this.checktag(ctb(CT.LUA_TTABLE));
+ }
+
+ ttisfunction() {
+ return this.checktype(CT.LUA_TFUNCTION);
+ }
+
+ ttisclosure() {
+ return (this.type & 0x1F) === CT.LUA_TFUNCTION;
+ }
+
+ ttisCclosure() {
+ return this.checktag(ctb(CT.LUA_TCCL));
+ }
+
+ ttisLclosure() {
+ return this.checktag(ctb(CT.LUA_TLCL));
+ }
+
+ ttislcf() {
+ return this.checktag(CT.LUA_TLCF);
+ }
+
+ ttisfulluserdata() {
+ return this.checktag(ctb(CT.LUA_TUSERDATA));
+ }
+
+ ttisthread() {
+ return this.checktag(ctb(CT.LUA_TTHREAD));
+ }
+
+ ttisdeadkey() {
+ return this.checktag(CT.LUA_TDEADKEY);
+ }
+
}
diff --git a/src/lstate.js b/src/lstate.js
index b919f0e..37f2d6b 100644
--- a/src/lstate.js
+++ b/src/lstate.js
@@ -19,6 +19,7 @@ class CallInfo {
}
};
this.nresults = 0;
+ this.callstatus = 0;
}
}
@@ -40,6 +41,15 @@ class lua_State {
}
module.exports = {
- lua_State: lua_State,
- CallInfo: CallInfo
+ lua_State: lua_State,
+ CallInfo: CallInfo,
+ CIST_OAH: (1<<0), /* original value of 'allowhook' */
+ CIST_LUA: (1<<1), /* call is running a Lua function */
+ CIST_HOOKED: (1<<2), /* call is running a debug hook */
+ CIST_FRESH: (1<<3), /* call is running on a fresh invocation of luaV_execute */
+ CIST_YPCALL: (1<<4), /* call is a yieldable protected call */
+ CIST_TAIL: (1<<5), /* call was tail called */
+ CIST_HOOKYIELD: (1<<6), /* last hook called yielded */
+ CIST_LEQ: (1<<7), /* using __lt for __le */
+ CIST_FIN: (1<<8) /* call is running a finalizer */
}; \ No newline at end of file
diff --git a/src/lvm.js b/src/lvm.js
index d548e77..2574a97 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -14,7 +14,8 @@ const Table = lobject.Table;
const LClosure = lobject.LClosure;
const lfunc = require('./lfunc.js');
const UpVal = lfunc.UpVal;
-const CallInfo = require('./lstate.js').CallInfo;
+const lstate = require('./lstate.js');
+const CallInfo = lstate.CallInfo;
const nil = new TValue(CT.LUA_TNIL, null);
@@ -60,9 +61,11 @@ class LuaVM {
execute() {
let L = this.L;
+ let ci = L.ci;
+ ci.callstatus |= lstate.CIST_FRESH;
newframe:
for (;;) {
- let ci = L.ci;
+ ci = L.ci;
var cl = ci.func;
let k = cl.p.k;
let base = ci.u.l.base
@@ -340,6 +343,7 @@ class LuaVM {
break;
}
case "OP_JMP": {
+ this.dojump(ci, i, 0);
break;
}
case "OP_EQ": {
@@ -349,6 +353,11 @@ class LuaVM {
break;
}
case "OP_LE": {
+ if (LuaVM.luaV_lessequal(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_TEST": {
@@ -397,7 +406,7 @@ class LuaVM {
oci.top = L.top;
oci.u.l.savedpc = nci.u.l.savedpc;
oci.pcOff = nci.pcOff;
- //TODO callstatus
+ oci.callstatus |= lstate.CIST_TAIL;
L.ci = oci;
ci = L.ci;
L.ciOff--;
@@ -410,13 +419,13 @@ class LuaVM {
}
case "OP_RETURN": {
let b = this.postcall(ci, ra, (i.B !== 0 ? i.B - 1 : L.top - ra));
- // TODO call status check
- ci = L.ci;
-
- // TODO what to return when end of program ?
- if (L.ci === null) return;
+ if (ci.callstatus & lstate.CIST_FRESH)
+ return; /* external invocation: return */
+
+ ci = L.ci;
if (b) L.top = ci.top;
+
continue newframe;
break;
}
@@ -525,6 +534,7 @@ class LuaVM {
ci.top = base + fsize;
L.top = ci.top;
ci.u.l.savedpc = p.code;
+ ci.callstatus = lstate.CIST_LUA;
break;
}
default:
@@ -619,6 +629,65 @@ class LuaVM {
return base;
}
+ dojump(ci, i, e) {
+ let a = i.A;
+ // TODO if (a != 0) luaF_close(L, ci.u.l.base + a - 1);
+ ci.pcOff += i.sBx + e;
+ }
+
+ donextjump(ci) {
+ this.dojump(ci, ci.u.l.savedpc[ci.pcOff], 1);
+ }
+
+ static luaV_lessequal(l, r) {
+ if (l.ttisnumber() && r.ttisnumber())
+ 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 {
+ // 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");
+ // return !res;
+ // }
+ }
+
+ static LEnum(l, r) {
+ if (l.ttisinteger()) {
+ if (r.ttisinteger())
+ return l.value <= r.value ? 1 : 0;
+ else
+ return LuaVM.LEintfloat(l.value, r.value);
+ } else {
+ if (r.ttisfloat())
+ return l.value <= r.value ? 1 : 0;
+ else if (isNan(l.value))
+ return false;
+ else
+ return !LuaVM.LTintfloat(r.value, l.value);
+ }
+ }
+
+ static LEintfloat(l, r) {
+ // TODO
+ return l <= r ? 1 : 0;
+ }
+
+ static LTintfloat(l, r) {
+ // TODO
+ return l < r ? 1 : 0;
+ }
+
+ static l_strcmp(ls, 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);
+ }
+
}
module.exports = {
diff --git a/tests/lvm.js b/tests/lvm.js
index c93b1ed..f826091 100644
--- a/tests/lvm.js
+++ b/tests/lvm.js
@@ -61,7 +61,7 @@ test('LOADK, RETURN', function (t) {
}, "Program executed without errors");
t.strictEqual(
- vm.L.stack[vm.L.stack.length - 1].value,
+ vm.L.stack[vm.L.top - 1].value,
"hello world",
"Program output is correct"
);
@@ -85,7 +85,7 @@ test('MOV', function (t) {
}, "Program executed without errors");
t.strictEqual(
- vm.L.stack[vm.L.stack.length - 1].value,
+ vm.L.stack[vm.L.top - 1].value,
"hello world",
"Program output is correct"
);
@@ -132,7 +132,7 @@ test('Unary op, LOADBOOL', function (t) {
}, "Program executed without errors");
t.deepEqual(
- vm.L.stack.slice(vm.L.stack.length - 3).map(function (e) { return e.value; }),
+ vm.L.stack.slice(vm.L.top - 3, vm.L.top).map(function (e) { return e.value; }),
[-5, true, -6],
"Program output is correct"
);
@@ -155,7 +155,7 @@ test('NEWTABLE', function (t) {
}, "Program executed without errors");
t.ok(
- vm.L.stack[vm.L.stack.length - 1] instanceof Table,
+ vm.L.stack[vm.L.top - 1] instanceof Table,
"Program output is correct"
);
});
@@ -182,7 +182,7 @@ test('CALL', function (t) {
}, "Program executed without errors");
t.strictEqual(
- vm.L.stack[vm.L.stack.length - 1].value,
+ vm.L.stack[vm.L.top - 1].value,
3,
"Program output is correct"
);
@@ -214,7 +214,7 @@ test('Multiple return', function (t) {
}, "Program executed without errors");
t.deepEqual(
- vm.L.stack.slice(vm.L.stack.length - 3).map(function (e) { return e.value; }),
+ vm.L.stack.slice(vm.L.top - 3, vm.L.top).map(function (e) { return e.value; }),
[3, -1, 2],
"Program output is correct"
);
@@ -266,25 +266,18 @@ test('VARARG', function (t) {
}, "Program executed without errors");
t.deepEqual(
- vm.L.stack.slice(vm.L.stack.length - 3).map(function (e) { return e.value; }),
+ vm.L.stack.slice(vm.L.top - 3, vm.L.top).map(function (e) { return e.value; }),
[1, 2, 3],
"Program output is correct"
);
});
-test('GETUPVAL, SETUPVAL', function (t) {
+test('LE, JMP', function (t) {
let luaCode = `
- local a = 1
+ local a, b = 1, 2
- local f = function ()
- a = a + 1
- return a
- end
-
- f()
-
- return a
+ return a >= b
`, vm;
t.plan(2);
@@ -298,7 +291,7 @@ test('GETUPVAL, SETUPVAL', function (t) {
t.strictEqual(
vm.L.stack[vm.L.top - 1].value,
- 2,
+ false,
"Program output is correct"
);
}); \ No newline at end of file