From 653f4a07af6506e0b8ef71e8957976b9559f67e4 Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Tue, 21 Feb 2017 15:24:37 +0100 Subject: pcall, xpcall --- src/lapi.js | 42 ++++++++++++++++++++++++++++++++++++------ src/lbaselib.js | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/lapi.js b/src/lapi.js index 907d2b1..9059ee9 100644 --- a/src/lapi.js +++ b/src/lapi.js @@ -30,7 +30,7 @@ const lua_atpanic = function(L, panicf) { return old; }; -// Return real index on stack +// Return value for idx on stack const index2addr = function(L, idx) { let ci = L.ci; if (idx > 0) { @@ -54,6 +54,30 @@ const index2addr = function(L, idx) { } }; +// Like index2addr but returns the index on stack +const index2addr_ = function(L, idx) { + let ci = L.ci; + if (idx > 0) { + let o = ci.funcOff + idx; + assert(idx <= ci.top - (ci.funcOff + 1), "unacceptable index"); + if (o >= L.top) return null; + else return o; + } else if (idx > lua.LUA_REGISTRYINDEX) { + assert(idx !== 0 && -idx <= L.top, "invalid index"); + return L.top + idx; + } else if (idx === lua.LUA_REGISTRYINDEX) { + return null; + } else { /* upvalues */ + idx = lua.LUA_REGISTRYINDEX - idx; + assert(idx <= MAXUPVAL + 1, "upvalue index too large"); + if (ci.func.ttislcf()) /* light C function? */ + return null; /* it has no upvalues */ + else { + return idx <= ci.func.nupvalues ? idx - 1 : null; + } + } +}; + const lua_checkstack = function(L, n) { return L.stack.length < luaconf.LUAI_MAXSTACK; }; @@ -99,7 +123,7 @@ const lua_pop = function(L, n) { } const reverse = function(L, from, to) { - for (; from < to; from++, to --) { + for (; from < to; from++, to--) { let temp = L.stack[from]; L.stack[from] = L.stack[to]; L.stack[to] = temp; @@ -113,15 +137,16 @@ const reverse = function(L, from, to) { const lua_rotate = function(L, idx, n) { let t = L.stack[L.top - 1]; let p = index2addr(L, idx); + let pIdx = index2addr_(L, idx); assert(!p.ttisnil() && idx > lua.LUA_REGISTRYINDEX, "index not in the stack"); assert((n >= 0 ? n : -n) <= (L.top - idx), "invalid 'n'"); - let m = n >= 0 ? L.top - 1 - n : L.top + idx - n - 1; /* end of prefix */ + let m = n >= 0 ? L.top - 1 - n : pIdx - n - 1; /* end of prefix */ - reverse(L, L.top + idx, m); + reverse(L, pIdx, m); reverse(L, m + 1, L.top - 1); - reverse(L, L.top + idx, L.top - 1); + reverse(L, pIdx, L.top - 1); }; const lua_remove = function(L, idx) { @@ -129,6 +154,10 @@ const lua_remove = function(L, idx) { lua_pop(L, 1); }; +const lua_insert = function(L, idx) { + lua_rotate(L, idx, 1); +}; + /* ** push functions (JS -> stack) */ @@ -629,4 +658,5 @@ module.exports.lua_setmetatable = lua_setmetatable; module.exports.lua_settop = lua_settop; module.exports.lua_rawequal = lua_rawequal; module.exports.lua_concat = lua_concat; -module.exports.lua_error = lua_error; \ No newline at end of file +module.exports.lua_error = lua_error; +module.exports.lua_insert = lua_insert; \ No newline at end of file diff --git a/src/lbaselib.js b/src/lbaselib.js index 205d57a..ac0ac4c 100644 --- a/src/lbaselib.js +++ b/src/lbaselib.js @@ -7,6 +7,7 @@ const lua = require('./lua.js'); const lapi = require('./lapi.js'); const lauxlib = require('./lauxlib.js'); const CT = lua.constant_types; +const TS = lua.thread_status; const luaB_print = function(L) { let n = lapi.lua_gettop(L); /* number of arguments */ @@ -99,6 +100,45 @@ const luaB_error = function(L) { return lapi.lua_error(L); }; +/* +** Continuation function for 'pcall' and 'xpcall'. Both functions +** already pushed a 'true' before doing the call, so in case of success +** 'finishpcall' only has to return everything in the stack minus +** 'extra' values (where 'extra' is exactly the number of items to be +** ignored). +*/ +const finishpcall = function(L, status, extra) { + if (status !== TS.LUA_OK && status !== TS.LUA_YIELD) { /* error? */ + lapi.lua_pushboolean(L, 0); /* first result (false) */ + lapi.lua_pushvalue(L, -2); /* error message */ + return 2; /* return false, msg */ + } else + return lapi.lua_gettop(L) - extra; +}; + +const luaB_pcall = function(L) { + lauxlib.luaL_checkany(L, 1); + lapi.lua_pushboolean(L, 1); /* first result if no errors */ + lapi.lua_insert(L, 1); /* put it in place */ + let status = lapi.lua_pcallk(L, lapi.lua_gettop(L) - 2, lua.LUA_MULTRET, 0, 0, finishpcall); + return finishpcall(L, status, 0); +}; + +/* +** Do a protected call with error handling. After 'lua_rotate', the +** stack will have ; so, the function passes +** 2 to 'finishpcall' to skip the 2 first values when returning results. +*/ +const luaB_xpcall = function(L) { + let n = lapi.lua_gettop(L); + lauxlib.luaL_checktype(L, 2, CT.LUA_TFUNCTION); + lapi.lua_pushboolean(L, 1); + lapi.lua_pushvalue(L, 1); + lapi.lua_rotate(L, 3, 2); + let status = lapi.lua_pcallk(L, n - 2, lua.LUA_MULTRET, 2, 2, finishpcall); + return finishpcall(L, status, 2); +}; + const base_funcs = { "collectgarbage": function () {}, "print": luaB_print, @@ -109,7 +149,9 @@ const base_funcs = { "rawset": luaB_rawset, "rawget": luaB_rawget, "type": luaB_type, - "error": luaB_error + "error": luaB_error, + "pcall": luaB_pcall, + "xpcall": luaB_xpcall, }; const luaopen_base = function(L) { -- cgit v1.2.3-70-g09d2