aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--src/ltm.js2
-rw-r--r--src/lvm.js10
-rw-r--r--tests/ltm.js254
4 files changed, 262 insertions, 8 deletions
diff --git a/README.md b/README.md
index 132a37f..9dce060 100644
--- a/README.md
+++ b/README.md
@@ -37,8 +37,8 @@
- [x] `__shr`
- [ ] `__unm`
- [ ] `__bnot`
- - [ ] `__lt`
- - [ ] `__le`
+ - [x] `__lt`
+ - [x] `__le`
- [ ] `__concat`
- [ ] `__call`
- [ ] `__tostring`
diff --git a/src/ltm.js b/src/ltm.js
index 4dc9cc7..f5c34b0 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -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;
diff --git a/src/lvm.js b/src/lvm.js
index cd9548e..815ca2f 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -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