aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lauxlib.js34
-rw-r--r--src/ldebug.js101
-rw-r--r--src/lfunc.js29
-rw-r--r--src/ltm.js16
-rw-r--r--src/lvm.js7
-rw-r--r--tests/ldebug.js42
6 files changed, 215 insertions, 14 deletions
diff --git a/src/lauxlib.js b/src/lauxlib.js
index 16b480d..b879446 100644
--- a/src/lauxlib.js
+++ b/src/lauxlib.js
@@ -17,6 +17,26 @@ const panic = function(L) {
throw new Error(msg);
};
+// const luaL_argerror = function(L, arg, extramsg) {
+// let ar = new lua.lua_Debug();
+//
+// if (!lapi.lua_getstack(L, 0, ar)) /* no stack frame? */
+// return luaL_error(L, 'bad argument #%d (%s)', arg, extramsg);
+//
+// ldebug.lua_getinfo(L, 'n', ar);
+//
+// if (ar.namewhat === 'method') {
+// arg--; /* do not count 'self' */
+// if (arg === 0) /* error is in the self argument itself? */
+// return luaL_error(L, "calling '%s' on bad self (%s)", ar.name, extramsg);
+// }
+//
+// if (ar.name === null)
+// ar.name = pushglobalfuncname(L, ar) ? lapi.lua_tostring(L, -1) : "?";
+//
+// return luaL_error(L, "bad argument #%d to '%s' (%s)", arg, ar.name, extramsg);
+// };
+
const typeerror = function(L, arg, tname) {
let typearg;
if (luaL_getmetafield(L, arg, "__name") === CT.LUA_TSTRING)
@@ -45,6 +65,17 @@ const luaL_where = function(L, level) {
lapi.lua_pushstring(L, "");
};
+const luaL_error = function(L, fmt, ...args) {
+ let i = 0;
+
+ // TODO: bypassing lua_pushvstring for now
+ lapi.lua_pushstring(L, fmt.replace(/(^%[sfIpdcU]|([^%])%[sfIpdcU])/g, function (m, p1, p2, off) {
+ return p2 ? p2 + args[i++] : args[i++];
+ }));
+
+ return lapi.lua_error(L);
+};
+
const tag_error = function(L, arg, tag) {
typeerror(L, arg, lapi.lua_typename(L, tag));
};
@@ -252,4 +283,5 @@ module.exports.luaL_optstring = luaL_optstring;
module.exports.luaL_checkinteger = luaL_checkinteger;
module.exports.luaL_optinteger = luaL_optinteger;
module.exports.luaL_opt = luaL_opt;
-module.exports.luaL_where = luaL_where; \ No newline at end of file
+module.exports.luaL_where = luaL_where;
+module.exports.luaL_error = luaL_error; \ No newline at end of file
diff --git a/src/ldebug.js b/src/ldebug.js
index c6b6752..ecd612b 100644
--- a/src/ldebug.js
+++ b/src/ldebug.js
@@ -9,6 +9,7 @@ const lobject = require('./lobject.js');
const lstate = require('./lstate.js');
const luaconf = require('./luaconf.js');
const OC = require('./lopcodes.js');
+const lvm = require('./lvm.js');
const ltm = require('./ltm.js');
const lfunc = require('./lfunc.js');
const TMS = ltm.TMS;
@@ -254,7 +255,7 @@ const findsetreg = function(p, lastpc, reg) {
}
default:
if (OC.testAMode(i.opcode) && reg === a)
- setreg= filterpc(pc, jmptarget);
+ setreg = filterpc(pc, jmptarget);
break;
}
}
@@ -392,6 +393,92 @@ const funcnamefromcode = function(L, ci) {
return r;
};
+const isinstack = function(L, ci, o) {
+ for (let i = ci.u.l.base; i < ci.top; i++) {
+ if (L.stack[i] === o)
+ return i;
+ }
+
+ return false;
+}
+
+/*
+** Checks whether value 'o' came from an upvalue. (That can only happen
+** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on
+** upvalues.)
+*/
+const getupvalname = function(L, ci, o, name) {
+ let c = ci.func;
+ for (let i = 0; i < c.nupvalues; i++) {
+ if (c.upvals[i].val(L) === o) {
+ return {
+ name: upvalname(c.p, i),
+ funcname: 'upvalue'
+ }
+ }
+ }
+
+ return null;
+};
+
+const varinfo = function(L, o) {
+ let ci = L.ci;
+ let kind = null;
+ if (ci.callstatus & lstate.CIST_LUA) {
+ kind = getupvalname(L, ci, o); /* check whether 'o' is an upvalue */
+ let stkid = isinstack(L, ci, o);
+ if (!kind && stkid) /* no? try a register */
+ kind = getobjname(ci.func.p, ci.pcOff, stkid);
+ }
+
+ return kind ? ` (${kind.funcname} '${kind.name}')` : ``;
+};
+
+const luaG_typeerror = function(L, o, op) {
+ let t = ltm.luaT_objtypename(L, o);
+ luaG_runerror(L, `attempt to ${op} a ${t} value${varinfo(L, o)}`);
+};
+
+const luaG_concaterror = function(L, p1, p2) {
+ if (p1.ttisstring() || p1.ttisnumber()) p1 = p2;
+ luaG_typeerror(L, p1, 'concatenate');
+};
+
+/*
+** Error when both values are convertible to numbers, but not to integers
+*/
+const luaG_opinterror = function(L, p1, p2, msg) {
+ let temp = lvm.tonumber(p1);
+ if (temp !== false)
+ p2 = p1;
+ luaG_typeerror(L, p2, msg);
+};
+
+const luaG_ordererror = function(L, p1, p2) {
+ let t1 = ltm.luaT_objtypename(L, p1);
+ let t2 = ltm.luaT_objtypename(L, p2);
+ if (t1 === t2)
+ luaG_runerror(L, `attempt to compare two ${t1} values`);
+ else
+ luaG_runerror(L, `attempt to compare ${t1} with ${t2}`);
+};
+
+/* add src:line information to 'msg' */
+const luaG_addinfo = function(L, msg, src, line) {
+ let buff = '?';
+ if (src)
+ buff = lobject.luaO_chunkid(src, luaconf.LUA_IDSIZE);
+
+ return `${buff}:${line}: ${msg}`;
+};
+
+const luaG_runerror = function(L, msg) {
+ let ci = L.ci;
+ if (ci.callstatus & lstate.CIST_LUA) /* if Lua function, add source:line information */
+ luaG_addinfo(L, msg, ci.func.p.source, currentline(ci));
+ luaG_errormsg(L);
+};
+
const luaG_errormsg = function(L) {
if (L.errfunc !== 0) { /* is there an error handling function? */
let errfunc = L.errfunc;
@@ -404,6 +491,12 @@ const luaG_errormsg = function(L) {
ldo.luaD_throw(L, TS.LUA_ERRRUN);
};
-module.exports.lua_getstack = lua_getstack;
-module.exports.lua_getinfo = lua_getinfo;
-module.exports.luaG_errormsg = luaG_errormsg; \ No newline at end of file
+module.exports.lua_getstack = lua_getstack;
+module.exports.lua_getinfo = lua_getinfo;
+module.exports.luaG_errormsg = luaG_errormsg;
+module.exports.luaG_addinfo = luaG_addinfo;
+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
diff --git a/src/lfunc.js b/src/lfunc.js
index c309147..7057f2d 100644
--- a/src/lfunc.js
+++ b/src/lfunc.js
@@ -99,9 +99,26 @@ const luaF_initupvals = function(L, cl) {
}
};
-module.exports.Proto = Proto;
-module.exports.UpVal = UpVal;
-module.exports.findupval = findupval;
-module.exports.luaF_close = luaF_close;
-module.exports.MAXUPVAL = 255;
-module.exports.luaF_initupvals = luaF_initupvals; \ No newline at end of file
+/*
+** Look for n-th local variable at line 'line' in function 'func'.
+** Returns null if not found.
+*/
+const luaF_getlocalname = function(f, local_number, pc) {
+ for (let i = 0; i < f.locvars.length && f.locvars[i].startpc <= pc; i++) {
+ if (pc < f.locvars[i].endpc) { /* is variable active? */
+ local_number--;
+ if (local_number == 0)
+ return f.locvars[i].varname;
+ }
+ }
+ return null; /* not found */
+}
+
+
+module.exports.Proto = Proto;
+module.exports.UpVal = UpVal;
+module.exports.findupval = findupval;
+module.exports.luaF_close = luaF_close;
+module.exports.MAXUPVAL = 255;
+module.exports.luaF_initupvals = luaF_initupvals;
+module.exports.luaF_getlocalname = luaF_getlocalname \ No newline at end of file
diff --git a/src/ltm.js b/src/ltm.js
index 77d65e4..f163f44 100644
--- a/src/ltm.js
+++ b/src/ltm.js
@@ -65,6 +65,21 @@ const luaT_init = function(L) {
}
};
+/*
+** Return the name of the type of an object. For tables and userdata
+** with metatable, use their '__name' metafield, if present.
+*/
+const luaT_objtypename = function(L, o) {
+ if ((o.ttistable() && o.metatable !== null)
+ || (o.ttisfulluserdata() && o.metatable !== null)) {
+ let name = o.__index(o, '__name');
+ if (name.ttisstring())
+ return name.value;
+ }
+
+ return ttypename(o.ttnov());
+};
+
const luaT_callTM = function(L, f, p1, p2, p3, hasres) {
let result = p3;
let func = L.top;
@@ -131,4 +146,5 @@ module.exports.luaT_trybinTM = luaT_trybinTM;
module.exports.luaT_callorderTM = luaT_callorderTM;
module.exports.luaT_gettmbyobj = luaT_gettmbyobj;
module.exports.luaT_init = luaT_init;
+module.exports.luaT_objtypename = luaT_objtypename;
module.exports.ttypename = ttypename; \ No newline at end of file
diff --git a/src/lvm.js b/src/lvm.js
index 6bbe997..97292cd 100644
--- a/src/lvm.js
+++ b/src/lvm.js
@@ -20,6 +20,7 @@ const llimit = require('./llimit.js');
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) {
@@ -865,7 +866,7 @@ const luaV_objlen = function(L, ra, rb) {
default: {
tm = ltm.luaT_gettmbyobj(L, rb, TMS.TM_LEN);
if (tm.ttisnil())
- throw new Error("attempt to get length"); // TODO: luaG_typeerror
+ ldebug.luaG_typeerror(L, rb, "get length of");
break;
}
}
@@ -952,7 +953,7 @@ const luaV_finishget = function(L, t, key, val, slot, recur) {
assert(!t.ttistable());
tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_INDEX);
if (tm.ttisnil())
- throw new Error(`attempt to index a ${tm.ttype()} value`); // TODO: luaG_typeerror
+ 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
@@ -1001,7 +1002,7 @@ const luaV_finishset = function(L, t, key, val, slot, recur) {
} else { /* not a table; check metamethod */
tm = ltm.luaT_gettmbyobj(L, t, TMS.TM_NEWINDEX);
if (tm.ttisnil())
- throw new Error(`attempt to index a ${tm.ttype()} value`); // TODO: luaG_typeerror
+ ldebug.luaG_typeerror(L, t, 'index');
}
if (tm.ttisfunction()) {
diff --git a/tests/ldebug.js b/tests/ldebug.js
new file mode 100644
index 0000000..a6fa55f
--- /dev/null
+++ b/tests/ldebug.js
@@ -0,0 +1,42 @@
+/*jshint esversion: 6 */
+"use strict";
+
+const test = require('tape');
+const beautify = require('js-beautify').js_beautify;
+
+const tests = require("./tests.js");
+const getState = tests.getState;
+const toByteCode = tests.toByteCode;
+
+const lvm = require("../src/lvm.js");
+const ldo = require("../src/ldo.js");
+const lapi = require("../src/lapi.js");
+const lauxlib = require("../src/lauxlib.js");
+const lua = require('../src/lua.js');
+const linit = require('../src/linit.js');
+
+test('luaG_typeerror', function (t) {
+ let luaCode = `
+ local a = true
+ return #a
+ `, L;
+
+ t.plan(1);
+
+ 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");
+
+
+ console.log(lapi.lua_tostring(L, -1));
+}); \ No newline at end of file