From a2031547aafbb07f6284cd10704435db23d9db60 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Wed, 8 Feb 2017 07:38:03 +0100 Subject: callstatus, OP_LE, OP_JMP, tests use L.top --- README.md | 6 ++-- src/lobject.js | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lstate.js | 14 +++++++-- src/lvm.js | 85 ++++++++++++++++++++++++++++++++++++++++++++++++------ tests/lvm.js | 29 +++++++------------ 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 -- cgit v1.2.3-54-g00ecf