From ad5cb0ddb6d4bd187cd4370711b1c9e34245fe07 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Wed, 8 Feb 2017 15:31:44 +0100 Subject: Upvalues --- src/lfunc.js | 19 ++++++--- src/lobject.js | 37 +++++++++++------- src/lstate.js | 4 +- src/lvm.js | 54 ++++++++++++++++--------- tests/lvm.js | 121 +++++++++++++++++++++++++++++++++++++++++++-------------- 5 files changed, 168 insertions(+), 67 deletions(-) diff --git a/src/lfunc.js b/src/lfunc.js index 929dae8..2c33a7d 100644 --- a/src/lfunc.js +++ b/src/lfunc.js @@ -24,16 +24,25 @@ class Proto { class UpVal { constructor() { - this.v = null; + this.v = null; /* if null, upval is closed, value is in u.value */ this.u = { - open: { - next: null, - touched: false + open: { /* (when open) */ + next: null, /* linked list */ + touched: false /* mark to avoid cycles with dead threads */ }, - value: null + value: null /* the value (when closed) */ }; } + val(L) { + return this.v !== null ? L.stack[this.v] : this.u.value; + } + + setval(L, ra) { + if (this.v !== null) this.v = ra; + else this.u.value = L.stack[ra]; + } + } module.exports = { diff --git a/src/lobject.js b/src/lobject.js index 3781623..a1ed6ca 100644 --- a/src/lobject.js +++ b/src/lobject.js @@ -1,8 +1,8 @@ /*jshint esversion: 6 */ "use strict"; -const CT = require('./lua.js').constant_types; - +const CT = require('./lua.js').constant_types; +const UpVal = require('./lfunc.js').UpVal; class TValue { @@ -59,15 +59,15 @@ class TValue { } ttisshrstring() { - return this.checktag(ctb(CT.LUA_TSHRSTR)); + return this.checktag(CT.LUA_TSHRSTR); } ttislngstring() { - return this.checktag(ctb(CT.LUA_TLNGSTR)); + return this.checktag(CT.LUA_TLNGSTR); } ttistable() { - return this.checktag(ctb(CT.LUA_TTABLE)); + return this.checktag(CT.LUA_TTABLE); } ttisfunction() { @@ -79,11 +79,11 @@ class TValue { } ttisCclosure() { - return this.checktag(ctb(CT.LUA_TCCL)); + return this.checktag(CT.LUA_TCCL); } ttisLclosure() { - return this.checktag(ctb(CT.LUA_TLCL)); + return this.checktag(CT.LUA_TLCL); } ttislcf() { @@ -91,11 +91,11 @@ class TValue { } ttisfulluserdata() { - return this.checktag(ctb(CT.LUA_TUSERDATA)); + return this.checktag(CT.LUA_TUSERDATA); } ttisthread() { - return this.checktag(ctb(CT.LUA_TTHREAD)); + return this.checktag(CT.LUA_TTHREAD); } ttisdeadkey() { @@ -112,7 +112,16 @@ class LClosure extends TValue { this.p = null; this.nupvalues = n; - this.upvals = []; + + let _ENV = new UpVal(); + _ENV.refcount = 0; + _ENV.v = 0; // _ENV is on the stack at index 0 + _ENV.u.open.next = null; + _ENV.u.open.touched = true; + + this.upvals = [ + _ENV + ]; this.value = this; } @@ -139,10 +148,10 @@ class Table extends TValue { this.usermetatable = null; this.metatable = { - __newindex: function (table, key) { + __newindex: function (table, key, value) { if (key instanceof TValue) { // Those lua values are used by value, tables and functions by reference - if ([CT.TNUMBER, CT.TSTRING, CT.TSHRSTR, CT.TLNGSTR, CT.TNUMFLT, CT.TNUMINT].indexOf(key.type) > -1) { + if ([CT.LUA_TNUMBER, CT.LUA_TSTRING, CT.LUA_TSHRSTR, CT.LUA_TLNGSTR, CT.LUA_TNUMFLT, CT.LUA_TNUMINT].indexOf(key.type) > -1) { key = key.value; } } @@ -154,10 +163,10 @@ class Table extends TValue { } }, - __index: function (table, key, value) { + __index: function (table, key) { if (key instanceof TValue) { // Those lua values are used by value, tables and functions by reference - if ([CT.TNUMBER, CT.TSTRING, CT.TSHRSTR, CT.TLNGSTR, CT.TNUMFLT, CT.TNUMINT].indexOf(key.type) > -1) { + if ([CT.LUA_TNUMBER, CT.LUA_TSTRING, CT.LUA_TSHRSTR, CT.LUA_TLNGSTR, CT.LUA_TNUMFLT, CT.LUA_TNUMINT].indexOf(key.type) > -1) { key = key.value; } } diff --git a/src/lstate.js b/src/lstate.js index 37f2d6b..2c322c3 100644 --- a/src/lstate.js +++ b/src/lstate.js @@ -2,6 +2,7 @@ "use strict"; const LUA_MULTRET = require('./lua.js').LUA_MULTRET; +const Table = require('./lobject.js').Table; class CallInfo { @@ -27,12 +28,13 @@ class CallInfo { class lua_State { constructor(cl) { - this.top = 1; + this.top = 2; this.ci = new CallInfo(0, cl, 1, 1, null, null); this.ci.u.l.savedpc = cl.p.code; this.ci.nresults = LUA_MULTRET; this.ciOff = 0; this.stack = [ + new Table(), // _ENV cl ]; this.openupval = []; diff --git a/src/lvm.js b/src/lvm.js index fa79695..fed3d36 100644 --- a/src/lvm.js +++ b/src/lvm.js @@ -89,40 +89,58 @@ class LuaVM { break; } case "OP_GETUPVAL": { - L.stack[ra] = L.stack[cl.upvals[i.B].v]; + L.stack[ra] = cl.upvals[i.B].val(L); break; } case "OP_SETUPVAL": { - L.stack[cl.upvals[i.B].v] = L.stack[ra]; + cl.upvals[i.B].setval(L, ra); break; } case "OP_GETTABUP": { + let table = cl.upvals[i.B].val(L); + let key = this.RKC(base, k, i); + + // if (!table.ttistable() || !table.metatable.__index(table, key)) { + // // __index + // } else { + L.stack[ra] = table.metatable.__index(table, key); + // } break; } case "OP_SETTABUP": { + let table = cl.upvals[i.A].val(L); + let key = this.RKB(base, k, i); + let v = this.RKC(base, k, i); + + // if (!table.ttistable() || !table.metatable.__index(table, key)) { + // // __index + // } else { + table.metatable.__newindex(table, key, v); + // } + break; } case "OP_GETTABLE": { - let t = L.stack[this.RKB(base, i)]; - let k = L.stack[this.RKC(base, i)]; + let table = this.RKB(base, k, i); + let key = this.RKC(base, k, i); - if (!t.ttistable() || !t.value.__index(t, k)) { - // __index - } else { - L.stack[ra] = t.value.__index(t, k); - } + // if (!table.ttistable() || !table.metatable.__index(table, key)) { + // // __index + // } else { + L.stack[ra] = table.metatable.__index(table, key); + // } break; } case "OP_SETTABLE": { - let t = L.stack[ra]; - let k = L.stack[this.RKB(base, i)]; - let v = L.stack[this.RKC(base, i)]; - - if (!t.ttistable() || !t.value.__index(t, k)) { - // __index - } else { - t.value.__newindex(t, k, v); - } + let table = L.stack[ra]; + let key = this.RKB(base, k, i); + let v = this.RKC(base, k, i); + + // if (!table.ttistable() || !table.metatable.__index(table, key)) { + // // __index + // } else { + table.metatable.__newindex(table, key, v); + // } break; } diff --git a/tests/lvm.js b/tests/lvm.js index 199001f..df01167 100644 --- a/tests/lvm.js +++ b/tests/lvm.js @@ -491,6 +491,69 @@ test('FORPREP, FORLOOP (float)', function (t) { return total `, 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, + 60.5, + "Program output is correct" + ); +}); + + +test('SETTABLE, GETTABLE', function (t) { + let luaCode = ` + local t = {} + + t[1] = "hello" + t["two"] = "world" + + return t + `, vm; + + t.plan(3); + + 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.array[1].value, + "hello", + "Program output is correct" + ); + + t.strictEqual( + vm.L.stack[vm.L.top - 1].value.hash.get("two").value, + "world", + "Program output is correct" + ); +}); + + +test('SETUPVAL, GETUPVAL', function (t) { + let luaCode = ` + local up = "hello" + + local f = function () + upup = "yo" + up = "world" + return up; + end + + return f() + `, vm; + t.plan(1); t.comment("Running following code: \n" + luaCode); @@ -502,40 +565,40 @@ test('FORPREP, FORLOOP (float)', function (t) { t.strictEqual( vm.L.stack[vm.L.top - 1].value, - 60.5, + "world", "Program output is correct" ); }); -// test('SETTABLE, GETTABLE', function (t) { -// let luaCode = ` -// local t = {} +test('SETTABUP, GETTABUP', function (t) { + let luaCode = ` + t = {} -// t[1] = "hello" -// t["two"] = "world" + t[1] = "hello" + t["two"] = "world" -// return t[1], t["two"] -// `, vm; + return t + `, vm; -// t.plan(2); - -// t.comment("Running following code: \n" + luaCode); - -// t.doesNotThrow(function () { -// vm = getVM(luaCode); -// vm.execute(); -// }, "Program executed without errors"); - -// t.stritEqual( -// vm.L.stack[vm.L.top - 1].value.array[1], -// "hello", -// "Program output is correct" -// ); - -// t.stritEqual( -// vm.L.stack[vm.L.top - 1].value.hash.get("two"), -// "world", -// "Program output is correct" -// ); -// }); \ No newline at end of file + t.plan(3); + + 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.array[1].value, + "hello", + "Program output is correct" + ); + + t.strictEqual( + vm.L.stack[vm.L.top - 1].value.hash.get("two").value, + "world", + "Program output is correct" + ); +}); \ No newline at end of file -- cgit v1.2.3-54-g00ecf