fengari-lua / fengari

🌙 φεγγάρι - The Lua VM written in JS ES6 for Node and the browser
MIT License
1.81k stars 64 forks source link

pcall doesn't provide error message #141

Closed FizzerWL closed 6 years ago

FizzerWL commented 6 years ago

Hello!

When I lua_pcall into code which calls a cfunction that throws, pcall seems to return -1 and not push the error message to the stack. Is this intended? Is there a way to get the error message?

const luaconf = fengari.luaconf;
const lua = fengari.lua;
const lauxlib = fengari.lauxlib;
const lualib = fengari.lualib;

const L = lauxlib.luaL_newstate();

lualib.luaL_openlibs(L);

//Make a javascript function that throws, put it in globals.
lua.lua_pushcfunction(L, function (l) { throw "thrown_error"; });
lua.lua_setglobal(L, "throwfn");

//pcall a function that calls the global
lauxlib.luaL_loadstring(L, fengari.to_luastring("throwfn()"));
var ret = lua.lua_pcall(L, 0, 0, 0);
console.log("ret = " + ret); //Why does this return -1? How can I get the error message "thrown_error"?

if (ret == lua.LUA_ERRRUN || ret == lua.LUA_ERRERR) {
    //Error pushed to the stack.  We don't get here in this example, why not?
    console.log("Error message: " + fengari.to_jsstring(lua.lua_tostring(L, -1)));
    lua.lua_pop(L, 1);
}

I also tried passing an error function to the third pcall parameter, but it was not called in this case.

Is this a bug, or am I doing something wrong?

Thank you!!

giann commented 6 years ago

The -1 is normal, it's the status of the function call when a js error happens. What does your last console log shows ?

FizzerWL commented 6 years ago

There is no console output. All this sample prints in Chrome is "ret = -1"

giann commented 6 years ago

can you try fengari.to_jsstring(lua.lua_tostring(L, -1)) outside your if at the end ?

daurnimator commented 6 years ago

See lua_atnativeerror

FizzerWL commented 6 years ago

@giann Unfortunately that doesn't work, as pcall isn't pushing anything to the stack.

@daurnimator Ahh, I wasn't aware of that function so thanks for the note! I can use this to handle javascript errors, and store the error in a global to be accessed after pcall exits. I'm not sure if this is the best way of handling it as it seems rather hacky, but it works:

var lastNativeError = null;
lua.lua_atnativeerror(L, function (l) {
    lastNativeError = lua.lua_touserdata(l, -1);
});

//Make a javascript function that throws, put it in globals.
lua.lua_pushcfunction(L, function (l) { throw "thrown_error"; });
lua.lua_setglobal(L, "throwfn");

//Call a function that calls the global
lauxlib.luaL_loadstring(L, fengari.to_luastring("throwfn()"));
var ret = lua.lua_pcall(L, 0, 0, 0);

if (ret == -1 && lastNativeError) {
    console.log("Error message: " + lastNativeError);
    _lastNativeError = null;
}
else if (ret == lua.LUA_ERRRUN || ret == lua.LUA_ERRERR) {
    //Error pushed to the stack.  
    console.log("Error message: " + fengari.to_jsstring(lua.lua_tostring(L, -1)));
    lua.lua_pop(L, 1);
}

I appreciate all the help!

daurnimator commented 6 years ago

Normally an atnativeerror handler will put the error onto the stack somehow. FWIW fengari-interop comes with an atnativeerror handler.

FizzerWL commented 6 years ago

Ahh OK, so a more correct way would be something like this?

lua.lua_atnativeerror(L, function (l) {
    lua.lua_pushstring(l, fengari.to_luastring(lua.lua_touserdata(l, -1)));
});

//Make a javascript function that throws, put it in globals.
lua.lua_pushcfunction(L, function (l) { throw "thrown_error"; });
lua.lua_setglobal(L, "throwfn");

//Call a function that calls the global
lauxlib.luaL_loadstring(L, fengari.to_luastring("throwfn()"));
var ret = lua.lua_pcall(L, 0, 0, 0);

if ((ret == -1 || ret == lua.LUA_ERRRUN || ret == lua.LUA_ERRERR) && lua.lua_type(L, -1) == 4) {
    //Error pushed to the stack
    console.log("Error message: " + fengari.to_jsstring(lua.lua_tostring(L, -1)));
    lua.lua_pop(L, 1);
}
daurnimator commented 6 years ago

Yep. I might write it like:

lua.lua_atnativeerror(L, function (l) {
    lua.lua_pushstring(l, fengari.to_luastring(''+lua.lua_touserdata(l, -1)));
});

//Make a javascript function that throws
lua.lua_pushcfunction(L, function (l) { throw "thrown_error"; });

//Call the function
if (lua.lua_pcall(L, 0, 0, 0) != lua.LUA_OK) {
    //Error pushed to the stack; print it
    console.log("Error message: " + fengari.to_jsstring(lauxlib.luaL_tolstring(L, -1)));
    lua.lua_pop(L, 2);
}