aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-14 14:24:38 +0100
committerBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-14 14:57:11 +0100
commit4f76b9ac649cfa47b61f6b29af8c24e08cafec33 (patch)
tree64a57aba1be35ddd2f6d5c8c831da24f81601d8c
parent238009cd056acc3277b38eb6520c1afee019bb26 (diff)
downloadfengari-4f76b9ac649cfa47b61f6b29af8c24e08cafec33.tar.gz
fengari-4f76b9ac649cfa47b61f6b29af8c24e08cafec33.tar.bz2
fengari-4f76b9ac649cfa47b61f6b29af8c24e08cafec33.zip
__index/__newindex chain
-rw-r--r--README.md8
-rw-r--r--tests/ltm.js401
2 files changed, 402 insertions, 7 deletions
diff --git a/README.md b/README.md
index d899717..c3e2b39 100644
--- a/README.md
+++ b/README.md
@@ -17,10 +17,10 @@
- [ ] userdata
- [ ] thread
- [ ] Tag Methods
- - [ ] `__index`
- - [ ] `__newindex`
+ - [x] `__index`
+ - [x] `__newindex`
- [x] `__gc` (unavailable)
- - [ ] `__mode`
+ - [x] `__mode` (unavailable)
- [ ] `__len`
- [ ] `__eq`
- [ ] `__add`
@@ -41,6 +41,8 @@
- [ ] `__le`
- [ ] `__concat`
- [ ] `__call`
+ - [ ] `__tostring`
+ - [ ] `__pairs`
- [ ] Debug (errors)
- [ ] C API
- [ ] stdlib
diff --git a/tests/ltm.js b/tests/ltm.js
index e7085cd..003afe3 100644
--- a/tests/ltm.js
+++ b/tests/ltm.js
@@ -125,8 +125,8 @@ test('__index function in metatable', function (t) {
"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.comment("We set a breakpoint")
+ L.stack[0].p.code[4].breakpoint = true;
t.doesNotThrow(function () {
VM.luaV_execute(L);
@@ -216,8 +216,8 @@ test('__newindex function in metatable', function (t) {
"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.comment("We set a breakpoint")
+ L.stack[0].p.code[4].breakpoint = true;
t.doesNotThrow(function () {
VM.luaV_execute(L);
@@ -255,4 +255,397 @@ test('__newindex function in metatable', function (t) {
null,
"Program output is correct"
);
+});
+
+
+test('__index table in metatable', function (t) {
+ let luaCode = `
+ local mmt = {
+ yo = "hello"
+ }
+
+ local mt = {
+ __index = mmt
+ }
+
+ 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> (8 instructions at 0x7fb57cc03210)
+ // 0+ params, 4 slots, 1 upvalue, 3 locals, 3 constants, 0 functions
+ // 1 [1] NEWTABLE 0 0 1
+ // 2 [2] SETTABLE 0 -1 -2 ; "yo" "hello"
+ // 3 [4] NEWTABLE 1 0 1
+ // 4 [5] SETTABLE 1 -3 0 ; "__index" -
+ // 5 [7] NEWTABLE 2 0 0
+ // 6 [9] GETTABLE 3 2 -1 ; "yo" <=== We stop here
+ // 7 [9] RETURN 3 2
+ // 8 [9] RETURN 0 1
+
+ t.strictEqual(
+ OC.OpCodes[L.stack[0].p.code[5].opcode],
+ "OP_GETTABLE",
+ "Correct opcode marked as breakpoint"
+ );
+
+ t.comment("We set a breakpoint")
+ L.stack[0].p.code[5].breakpoint = true;
+
+ 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[5].breakpoint = false;
+
+ t.ok(
+ L.stack[3].ttistable() && !L.stack[3].value.hash.get("__index"),
+ "t is on stack at 3"
+ );
+
+ t.ok(
+ L.stack[2].ttistable() && L.stack[2].value.hash.get("__index"),
+ "mt is on stack at 2"
+ );
+
+ t.comment("We manually set t's metatable to mt");
+ L.stack[3].metatable = L.stack[2];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Second part of the program executed without errors");
+
+ t.strictEqual(
+ L.stack[L.top - 1].value,
+ "hello",
+ "Program output is correct"
+ );
+});
+
+
+test('__newindex table in metatable', function (t) {
+ let luaCode = `
+ local mmt = {
+ yo = "hello"
+ }
+
+ local mt = {
+ __newindex = mmt
+ }
+
+ local t = {}
+
+ -- setmetatable(t, mt)
+
+ t.yo = "world"
+
+ return t.yo, mmt.yo
+ `, L;
+
+ t.plan(9);
+
+ t.comment("Running following code: \n" + luaCode);
+
+ t.doesNotThrow(function () {
+ L = getState(luaCode);
+ }, "Bytecode parsed without errors");
+
+
+ // main <hello.lua:0,0> (10 instructions at 0x7fba6a403210)
+ // 0+ params, 5 slots, 1 upvalue, 3 locals, 4 constants, 0 functions
+ // 1 [1] NEWTABLE 0 0 1
+ // 2 [2] SETTABLE 0 -1 -2 ; "yo" "hello"
+ // 3 [5] NEWTABLE 1 0 1
+ // 4 [6] SETTABLE 1 -3 0 ; "__newindex" -
+ // 5 [9] NEWTABLE 2 0 0
+ // 6 [13] SETTABLE 2 -1 -4 ; "yo" "world" <=== We stop here
+ // 7 [15] GETTABLE 3 2 -1 ; "yo"
+ // 8 [15] GETTABLE 4 0 -1 ; "yo"
+ // 9 [15] RETURN 3 3
+ // 10 [15] RETURN 0 1
+
+ t.strictEqual(
+ OC.OpCodes[L.stack[0].p.code[5].opcode],
+ "OP_SETTABLE",
+ "Correct opcode marked as breakpoint"
+ );
+
+ t.comment("We set a breakpoint")
+ L.stack[0].p.code[5].breakpoint = true;
+
+ 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[5].breakpoint = false;
+
+ t.ok(
+ L.stack[3].ttistable() && !L.stack[3].value.hash.get("__newindex"),
+ "t is on stack at 3"
+ );
+
+ t.ok(
+ L.stack[2].ttistable() && L.stack[2].value.hash.get("__newindex"),
+ "mt is on stack at 2"
+ );
+
+ t.comment("We manually set t's metatable to mt");
+ L.stack[3].metatable = L.stack[2];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Second part of the program executed without errors");
+
+ t.strictEqual(
+ L.stack[L.top - 1].value,
+ "world",
+ "Program output is correct"
+ );
+
+ t.strictEqual(
+ L.stack[L.top - 2].value,
+ null,
+ "Program output is correct"
+ );
+});
+
+
+test('__index table with own metatable', function (t) {
+ let luaCode = `
+ local mmmt = {
+ __index = function (t, k)
+ return "hello"
+ end
+ }
+
+ local mmt = {
+ yoo = "bye"
+ }
+
+ -- setmetatable(mmt, mmmt)
+
+ local mt = {
+ __index = mmt
+ }
+
+ -- setmetatable(mt, mmt)
+
+ local t = {}
+
+ -- setmetatable(t, mt)
+
+ return t.yo
+ `, L;
+
+ t.plan(6);
+
+ t.comment("Running following code: \n" + luaCode);
+
+ t.doesNotThrow(function () {
+ L = getState(luaCode);
+ }, "Bytecode parsed without errors");
+
+
+ // main <hello.lua:0,0> (11 instructions at 0x7f96e3403210)
+ // 0+ params, 5 slots, 1 upvalue, 4 locals, 3 constants, 1 function
+ // 1 [1] NEWTABLE 0 0 1
+ // 2 [4] CLOSURE 1 0 ; 0x7f96e3403440
+ // 3 [4] SETTABLE 0 -1 1 ; "__index" -
+ // 4 [7] NEWTABLE 1 0 1
+ // 5 [8] SETTABLE 1 -2 -3 ; "yo" "bye"
+ // 6 [13] NEWTABLE 2 0 1 <=== We stop here
+ // 7 [14] SETTABLE 2 -1 1 ; "__index" -
+ // 8 [17] NEWTABLE 3 0 0 <=== We stop here
+ // 9 [21] GETTABLE 4 3 -2 ; "yo" <=== We stop here
+ // 10 [21] RETURN 4 2
+ // 11 [21] RETURN 0 1
+ //
+ // function <hello.lua:2,4> (3 instructions at 0x7f96e3403440)
+ // 2 params, 3 slots, 0 upvalues, 2 locals, 1 constant, 0 functions
+ // 1 [3] LOADK 2 -1 ; "hello"
+ // 2 [3] RETURN 2 2
+ // 3 [4] RETURN 0 1
+
+ L.stack[0].p.code[5].breakpoint = true;
+ L.stack[0].p.code[7].breakpoint = true;
+ L.stack[0].p.code[8].breakpoint = true;
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "First part of the program executed without errors");
+
+ L.ci.pcOff--;
+ L.stack[0].p.code[5].breakpoint = false;
+
+ t.comment("We manually set mmt's metatable to mmmt");
+ L.stack[2].metatable = L.stack[1];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Second part of the program executed without errors");
+
+ L.ci.pcOff--;
+ L.stack[0].p.code[7].breakpoint = false;
+
+ t.comment("We manually set mt's metatable to mmt");
+ L.stack[3].metatable = L.stack[2];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Third part of the program executed without errors");
+
+ L.ci.pcOff--;
+ L.stack[0].p.code[8].breakpoint = false;
+
+ t.comment("We manually set t's metatable to mt");
+ L.stack[4].metatable = L.stack[3];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Fourth part of the program executed without errors");
+
+ t.strictEqual(
+ L.stack[L.top - 1].value,
+ "hello",
+ "Program output is correct"
+ );
+});
+
+
+test('__newindex table with own metatable', function (t) {
+ let luaCode = `
+ local up = nil
+
+ local mmmt = {
+ __newindex = function (t, k, v)
+ up = v
+ end
+ }
+
+ local mmt = {}
+
+ -- setmetatable(mmt, mmmt)
+
+ local mt = {
+ __newindex = mmt
+ }
+
+ -- setmetatable(mt, mmt)
+
+ local t = {}
+
+ -- setmetatable(t, mt)
+
+ t.yo = "hello"
+
+ return t.yo, up
+ `, L;
+
+ t.plan(7);
+
+ t.comment("Running following code: \n" + luaCode);
+
+ t.doesNotThrow(function () {
+ L = getState(luaCode);
+ }, "Bytecode parsed without errors");
+
+
+ // main <hello.lua:0,0> (13 instructions at 0x7ff6ed500640)
+ // 0+ params, 7 slots, 1 upvalue, 5 locals, 3 constants, 1 function
+ // 1 [1] LOADNIL 0 0
+ // 2 [3] NEWTABLE 1 0 1
+ // 3 [6] CLOSURE 2 0 ; 0x7ff6ed5007f0
+ // 4 [6] SETTABLE 1 -1 2 ; "__newindex" -
+ // 5 [9] NEWTABLE 2 0 0
+ // 6 [13] NEWTABLE 3 0 1 <=== We stop here
+ // 7 [14] SETTABLE 3 -1 2 ; "__newindex" -
+ // 8 [19] NEWTABLE 4 0 0 <=== We stop here
+ // 9 [23] SETTABLE 4 -2 -3 ; "yo" "hello" <=== We stop here
+ // 10 [25] GETTABLE 5 4 -2 ; "yo"
+ // 11 [25] MOVE 6 0
+ // 12 [25] RETURN 5 3
+ // 13 [25] RETURN 0 1
+ //
+ // function <hello.lua:4,6> (2 instructions at 0x7ff6ed5007f0)
+ // 3 params, 3 slots, 1 upvalue, 3 locals, 0 constants, 0 functions
+ // 1 [5] SETUPVAL 2 0 ; up
+ // 2 [6] RETURN 0 1
+
+ L.stack[0].p.code[5].breakpoint = true;
+ L.stack[0].p.code[7].breakpoint = true;
+ L.stack[0].p.code[8].breakpoint = true;
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "First part of the program executed without errors");
+
+ L.ci.pcOff--;
+ L.stack[0].p.code[5].breakpoint = false;
+
+ t.comment("We manually set mmt's metatable to mmmt");
+ L.stack[2].metatable = L.stack[1];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Second part of the program executed without errors");
+
+ L.ci.pcOff--;
+ L.stack[0].p.code[7].breakpoint = false;
+
+ t.comment("We manually set mt's metatable to mmt");
+ L.stack[3].metatable = L.stack[2];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Third part of the program executed without errors");
+
+ L.ci.pcOff--;
+ L.stack[0].p.code[8].breakpoint = false;
+
+ t.comment("We manually set t's metatable to mt");
+ L.stack[4].metatable = L.stack[3];
+
+ t.doesNotThrow(function () {
+ VM.luaV_execute(L);
+ }, "Fourth part of the program executed without errors");
+
+ t.strictEqual(
+ L.stack[L.top - 2].value,
+ "hello",
+ "Program output is correct"
+ );
+
+ t.strictEqual(
+ L.stack[L.top - 1].value,
+ null,
+ "Program output is correct"
+ );
}); \ No newline at end of file