From 97cd23961c8a5b69c4845528a1a494f40127089f Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 14 Feb 2017 08:22:57 +0100 Subject: settable, gettable (handle tm) --- src/lvm.js | 128 ++++++++++++++++++++++++++++++++++++++++++++++------------- tests/ltm.js | 23 ++++++----- 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 -- cgit v1.2.3-70-g09d2