summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lfunc.js19
-rw-r--r--src/lobject.js37
-rw-r--r--src/lstate.js4
-rw-r--r--src/lvm.js54
-rw-r--r--tests/lvm.js121
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