aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lvm.js128
-rw-r--r--tests/ltm.js23
2 files changed, 113 insertions, 38 deletions
diff --git a/src/lvm.js b/src/lvm.js
index 092559a..d5c6c46 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -19,6 +19,7 @@ const lstate = require('./lstate.js');
const CallInfo = lstate.CallInfo;
const llimit = require('./llimit.js');
const ldo = require('./ldo.js');
+const nil = ldo.nil;
const ltm = require('./ltm.js');
const ltable = require('./ltable.js');
const TMS = ltm.TMS;
@@ -91,7 +92,7 @@ const luaV_execute = function(L) {
}
case "OP_LOADNIL": {
for (let j = 0; j <= i.B; j++)
- L.stack[ra + j] = ldo.nil;
+ L.stack[ra + j] = nil;
break;
}
case "OP_GETUPVAL": {
@@ -106,11 +107,8 @@ const luaV_execute = function(L) {
let table = cl.upvals[i.B].val(L);
let key = RKC(L, base, k, i);
- // if (!table.ttistable() || !table.__index(table, key)) {
- // // __index
- // } else {
- L.stack[ra] = table.__index(table, key);
- // }
+ gettable(L, table, key, ra);
+ base = ci.u.l.base;
break;
}
case "OP_SETTABUP": {
@@ -118,11 +116,8 @@ const luaV_execute = function(L) {
let key = RKB(L, base, k, i);
let v = RKC(L, base, k, i);
- // if (!table.ttistable() || !table.__index(table, key)) {
- // // __index
- // } else {
- table.__newindex(table, key, v);
- // }
+ settable(L, table, key, v);
+ base = ci.u.l.base;
break;
}
@@ -130,11 +125,8 @@ const luaV_execute = function(L) {
let table = RKB(L, base, k, i);
let key = RKC(L, base, k, i);
- // if (!table.ttistable() || !table.__index(table, key)) {
- // // __index
- // } else {
- L.stack[ra] = table.__index(table, key);
- // }
+ gettable(L, table, key, ra);
+ base = ci.u.l.base;
break;
}
case "OP_SETTABLE": {
@@ -142,11 +134,8 @@ const luaV_execute = function(L) {
let key = RKB(L, base, k, i);
let v = RKC(L, base, k, i);
- // if (!table.ttistable() || !table.__index(table, key)) {
- // // __index
- // } else {
- table.__newindex(table, key, v);
- // }
+ settable(L, table, key, v);
+ base = ci.u.l.base;
break;
}
@@ -160,11 +149,8 @@ const luaV_execute = function(L) {
L.stack[ra + 1] = table;
- // if (!table.ttistable() || !table.__index(table, key)) {
- // // __index
- // } else {
- L.stack[ra] = table.__index(table, key);
- // }
+ gettable(L, table, key, ra);
+ base = ci.u.l.base;
break;
}
@@ -646,7 +632,7 @@ const luaV_execute = function(L) {
L.stack[ra + j] = L.stack[base - n + j];
for (; j < b; j++) /* complete required results with nil */
- L.stack[ra + j] = ldo.nil;
+ L.stack[ra + j] = nil;
break;
}
case "OP_EXTRAARG": {
@@ -937,6 +923,94 @@ const luaV_concat = function(L, total) {
} while (total > 1); /* repeat until only 1 result left */
};
+const MAXTAGRECUR = 2000;
+
+const gettable = function(L, table, key, ra, recur) {
+ recur = recur ? recur : 0;
+
+ if (recur >= MAXTAGRECUR)
+ throw new Error("'__index' chain too long; possible loop"); // TODO: luaG_runerror
+
+ if (table.ttistable()) {
+ let element = table.__index(table, key);
+
+ if (!element.ttisnil()) {
+ L.stack[ra] = table.__index(table, key);
+ } else {
+ luaV_finishget(L, table, key, ra, element, recur);
+ }
+ } else {
+ luaV_finishget(L, table, key, ra, null, recur);
+ }
+};
+
+const luaV_finishget = function(L, t, key, val, slot, recur) {
+ let tm;
+ if (slot === null) { /* 't' is not a table? */
+ assert(!t.ttistable());
+ tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_INDEX);
+ if (tm.ttisnil())
+ throw new Error(`attempt to index a ${tm.ttype()} value`); // TODO: luaG_typeerror
+ } else { /* 't' is a table */
+ assert(slot.ttisnil());
+ tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_INDEX); // TODO: fasttm
+ if (tm.ttisnil()) {
+ L.stack[val] = nil;
+ return;
+ }
+ }
+
+ if (tm.ttisfunction()) {
+ ltm.luaT_callTM(L, tm, t, key, val, 1);
+ return;
+ }
+
+ gettable(L, tm, key, val, recur + 1);
+};
+
+const settable = function(L, table, key, v, recur) {
+ recur = recur ? recur : 0;
+
+ if (recur >= MAXTAGRECUR)
+ throw new Error("'__newindex' chain too long; possible loop"); // TODO: luaG_runerror
+
+ if (table.ttistable()) {
+ let element = table.__index(table, key);
+
+ if (!element.ttisnil()) {
+ table.__newindex(table, key, v);
+ } else {
+ luaV_finishset(L, table, key, v, element, recur);
+ }
+ } else {
+ luaV_finishset(L, table, key, v, null, recur);
+ }
+};
+
+const luaV_finishset = function(L, t, key, val, slot, recur) {
+ let tm;
+ if (slot !== null) { /* is 't' a table? */
+ assert(slot.ttisnil());
+ tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_NEWINDEX); // TODO: fasttm
+ if (tm.ttisnil()) {
+ t.__newindex(t, key, val);
+ return;
+ }
+ } else { /* not a table; check metamethod */
+ tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_NEWINDEX);
+ if (tm.ttisnil())
+ throw new Error(`attempt to index a ${tm.ttype()} value`); // TODO: luaG_typeerror
+ }
+
+ if (tm.ttisfunction()) {
+ ltm.luaT_callTM(L, tm, t, key, val, 1);
+ return;
+ }
+
+ settable(L, tm, key, val, recur + 1);
+}
+
+
module.exports.RA = RA;
module.exports.RB = RB;
module.exports.RC = RC;
diff --git a/tests/ltm.js b/tests/ltm.js
index 8a5ccb7..290ef9c 100644
--- a/tests/ltm.js
+++ b/tests/ltm.js
@@ -4,23 +4,18 @@
const test = require('tape');
const beautify = require('js-beautify').js_beautify;
-const lua_State = require("../src/lstate.js").lua_State;
const VM = require("../src/lvm.js");
-const Table = require("../src/lobject.js").Table;;
-const getState = require("./tests.js");
+const getState = require("./tests.js").getState;
-test('__add', function (t) {
+test('__index', function (t) {
let luaCode = `
- local t = {}
- setmetatable(t, {__add = function ()
- return "hello"
- end})
- return t + 1
+ local t = {yo=1}
+ return t.yo, t.lo
`, L;
- t.plan(2);
+ t.plan(3);
t.comment("Running following code: \n" + luaCode);
@@ -31,7 +26,13 @@ test('__add', function (t) {
t.strictEqual(
L.stack[L.top - 1].value,
- "hello",
+ null,
+ "Program output is correct"
+ );
+
+ t.strictEqual(
+ L.stack[L.top - 2].value,
+ 1,
"Program output is correct"
);
}); \ No newline at end of file