Skip to content

Commit e825af5

Browse files
committed
feat: handle errors when calling js-functions from lua
1 parent ff0fabe commit e825af5

3 files changed

Lines changed: 56 additions & 32 deletions

File tree

binding.gyp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,20 @@
2222
"<@(lua_libraries)"
2323
],
2424
"dependencies": [
25-
"<!(node -p \"require('node-addon-api').targets\"):node_addon_api"
25+
"<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except"
2626
],
2727
"defines": [
2828
"NAPI_VERSION=8",
2929
"NDEBUG"
3030
],
31-
"cflags!": [
32-
"-fno-exceptions"
33-
],
34-
"cflags_cc!": [
35-
"-fno-exceptions"
36-
],
3731
"cflags+": [
3832
"-flto",
3933
"-fdata-sections",
4034
"-ffunction-sections",
4135
"-fvisibility=hidden"
4236
],
4337
"cflags_cc+": [
38+
"-fexceptions",
4439
"-flto",
4540
"-fdata-sections",
4641
"-ffunction-sections",

src/lua-state-context.cpp

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -610,33 +610,50 @@ namespace {
610610
return luaL_error(L, "Invalid js-function reference");
611611
}
612612

613-
Napi::Env env = js_function_holder->ref->Env();
614-
Napi::HandleScope scope(env);
615-
616-
// cast lua arguments to javascript
617-
int top_index = lua_gettop(L);
618-
619-
std::vector<napi_value> js_args;
620-
js_args.reserve(top_index - 1);
621-
for (int i = 2; i <= top_index; ++i) {
622-
auto js_arg = ReadJsValueFromStack(L, env, i);
623-
js_args.push_back(js_arg);
624-
}
613+
try {
614+
Napi::Env env = js_function_holder->ref->Env();
615+
Napi::HandleScope scope(env);
616+
617+
// cast lua arguments to javascript
618+
int top_index = lua_gettop(L);
619+
620+
std::vector<napi_value> js_args;
621+
js_args.reserve(top_index - 1);
622+
for (int i = 2; i <= top_index; ++i) {
623+
auto js_arg = ReadJsValueFromStack(L, env, i);
624+
js_args.push_back(js_arg);
625+
}
625626

626-
// call js function
627-
Napi::Value js_function_result = js_function_holder->ref->Call(js_args);
627+
// call js function
628+
Napi::Value js_function_result = js_function_holder->ref->Call(js_args);
628629

629-
if (js_function_result.IsArray()) {
630-
Napi::Array js_function_results = js_function_result.As<Napi::Array>();
631-
uint32_t js_function_results_length = js_function_results.Length();
632-
for (uint32_t i = 0; i < js_function_results_length; ++i) {
633-
Napi::Value result = js_function_results.Get(i);
634-
PushJsValueToStack(L, result);
630+
// return function call result to lua
631+
if (js_function_result.IsArray()) {
632+
Napi::Array js_function_results = js_function_result.As<Napi::Array>();
633+
uint32_t js_function_results_length = js_function_results.Length();
634+
for (uint32_t i = 0; i < js_function_results_length; ++i) {
635+
Napi::Value result = js_function_results.Get(i);
636+
PushJsValueToStack(L, result);
637+
}
638+
return js_function_results_length;
639+
} else {
640+
PushJsValueToStack(L, js_function_result);
641+
return 1;
635642
}
636-
return js_function_results_length;
637-
} else {
638-
PushJsValueToStack(L, js_function_result);
639-
return 1;
643+
} catch (const Napi::Error& e) {
644+
auto js_name = e.Get("name");
645+
std::string name;
646+
if (js_name.IsString()) {
647+
name = js_name.As<Napi::String>().Utf8Value();
648+
} else {
649+
name = "Error";
650+
}
651+
std::string msg = name + ": " + e.Message();
652+
return luaL_error(L, msg.c_str());
653+
} catch (const std::exception& e) {
654+
return luaL_error(L, e.what());
655+
} catch (...) {
656+
return luaL_error(L, "Unknown error from JS function");
640657
}
641658
}
642659

tests/lua-state.set-global.test.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ describe(`${LuaState.name}#${LuaState.prototype.setGlobal.name}`, () => {
109109
})
110110

111111
describe('with function', () => {
112-
it('should set the function', () => {
112+
it('should set function', () => {
113113
const func = mock.fn((num, str, bool, tbl) => {
114114
return { args: { num, str, bool, tbl } }
115115
})
@@ -141,5 +141,17 @@ describe(`${LuaState.name}#${LuaState.prototype.setGlobal.name}`, () => {
141141
luaState.eval(`r1, r2 = func()`)
142142
deepStrictEqual(luaState.eval(`return { r1, r2 }`), { 1: 1, 2: 'foo' })
143143
})
144+
145+
it('should set function with exception', () => {
146+
const err = new Error('hello world')
147+
luaState.setGlobal('throw', () => {
148+
throw err
149+
})
150+
const res = luaState.eval(`
151+
local ok, err = pcall(throw)
152+
return { ok, err }
153+
`)
154+
deepStrictEqual(res, { 1: false, 2: `${err.name}: ${err.message}` })
155+
})
144156
})
145157
})

0 commit comments

Comments
 (0)