From 4a0aa6eecf10432453c22031c247cf24819f1040 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Fri, 12 May 2017 14:52:14 +1000 Subject: Add facility for a user provided (state-global) native error handler --- README.md | 6 ++++++ src/lapi.js | 7 +++++++ src/ldo.js | 26 ++++++++++++++++++++------ src/lstate.js | 1 + src/lua.js | 1 + tests/lapi.js | 40 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 75 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a41b94b..0fbfc4e 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,12 @@ Alias for `lua_pushcfunction`. Alias for `lua_pushcclosure`. +### `lua_atnativeerror(L, func)` + +Sets a function to be called if a native JavaScript error is thrown across a lua pcall. +The function will be run as if it were a message handler (see https://www.lua.org/manual/5.3/manual.html#2.3). + + ### `b = lua_isproxy(p, L)` Returns a boolean `b` indicating whether `p` is a proxy (See `lua_toproxy`). diff --git a/src/lapi.js b/src/lapi.js index f56fc81..c0e619b 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -36,6 +36,12 @@ const lua_atpanic = function(L, panicf) { return old; }; +const lua_atnativeerror = function(L, errorf) { + let old = L.l_G.atnativeerror; + L.l_G.atnativeerror = errorf; + return old; +}; + // Return value for idx on stack const index2addr = function(L, idx) { let ci = L.ci; @@ -1104,6 +1110,7 @@ module.exports.index2addr_ = index2addr_; module.exports.lua_absindex = lua_absindex; module.exports.lua_arith = lua_arith; module.exports.lua_atpanic = lua_atpanic; +module.exports.lua_atnativeerror = lua_atnativeerror; module.exports.lua_call = lua_call; module.exports.lua_callk = lua_callk; module.exports.lua_checkstack = lua_checkstack; diff --git a/src/ldo.js b/src/ldo.js index bb34216..c4619e5 100644 --- a/src/ldo.js +++ b/src/ldo.js @@ -273,12 +273,26 @@ const luaD_rawrunprotected = function(L, f, ud) { } catch (e) { if (lj.status === TS.LUA_OK) { /* error was not thrown via luaD_throw, i.e. it is a JS error */ - lj.status = -1; - /* run error handler (if possible) with error object as light userdata */ - try { - lapi.lua_pushlightuserdata(L, e); - ldebug.luaG_errormsg(L); - } catch (e) {} + /* run user error handler (if it exists) */ + let atnativeerror = L.l_G.atnativeerror; + if (atnativeerror) { + try { + lj.status = TS.LUA_OK; + + lapi.lua_pushcfunction(L, atnativeerror); + lapi.lua_pushlightuserdata(L, e); + luaD_callnoyield(L, L.top - 2, 1); + + lj.status = TS.LUA_ERRRUN; + } catch(e2) { + if (lj.status === TS.LUA_OK) { + /* also failed */ + lj.status = -1; + } + } + } else { + lj.status = -1; + } } } diff --git a/src/lstate.js b/src/lstate.js index 7d7ff80..5a65c12 100644 --- a/src/lstate.js +++ b/src/lstate.js @@ -66,6 +66,7 @@ class global_State { this.mainthread = L; this.l_registry = new lobject.TValue(CT.LUA_TNIL, null); this.panic = null; + this.atnativeerror = null; this.version = null; this.tmname = new Array(ltm.TMS.TM_N); this.mt = new Array(LUA_NUMTAGS); diff --git a/src/lua.js b/src/lua.js index 135dadb..94eff3d 100644 --- a/src/lua.js +++ b/src/lua.js @@ -95,6 +95,7 @@ module.exports.LUA_DIRSEP = defs.LUA_DIRSEP; module.exports.lua_absindex = lapi.lua_absindex; module.exports.lua_arith = lapi.lua_arith; module.exports.lua_atpanic = lapi.lua_atpanic; +module.exports.lua_atnativeerror = lapi.lua_atnativeerror; module.exports.lua_call = lapi.lua_call; module.exports.lua_callk = lapi.lua_callk; module.exports.lua_checkstack = lapi.lua_checkstack; diff --git a/tests/lapi.js b/tests/lapi.js index b739860..1dbdd5c 100644 --- a/tests/lapi.js +++ b/tests/lapi.js @@ -481,3 +481,43 @@ test('lua_settable, lua_gettable', function (t) { "Correct element(s) on the stack" ); }); + +test('lua_atnativeerror', function(t) { + t.plan(7); + + let L = lauxlib.luaL_newstate(); + let errob = {}; + + lua.lua_pushcfunction(L, function(L) { + throw errob; + }); + t.strictEqual(lua.lua_pcall(L, 0, 0, 0), -1, "without a native error handler pcall should be -1"); + lua.lua_pop(L, 1); + + + lua.lua_atnativeerror(L, function(L) { + let e = lua.lua_touserdata(L, 1); + t.strictEqual(e, errob); + lua.lua_pushstring(L, lua.to_luastring("runtime error!")); + return 1; + }); + lua.lua_pushcfunction(L, function(L) { + throw errob; + }); + t.strictEqual(lua.lua_pcall(L, 0, 0, 0), lua.LUA_ERRRUN, "should return lua.LUA_ERRRUN"); + t.strictEqual(lua.lua_tojsstring(L, -1), "runtime error!"); + lua.lua_pop(L, 1); + + + lua.lua_atnativeerror(L, function(L) { + let e = lua.lua_touserdata(L, 1); + t.strictEqual(e, errob); + lauxlib.luaL_error(L, lua.to_luastring("runtime error!")); + }); + lua.lua_pushcfunction(L, function(L) { + throw errob; + }); + t.strictEqual(lua.lua_pcall(L, 0, 0, 0), lua.LUA_ERRRUN, "luaL_error from a native error handler should result in LUA_ERRRUN"); + t.strictEqual(lua.lua_tojsstring(L, -1), "runtime error!"); + lua.lua_pop(L, 1); +}); -- cgit v1.2.3-54-g00ecf