diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | src/ltm.js | 2 | ||||
-rw-r--r-- | src/lvm.js | 10 | ||||
-rw-r--r-- | tests/ltm.js | 254 |
4 files changed, 262 insertions, 8 deletions
@@ -37,8 +37,8 @@ - [x] `__shr` - [ ] `__unm` - [ ] `__bnot` - - [ ] `__lt` - - [ ] `__le` + - [x] `__lt` + - [x] `__le` - [ ] `__concat` - [ ] `__call` - [ ] `__tostring` @@ -79,7 +79,7 @@ const luaT_trybinTM = function(L, p1, p2, res, event) { }; const luaT_callorderTM = function(L, p1, p2, event) { - if (!luaT_callbinTM(L, p2, p2, L.top, event)) + if (!luaT_callbinTM(L, p1, p2, L.top, event)) return -1; else return !L.stack[L.top].l_isfalse() ? 1 : 0; @@ -662,7 +662,7 @@ const luaV_lessthan = function(L, l, r) { else if (l.ttisstring() && r.ttisstring()) return l_strcmp(l, r) < 0; else { - let res = ltm.luatT_callorderTM(L, l, r, TMS.TM_LT); + let res = ltm.luaT_callorderTM(L, l, r, TMS.TM_LT); if (res < 0) throw new Error("TM order error"); // TODO: luaG_ordererror return res; @@ -677,17 +677,17 @@ const luaV_lessequal = function(L, l, r) { else if (l.ttisstring() && r.ttisstring()) return l_strcmp(l, r) <= 0; else { - res = ltm.luatT_callorderTM(L, l, r, TMS.TM_LE); + res = ltm.luaT_callorderTM(L, l, r, TMS.TM_LE); if (res >= 0) return res; } L.ci.callstatus |= lstate.CIST_LEQ; /* mark it is doing 'lt' for 'le' */ - res = ltm.luatT_callorderTM(L, l, r, TMS.TM_LT); + res = ltm.luaT_callorderTM(L, l, r, TMS.TM_LT); L.ci.callstatus ^= lstate.CIST_LEQ; /* clear mark */ if (res < 0) throw new Error("TM order error"); // TODO: luaG_ordererror - return res === 1; + return res !== 1 ? 1 : 0; /* result is negated */ }; const luaV_equalobj = function(L, t1, t2) { @@ -709,11 +709,11 @@ const luaV_equalobj = function(L, t1, t2) { case CT.LUA_TNUMINT: case CT.LUA_TNUMFLT: case CT.LUA_TBOOLEAN: - case CT.LUA_TLIGHTUSERDATA: case CT.LUA_TLCF: case CT.LUA_TSHRSTR: case CT.LUA_TLNGSTR: return t1.value === t2.value ? 1 : 0; + case CT.LUA_TLIGHTUSERDATA: case CT.LUA_TUSERDATA: case CT.LUA_TTABLE: if (t1 === t2) return 1; diff --git a/tests/ltm.js b/tests/ltm.js index 4aec5d9..b3f5e30 100644 --- a/tests/ltm.js +++ b/tests/ltm.js @@ -813,4 +813,258 @@ test('binary __xxx functions in metatable', function (t) { ], "Program output is correct" ); +}); + + +test('__eq', function (t) { + let luaCode = ` + local mt = { + __eq = function (a, b) + return true + end + } + + local t = {} + + -- setmetatable(t, mt) + + return t == {} + `, L; + + t.plan(4); + + t.comment("Running following code: \n" + luaCode); + + // main <hello.lua:0,0> (11 instructions at 0x7fce9fc03210) + // 0+ params, 3 slots, 1 upvalue, 2 locals, 1 constant, 1 function + // 1 [1] NEWTABLE 0 0 1 + // 2 [4] CLOSURE 1 0 ; 0x7fce9fc03440 + // 3 [4] SETTABLE 0 -1 1 ; "__eq" - + // 4 [7] NEWTABLE 1 0 0 + // 5 [11] NEWTABLE 2 0 0 + // 6 [11] EQ 1 1 2 <=== We stop here + // 7 [11] JMP 0 1 ; to 9 + // 8 [11] LOADBOOL 2 0 1 + // 9 [11] LOADBOOL 2 1 0 + // 10 [11] RETURN 2 2 + // 11 [11] RETURN 0 1 + // + // ... + + t.doesNotThrow(function () { + L = getState(luaCode); + }, "Bytecode parsed without errors"); + + L.stack[0].p.code[5].breakpoint = true; + + t.doesNotThrow(function () { + ldo.luaD_call(L, 0, -1); + }, "First part of the program executed without errors"); + + L.ci.pcOff--; + L.stack[0].p.code[5].breakpoint = false; + + 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, + true, + "Program output is correct" + ); +}); + + +test('__lt', function (t) { + let luaCode = ` + local mt = { + __lt = function (a, b) + return true + end + } + + local t = {} + + -- setmetatable(t, mt) + + return t < {} + `, L; + + t.plan(4); + + t.comment("Running following code: \n" + luaCode); + + // main <hello.lua:0,0> (11 instructions at 0x7fc879d00ac0) + // 0+ params, 3 slots, 1 upvalue, 2 locals, 1 constant, 1 function + // 1 [1] NEWTABLE 0 0 1 + // 2 [4] CLOSURE 1 0 ; 0x7fc879d00d10 + // 3 [4] SETTABLE 0 -1 1 ; "__lt" - + // 4 [7] NEWTABLE 1 0 0 + // 5 [11] NEWTABLE 2 0 0 + // 6 [11] LT 1 1 2 <=== We stop here + // 7 [11] JMP 0 1 ; to 9 + // 8 [11] LOADBOOL 2 0 1 + // 9 [11] LOADBOOL 2 1 0 + // 10 [11] RETURN 2 2 + // 11 [11] RETURN 0 1 + // + // ... + + t.doesNotThrow(function () { + L = getState(luaCode); + }, "Bytecode parsed without errors"); + + L.stack[0].p.code[5].breakpoint = true; + + t.doesNotThrow(function () { + ldo.luaD_call(L, 0, -1); + }, "First part of the program executed without errors"); + + L.ci.pcOff--; + L.stack[0].p.code[5].breakpoint = false; + + 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, + true, + "Program output is correct" + ); +}); + + +test('__le', function (t) { + let luaCode = ` + local mt = { + __le = function (a, b) + return true + end + } + + local t = {} + + -- setmetatable(t, mt) + + return t <= {} + `, L; + + t.plan(4); + + t.comment("Running following code: \n" + luaCode); + + // main <hello.lua:0,0> (11 instructions at 0x7fc879d00ac0) + // 0+ params, 3 slots, 1 upvalue, 2 locals, 1 constant, 1 function + // 1 [1] NEWTABLE 0 0 1 + // 2 [4] CLOSURE 1 0 ; 0x7fc879d00d10 + // 3 [4] SETTABLE 0 -1 1 ; "__lt" - + // 4 [7] NEWTABLE 1 0 0 + // 5 [11] NEWTABLE 2 0 0 + // 6 [11] LE 1 1 2 <=== We stop here + // 7 [11] JMP 0 1 ; to 9 + // 8 [11] LOADBOOL 2 0 1 + // 9 [11] LOADBOOL 2 1 0 + // 10 [11] RETURN 2 2 + // 11 [11] RETURN 0 1 + // + // ... + + t.doesNotThrow(function () { + L = getState(luaCode); + }, "Bytecode parsed without errors"); + + L.stack[0].p.code[5].breakpoint = true; + + t.doesNotThrow(function () { + ldo.luaD_call(L, 0, -1); + }, "First part of the program executed without errors"); + + L.ci.pcOff--; + L.stack[0].p.code[5].breakpoint = false; + + 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, + true, + "Program output is correct" + ); +}); + + + + +test('__le that uses __lt', function (t) { + let luaCode = ` + local mt = { + __lt = function (a, b) + return false + end + } + + local t = {} + + -- setmetatable(t, mt) + + return {} <= t + `, L; + + t.plan(4); + + t.comment("Running following code: \n" + luaCode); + + // main <hello.lua:0,0> (11 instructions at 0x7fc879d00ac0) + // 0+ params, 3 slots, 1 upvalue, 2 locals, 1 constant, 1 function + // 1 [1] NEWTABLE 0 0 1 + // 2 [4] CLOSURE 1 0 ; 0x7fc879d00d10 + // 3 [4] SETTABLE 0 -1 1 ; "__lt" - + // 4 [7] NEWTABLE 1 0 0 + // 5 [11] NEWTABLE 2 0 0 + // 6 [11] LE 1 1 2 <=== We stop here + // 7 [11] JMP 0 1 ; to 9 + // 8 [11] LOADBOOL 2 0 1 + // 9 [11] LOADBOOL 2 1 0 + // 10 [11] RETURN 2 2 + // 11 [11] RETURN 0 1 + // + // ... + + t.doesNotThrow(function () { + L = getState(luaCode); + }, "Bytecode parsed without errors"); + + L.stack[0].p.code[5].breakpoint = true; + + t.doesNotThrow(function () { + ldo.luaD_call(L, 0, -1); + }, "First part of the program executed without errors"); + + L.ci.pcOff--; + L.stack[0].p.code[5].breakpoint = false; + + 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, + true, + "Program output is correct" + ); });
\ No newline at end of file |