aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ltm.js2
-rw-r--r--src/lvm.js3
-rw-r--r--tests/ltm.js97
3 files changed, 96 insertions, 6 deletions
diff --git a/src/ltm.js b/src/ltm.js
index f963aa9..fc0d0ec 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -87,7 +87,7 @@ const luaT_gettmbyobj = function(L, o, event) {
switch(o.ttnov()) {
case CT.LUA_TTABLE:
case CT.LUA_TTUSERDATA:
- mt = o.value.metatable;
+ mt = o.metatable;
break;
default:
// TODO: mt = G(L)->mt[ttnov(o)];
diff --git a/src/lvm.js b/src/lvm.js
index d5c6c46..b0b5d53 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -68,6 +68,9 @@ const luaV_execute = function(L) {
opcode = OC.OpCodes[i.opcode];
}
+ if (i.breakpoint) // TODO: remove, used until lapi
+ return;
+
switch (opcode) {
case "OP_MOVE": {
L.stack[ra] = L.stack[RB(L, base, i)];
diff --git a/tests/ltm.js b/tests/ltm.js
index cb0e38d..5644ae8 100644
--- a/tests/ltm.js
+++ b/tests/ltm.js
@@ -1,12 +1,13 @@
/*jshint esversion: 6 */
"use strict";
-const test = require('tape');
-const beautify = require('js-beautify').js_beautify;
+const test = require('tape');
+const beautify = require('js-beautify').js_beautify;
-const VM = require("../src/lvm.js");
+const VM = require("../src/lvm.js");
+const OC = require('../src/lopcodes.js');
-const getState = require("./tests.js").getState;
+const getState = require("./tests.js").getState;
test('__index, __newindex: with actual table', function (t) {
@@ -55,7 +56,6 @@ test('__index: with non table', function (t) {
t.throws(function () {
VM.luaV_execute(L);
}, "Program executed with expected error");
-
});
@@ -76,5 +76,92 @@ test('__newindex: with non table', function (t) {
t.throws(function () {
VM.luaV_execute(L);
}, "Program executed with expected error");
+});
+
+
+test('__index function in metatable', function (t) {
+ let luaCode = `
+ local mt = {
+ __index = function (table, key)
+ return "__index"
+ end
+ }
+
+ local t = {}
+
+ -- setmetatable(t, mt)
+
+ return t.yo
+ `, L;
+
+ t.plan(8);
+
+ t.comment("Running following code: \n" + luaCode);
+
+ t.doesNotThrow(function () {
+ L = getState(luaCode);
+ }, "Bytecode parsed without errors");
+
+
+ // main <hello.lua:0,0> (7 instructions at 0x7fe6b4403050)
+ // 0+ params, 3 slots, 1 upvalue, 2 locals, 2 constants, 1 function
+ // 1 [1] NEWTABLE 0 0 1
+ // 2 [4] CLOSURE 1 0 ; 0x7fe6b4403290
+ // 3 [4] SETTABLE 0 -1 1 ; "__index" -
+ // 4 [7] NEWTABLE 1 0 0
+ // 5 [9] GETTABLE 2 1 -2 ; "yo" <=== We stop here
+ // 6 [9] RETURN 2 2
+ // 7 [9] RETURN 0 1
+ //
+ // function <hello.lua:2,4> (3 instructions at 0x7fe6b4403290)
+ // 2 params, 3 slots, 0 upvalues, 2 locals, 1 constant, 0 functions
+ // 1 [3] LOADK 2 -1 ; "__index"
+ // 2 [3] RETURN 2 2
+ // 3 [4] RETURN 0 1
+
+ t.strictEqual(
+ OC.OpCodes[L.stack[0].p.code[4].opcode],
+ "OP_GETTABLE",
+ "Correct opcode marked as breakpoint"
+ );
+
+ t.comment("We set a breakpoint just before 'return t.yo'")
+ L.stack[0].p.code[4].breakpoint = true; // Stop just before 'return t.yo'
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "First part of the program executed without errors");
+
+ t.strictEqual(
+ OC.OpCodes[L.ci.u.l.savedpc[L.ci.pcOff - 1].opcode],
+ "OP_GETTABLE",
+ "Stopped at correct opcode"
+ );
+
+ t.comment("We unset the breakpoint and correct pcOff");
+ L.ci.pcOff--;
+ L.stack[0].p.code[4].breakpoint = false;
+
+ t.ok(
+ L.stack[2].ttistable() && !L.stack[2].value.hash.get("__index"),
+ "t is on stack at 2"
+ );
+ t.ok(
+ L.stack[1].ttistable() && L.stack[1].value.hash.get("__index"),
+ "mt is on stack at 1"
+ );
+
+ t.comment("We manually set t's metatable to mt");
+ L.stack[2].metatable = L.stack[1];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Second part of the program executed without errors");
+
+ t.strictEqual(
+ L.stack[L.top - 1].value,
+ "__index",
+ "Program output is correct"
+ );
}); \ No newline at end of file