summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ldebug.js56
-rw-r--r--src/ldo.js3
-rw-r--r--src/ltm.js18
-rw-r--r--src/lvm.js53
-rw-r--r--tests/ldebug.js84
5 files changed, 161 insertions, 53 deletions
diff --git a/src/ldebug.js b/src/ldebug.js
index 3180e40..be122e0 100644
--- a/src/ldebug.js
+++ b/src/ldebug.js
@@ -13,7 +13,6 @@ const lvm = require('./lvm.js');
const ltm = require('./ltm.js');
const lfunc = require('./lfunc.js');
const lapi = require('./lapi.js');
-const TMS = ltm.TMS;
const TValue = lobject.TValue;
const Table = lobject.Table;
const CT = lua.constant_types;
@@ -360,31 +359,31 @@ const funcnamefromcode = function(L, ci) {
case 'OP_SELF':
case 'OP_GETTABUP':
case 'OP_GETTABLE':
- tm = TMS.TM_INDEX;
+ tm = ltm.TMS.TM_INDEX;
break;
case 'OP_SETTABUP':
case 'OP_SETTABLE':
- tm = TMS.TM_NEWINDEX;
+ tm = ltm.TMS.TM_NEWINDEX;
break;
- case 'OP_ADD': tm = TMS.OP_ADD; break;
- case 'OP_SUB': tm = TMS.OP_SUB; break;
- case 'OP_MUL': tm = TMS.OP_MUL; break;
- case 'OP_MOD': tm = TMS.OP_MOD; break;
- case 'OP_POW': tm = TMS.OP_POW; break;
- case 'OP_DIV': tm = TMS.OP_DIV; break;
- case 'OP_IDIV': tm = TMS.OP_IDI; break;
- case 'OP_BAND': tm = TMS.OP_BAN; break;
- case 'OP_BOR': tm = TMS.OP_BOR; break;
- case 'OP_BXOR': tm = TMS.OP_BXO; break;
- case 'OP_SHL': tm = TMS.OP_SHL; break;
- case 'OP_SHR': tm = TMS.OP_SHR; break;
- case 'OP_UNM': tm = TMS.TM_UNM; break;
- case 'OP_BNOT': tm = TMS.TM_BNOT; break;
- case 'OP_LEN': tm = TMS.TM_LEN; break;
- case 'OP_CONCAT': tm = TMS.TM_CONCAT; break;
- case 'OP_EQ': tm = TMS.TM_EQ; break;
- case 'OP_LT': tm = TMS.TM_LT; break;
- case 'OP_LE': tm = TMS.TM_LE; break;
+ case 'OP_ADD': tm = ltm.TMS.OP_ADD; break;
+ case 'OP_SUB': tm = ltm.TMS.OP_SUB; break;
+ case 'OP_MUL': tm = ltm.TMS.OP_MUL; break;
+ case 'OP_MOD': tm = ltm.TMS.OP_MOD; break;
+ case 'OP_POW': tm = ltm.TMS.OP_POW; break;
+ case 'OP_DIV': tm = ltm.TMS.OP_DIV; break;
+ case 'OP_IDIV': tm = ltm.TMS.OP_IDI; break;
+ case 'OP_BAND': tm = ltm.TMS.OP_BAN; break;
+ case 'OP_BOR': tm = ltm.TMS.OP_BOR; break;
+ case 'OP_BXOR': tm = ltm.TMS.OP_BXO; break;
+ case 'OP_SHL': tm = ltm.TMS.OP_SHL; break;
+ case 'OP_SHR': tm = ltm.TMS.OP_SHR; break;
+ case 'OP_UNM': tm = ltm.TMS.TM_UNM; break;
+ case 'OP_BNOT': tm = ltm.TMS.TM_BNOT; break;
+ case 'OP_LEN': tm = ltm.TMS.TM_LEN; break;
+ case 'OP_CONCAT': tm = ltm.TMS.TM_CONCAT; break;
+ case 'OP_EQ': tm = ltm.TMS.TM_EQ; break;
+ case 'OP_LT': tm = ltm.TMS.TM_LT; break;
+ case 'OP_LE': tm = ltm.TMS.TM_LE; break;
default:
return null; /* cannot find a reasonable name */
}
@@ -494,6 +493,16 @@ const luaG_errormsg = function(L) {
ldo.luaD_throw(L, TS.LUA_ERRRUN);
};
+/*
+** Error when both values are convertible to numbers, but not to integers
+*/
+const luaG_tointerror = function(L, p1, p2) {
+ let temp = lvm.tointeger(p1);
+ if (temp === false)
+ p2 = p1;
+ luaG_runerror(L, `number${varinfo(L, p2)} has no integer representation`);
+}
+
module.exports.lua_getstack = lua_getstack;
module.exports.lua_getinfo = lua_getinfo;
module.exports.luaG_errormsg = luaG_errormsg;
@@ -502,4 +511,5 @@ module.exports.luaG_runerror = luaG_runerror;
module.exports.luaG_typeerror = luaG_typeerror;
module.exports.luaG_concaterror = luaG_concaterror;
module.exports.luaG_opinterror = luaG_opinterror;
-module.exports.luaG_ordererror = luaG_ordererror; \ No newline at end of file
+module.exports.luaG_ordererror = luaG_ordererror;
+module.exports.luaG_tointerror = luaG_tointerror; \ No newline at end of file
diff --git a/src/ldo.js b/src/ldo.js
index 04592e9..bf07c8c 100644
--- a/src/ldo.js
+++ b/src/ldo.js
@@ -15,7 +15,6 @@ const CT = lua.constant_types;
const TS = lua.thread_status;
const LUA_MULTRET = lua.LUA_MULTRET;
const TValue = lobject.TValue;
-const TMS = ltm.TMS;
const nil = new TValue(CT.LUA_TNIL, null);
@@ -189,7 +188,7 @@ const adjust_varargs = function(L, p, actual) {
};
const tryfuncTM = function(L, off, func) {
- let tm = ltm.luaT_gettmbyobj(L, func, TMS.TM_CALL);
+ let tm = ltm.luaT_gettmbyobj(L, func, ltm.TMS.TM_CALL);
if (!tm.ttisfunction(tm))
throw new Error("__call metatable member is not a function"); // TODO: luaG_typeerror
/* Open a hole inside the stack at 'func' */
diff --git a/src/ltm.js b/src/ltm.js
index f163f44..6186066 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -9,6 +9,8 @@ const Table = lobject.Table;
const ldo = require('./ldo.js');
const lstate = require('./lstate.js');
const lua = require('./lua.js');
+const ldebug = require('./ldebug.js');
+const lvm = require('./lvm.js');
const CT = lua.constant_types;
@@ -114,7 +116,21 @@ const luaT_callbinTM = function(L, p1, p2, res, event) {
const luaT_trybinTM = function(L, p1, p2, res, event) {
if (!luaT_callbinTM(L, p1, p2, res, event)) {
- throw new Error("TM error"); // TODO: luaG_error
+ switch (event) {
+ case TMS.TM_CONCAT:
+ ldebug.luaG_concaterror(L, p1, p2);
+ case TMS.TM_BAND: case TMS.TM_BOR: case TMS.TM_BXOR:
+ case TMS.TM_SHL: case TMS.TM_SHR: case TMS.TM_BNOT: {
+ let n1 = lvm.tonumber(p1);
+ let n2 = lvm.tonumber(p2);
+ if (n1 !== false && n2 !== false)
+ ldebug.luaG_tointerror(L, p1, p2);
+ else
+ ldebug.luaG_opinterror(L, p1, p2, "perform bitwise operation on");
+ }
+ default:
+ ldebug.luaG_opinterror(L, p1, p2, "perform arithmetic on");
+ }
}
};
diff --git a/src/lvm.js b/src/lvm.js
index 97292cd..c74fa45 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -21,7 +21,6 @@ const ldo = require('./ldo.js');
const ltm = require('./ltm.js');
const ltable = require('./ltable.js');
const ldebug = require('./ldebug.js');
-const TMS = ltm.TMS;
const RA = function(L, base, i) {
return base + i.A;
@@ -167,7 +166,7 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, op1.value + op2.value);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_ADD);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_ADD);
base = ci.u.l.base;
}
break;
@@ -183,7 +182,7 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, op1.value - op2.value);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_SUB);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SUB);
base = ci.u.l.base;
}
break;
@@ -199,7 +198,7 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value * op2.value);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_MUL);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_MUL);
base = ci.u.l.base;
}
break;
@@ -215,7 +214,7 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value % op2.value);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_MOD);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_MOD);
base = ci.u.l.base;
}
break;
@@ -229,7 +228,7 @@ const luaV_execute = function(L) {
if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, Math.pow(op1.value, op2.value));
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_POW);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_POW);
base = ci.u.l.base;
}
break;
@@ -243,7 +242,7 @@ const luaV_execute = function(L) {
if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, k[i.B].value / op2.value);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_DIV);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_DIV);
base = ci.u.l.base;
}
break;
@@ -259,7 +258,7 @@ const luaV_execute = function(L) {
} else if (numberop1 !== false && numberop2 !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, (op1.value / op2.value)|0);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_IDIV);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_IDIV);
base = ci.u.l.base;
}
break;
@@ -273,7 +272,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value & op2.value)|0);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_BAND);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BAND);
base = ci.u.l.base;
}
break;
@@ -287,7 +286,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value | op2.value)|0);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_BOR);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BOR);
base = ci.u.l.base;
}
break;
@@ -301,7 +300,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value ^ op2.value)|0);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_BXOR);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_BXOR);
base = ci.u.l.base;
}
break;
@@ -315,7 +314,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value << op2.value)|0);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_SHL);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SHL);
base = ci.u.l.base;
}
break;
@@ -329,7 +328,7 @@ const luaV_execute = function(L) {
if (op1.ttisinteger() && op2.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, (op1.value >> op2.value)|0);
} else {
- ltm.luaT_trybinTM(L, op1, op2, ra, TMS.TM_SHR);
+ ltm.luaT_trybinTM(L, op1, op2, ra, ltm.TMS.TM_SHR);
base = ci.u.l.base;
}
break;
@@ -343,7 +342,7 @@ const luaV_execute = function(L) {
} else if (numberop !== false) {
L.stack[ra] = new TValue(CT.LUA_TNUMFLT, -op.value);
} else {
- ltm.luaT_trybinTM(L, op, op, ra, TMS.TM_UNM);
+ ltm.luaT_trybinTM(L, op, op, ra, ltm.TMS.TM_UNM);
base = ci.u.l.base;
}
break;
@@ -355,7 +354,7 @@ const luaV_execute = function(L) {
if (op.ttisinteger()) {
L.stack[ra] = new TValue(CT.LUA_TNUMINT, ~op.value);
} else {
- ltm.luaT_trybinTM(L, op, op, ra, TMS.TM_BNOT);
+ ltm.luaT_trybinTM(L, op, op, ra, ltm.TMS.TM_BNOT);
base = ci.u.l.base;
}
break;
@@ -661,7 +660,7 @@ const luaV_lessthan = function(L, l, r) {
else if (l.ttisstring() && r.ttisstring())
return l_strcmp(l, r) < 0;
else {
- let res = ltm.luaT_callorderTM(L, l, r, TMS.TM_LT);
+ let res = ltm.luaT_callorderTM(L, l, r, ltm.TMS.TM_LT);
if (res < 0)
throw new Error("TM order error"); // TODO: luaG_ordererror
return res;
@@ -676,13 +675,13 @@ const luaV_lessequal = function(L, l, r) {
else if (l.ttisstring() && r.ttisstring())
return l_strcmp(l, r) <= 0;
else {
- res = ltm.luaT_callorderTM(L, l, r, TMS.TM_LE);
+ res = ltm.luaT_callorderTM(L, l, r, ltm.TMS.TM_LE);
if (res >= 0)
return res;
}
L.ci.callstatus |= lstate.CIST_LEQ; /* mark it is doing 'lt' for 'le' */
- res = ltm.luaT_callorderTM(L, l, r, TMS.TM_LT);
+ res = ltm.luaT_callorderTM(L, l, r, ltm.TMS.TM_LT);
L.ci.callstatus ^= lstate.CIST_LEQ; /* clear mark */
if (res < 0)
throw new Error("TM order error"); // TODO: luaG_ordererror
@@ -719,9 +718,9 @@ const luaV_equalobj = function(L, t1, t2) {
else if (L === null) return 0;
// TODO: fasttm ?
- tm = ltm.luaT_gettmbyobj(L, t1, TMS.TM_EQ);
+ tm = ltm.luaT_gettmbyobj(L, t1, ltm.TMS.TM_EQ);
if (tm.ttisnil())
- tm = ltm.luaT_gettmbyobj(L, t2, TMS.TM_EQ);
+ tm = ltm.luaT_gettmbyobj(L, t2, ltm.TMS.TM_EQ);
break;
default:
return t1.value === t2.value ? 1 : 0;
@@ -854,7 +853,7 @@ const luaV_objlen = function(L, ra, rb) {
let tm;
switch(rb.ttype()) {
case CT.LUA_TTABLE: {
- tm = ltm.luaT_gettmbyobj(L, rb, TMS.TM_LEN);
+ tm = ltm.luaT_gettmbyobj(L, rb, ltm.TMS.TM_LEN);
if (!tm.ttisnil()) break;
L.stack[ra] = rb.luaH_getn();
return;
@@ -864,7 +863,7 @@ const luaV_objlen = function(L, ra, rb) {
L.stack[ra] = rb.value.length; // TODO: 8-byte clean string
return;
default: {
- tm = ltm.luaT_gettmbyobj(L, rb, TMS.TM_LEN);
+ tm = ltm.luaT_gettmbyobj(L, rb, ltm.TMS.TM_LEN);
if (tm.ttisnil())
ldebug.luaG_typeerror(L, rb, "get length of");
break;
@@ -899,7 +898,7 @@ const luaV_concat = function(L, total) {
let v2 = L.stack[top-1];
if (!(v.ttisstring() || v.ttisnumber()) || !tostring(L, top - 2)) // TODO: tostring
- ltm.luaT_trybinTM(L, v, v2, top-2, TMS.TM_CONCAT);
+ ltm.luaT_trybinTM(L, v, v2, top-2, ltm.TMS.TM_CONCAT);
else if (v2.ttisstring() && v2.value.length === 0)
tostring(L, top - 2)
else if (v.ttisstring() && v.value.length === 0)
@@ -951,12 +950,12 @@ const luaV_finishget = function(L, t, key, val, slot, recur) {
let tm;
if (slot === null) { /* 't' is not a table? */
assert(!t.ttistable());
- tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_INDEX);
+ tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_INDEX);
if (tm.ttisnil())
ldebug.luaG_typeerror(L, t, 'index');
} else { /* 't' is a table */
assert(slot.ttisnil());
- tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_INDEX); // TODO: fasttm
+ tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_INDEX); // TODO: fasttm
if (tm.ttisnil()) {
L.stack[val] = ldo.nil;
return;
@@ -994,13 +993,13 @@ const luaV_finishset = function(L, t, key, val, slot, recur) {
let tm;
if (slot !== null) { /* is 't' a table? */
assert(slot.ttisnil());
- tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_NEWINDEX); // TODO: fasttm
+ tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_NEWINDEX); // TODO: fasttm
if (tm.ttisnil()) {
t.__newindex(t, key, val);
return;
}
} else { /* not a table; check metamethod */
- tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_NEWINDEX);
+ tm = ltm.luaT_gettmbyobj(L, t, ltm.TMS.TM_NEWINDEX);
if (tm.ttisnil())
ldebug.luaG_typeerror(L, t, 'index');
}
diff --git a/tests/ldebug.js b/tests/ldebug.js
index b627cf7..491b8ba 100644
--- a/tests/ldebug.js
+++ b/tests/ldebug.js
@@ -129,4 +129,88 @@ test('luaG_typeerror', function (t) {
lapi.lua_tostring(L, -1).endsWith("attempt to index a boolean value (local 'a')"),
"Correct error was thrown"
)
+});
+
+
+test('luaG_concaterror', function (t) {
+ let luaCode = `
+ return {} .. 'hello'
+ `, L;
+
+ t.plan(2);
+
+ t.doesNotThrow(function () {
+
+ let bc = toByteCode(luaCode).dataView;
+
+ L = lauxlib.luaL_newstate();
+
+ linit.luaL_openlibs(L);
+
+ lapi.lua_load(L, bc, "test-typeerror");
+
+ lapi.lua_pcall(L, 0, -1, 0);
+
+ }, "JS Lua program ran without error");
+
+ t.ok(
+ lapi.lua_tostring(L, -1).endsWith("attempt to concatenate a table value"),
+ "Correct error was thrown"
+ )
+});
+
+
+test('luaG_opinterror', function (t) {
+ let luaCode = `
+ return {} + 'hello'
+ `, L;
+
+ t.plan(2);
+
+ t.doesNotThrow(function () {
+
+ let bc = toByteCode(luaCode).dataView;
+
+ L = lauxlib.luaL_newstate();
+
+ linit.luaL_openlibs(L);
+
+ lapi.lua_load(L, bc, "test-typeerror");
+
+ lapi.lua_pcall(L, 0, -1, 0);
+
+ }, "JS Lua program ran without error");
+
+ t.ok(
+ lapi.lua_tostring(L, -1).endsWith("attempt to perform arithmetic on a string value"),
+ "Correct error was thrown"
+ )
+});
+
+
+test('luaG_tointerror', function (t) {
+ let luaCode = `
+ return 123.5 & 12
+ `, L;
+
+ t.plan(2);
+
+ t.doesNotThrow(function () {
+
+ let bc = toByteCode(luaCode).dataView;
+
+ L = lauxlib.luaL_newstate();
+
+ linit.luaL_openlibs(L);
+
+ lapi.lua_load(L, bc, "test-typeerror");
+
+ lapi.lua_pcall(L, 0, -1, 0);
+
+ }, "JS Lua program ran without error");
+
+ t.ok(
+ lapi.lua_tostring(L, -1).endsWith("number has no integer representation"),
+ "Correct error was thrown"
+ )
}); \ No newline at end of file