aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-14 12:56:44 +0100
committerBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-14 12:56:44 +0100
commit238009cd056acc3277b38eb6520c1afee019bb26 (patch)
treeee13afbc9694a211cf79c0ab97c294c22b9cb9f7
parent9e3acbbb3f0dc45cc1444645cd1b4585ef911017 (diff)
downloadfengari-238009cd056acc3277b38eb6520c1afee019bb26.tar.gz
fengari-238009cd056acc3277b38eb6520c1afee019bb26.tar.bz2
fengari-238009cd056acc3277b38eb6520c1afee019bb26.zip
__newindex
-rw-r--r--src/ltm.js3
-rw-r--r--src/lvm.js2
-rw-r--r--tests/ltm.js91
3 files changed, 95 insertions, 1 deletions
diff --git a/src/ltm.js b/src/ltm.js
index fc0d0ec..c712160 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -1,6 +1,8 @@
/*jshint esversion: 6 */
"use strict";
+const assert = require('assert');
+
const lobject = require('./lobject.js');
const TValue = lobject.TValue;
const Table = lobject.Table;
@@ -56,6 +58,7 @@ const luaT_callTM = function(L, f, p1, p2, p3, hasres) {
ldo.luaD_callnoyield(L, func, hasres);
if (hasres) {
+ assert(typeof result === "number");
L.stack[result] = L.stack[--L.top];
}
};
diff --git a/src/lvm.js b/src/lvm.js
index b0b5d53..cd9548e 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -1006,7 +1006,7 @@ const luaV_finishset = function(L, t, key, val, slot, recur) {
}
if (tm.ttisfunction()) {
- ltm.luaT_callTM(L, tm, t, key, val, 1);
+ ltm.luaT_callTM(L, tm, t, key, val, 0);
return;
}
diff --git a/tests/ltm.js b/tests/ltm.js
index 5644ae8..e7085cd 100644
--- a/tests/ltm.js
+++ b/tests/ltm.js
@@ -164,4 +164,95 @@ test('__index function in metatable', function (t) {
"__index",
"Program output is correct"
);
+});
+
+
+test('__newindex function in metatable', function (t) {
+ let luaCode = `
+ local mt = {
+ __newindex = function (table, key, value)
+ return "__newindex"
+ end
+ }
+
+ local t = {}
+
+ -- setmetatable(t, mt)
+
+ t.yo = "hello"
+
+ 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> (8 instructions at 0x7faadcf00ac0)
+ // 0+ params, 3 slots, 1 upvalue, 2 locals, 3 constants, 1 function
+ // 1 [1] NEWTABLE 0 0 1
+ // 2 [4] CLOSURE 1 0 ; 0x7faadcf00d10
+ // 3 [4] SETTABLE 0 -1 1 ; "__newindex" -
+ // 4 [7] NEWTABLE 1 0 0
+ // 5 [11] SETTABLE 1 -2 -3 ; "yo" "hello" <=== We stop here
+ // 6 [13] GETTABLE 2 1 -2 ; "yo"
+ // 7 [13] RETURN 2 2
+ // 8 [13] RETURN 0 1
+ //
+ // function <hello.lua:2,4> (3 instructions at 0x7faadcf00d10)
+ // 3 params, 4 slots, 0 upvalues, 3 locals, 1 constant, 0 functions
+ // 1 [3] LOADK 3 -1 ; "__newindex"
+ // 2 [3] RETURN 3 2
+ // 3 [4] RETURN 0 1
+
+ t.strictEqual(
+ OC.OpCodes[L.stack[0].p.code[4].opcode],
+ "OP_SETTABLE",
+ "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_SETTABLE",
+ "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("__newindex"),
+ "t is on stack at 2"
+ );
+
+ t.ok(
+ L.stack[1].ttistable() && L.stack[1].value.hash.get("__newindex"),
+ "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,
+ null,
+ "Program output is correct"
+ );
}); \ No newline at end of file