aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-21 15:24:37 +0100
committerBenoit Giannangeli <benoit.giannangeli@boursorama.fr>2017-02-21 15:24:37 +0100
commit653f4a07af6506e0b8ef71e8957976b9559f67e4 (patch)
tree7bf59628c6e68bb33fa278d24f9e07f0104995ad
parent82ef443de9a3ba53b4a5b9abd161ddc164776a59 (diff)
downloadfengari-653f4a07af6506e0b8ef71e8957976b9559f67e4.tar.gz
fengari-653f4a07af6506e0b8ef71e8957976b9559f67e4.tar.bz2
fengari-653f4a07af6506e0b8ef71e8957976b9559f67e4.zip
pcall, xpcall
-rw-r--r--README.md4
-rw-r--r--src/lapi.js42
-rw-r--r--src/lbaselib.js44
-rw-r--r--tests/lbaselib.js70
4 files changed, 151 insertions, 9 deletions
diff --git a/README.md b/README.md
index dd2ba4f..a7d0124 100644
--- a/README.md
+++ b/README.md
@@ -74,6 +74,7 @@
- [x] lua_istable
- [x] lua_remove
- [x] lua_rotate
+ - [x] lua_insert
- [ ] lua_arith
- [ ] lua_close
- [ ] lua_compare
@@ -91,7 +92,6 @@
- [ ] lua_getstack
- [ ] lua_getupvalue
- [ ] lua_getuservalue
- - [ ] lua_insert
- [ ] lua_isboolean
- [ ] lua_iscfunction
- [ ] lua_isfunction
@@ -209,6 +209,7 @@
- [x] rawget
- [x] type
- [x] error
+ - [x] pcall
- [ ] assert
- [ ] collectgarbage
- [ ] dofile
@@ -218,7 +219,6 @@
- [ ] loadstring
- [ ] next
- [ ] pairs
- - [ ] pcall
- [ ] rawlen
- [ ] select
- [ ] tonumber
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 <f, err, true, f, [args...]>; 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) {
diff --git a/tests/lbaselib.js b/tests/lbaselib.js
index b55afde..cca3ebe 100644
--- a/tests/lbaselib.js
+++ b/tests/lbaselib.js
@@ -290,4 +290,74 @@ test('error, protected', function (t) {
lapi.lua_tostring(L, -1).endsWith("you fucked up"),
"Error is on the stack"
)
+});
+
+
+test('pcall', function (t) {
+ let luaCode = `
+ local willFail = function ()
+ error("you fucked up")
+ end
+
+ return pcall(willFail)
+ `, 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-pcall");
+
+ lapi.lua_call(L, 0, -1);
+
+ }, "JS Lua program ran without error");
+
+ t.ok(
+ lapi.lua_tostring(L, -1).endsWith("you fucked up"),
+ "Error is on the stack"
+ )
+});
+
+
+test('xpcall', function (t) {
+ let luaCode = `
+ local willFail = function ()
+ error("you fucked up")
+ end
+
+ local msgh = function (err)
+ return "Something's wrong: " .. err
+ end
+
+ return xpcall(willFail, msgh)
+ `, 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-pcall");
+
+ lapi.lua_call(L, 0, -1);
+
+ }, "JS Lua program ran without error");
+
+ console.log(lapi.lua_tostring(L, -1));
+
+ // t.ok(
+ // lapi.lua_tostring(L, -1).endsWith("you fucked up"),
+ // "Error is on the stack"
+ // )
}); \ No newline at end of file