From e111f5cb7a21455df2c62eb65cd7b38aac0d834c Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 21 Feb 2017 09:55:56 +0100 Subject: luaG_concaterror, luaG_opinterror, luaG_tointerror --- src/ldebug.js | 56 ++++++++++++++++++++++---------------- src/ldo.js | 3 +-- src/ltm.js | 18 ++++++++++++- src/lvm.js | 53 ++++++++++++++++++------------------ tests/ldebug.js | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 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 -- cgit v1.2.3-70-g09d2