diff --git a/binaries/data/mods/_test.scriptinterface/exception/catch.js b/binaries/data/mods/_test.scriptinterface/exception/catch.js new file mode 100644 index 0000000000..ef716ea765 --- /dev/null +++ b/binaries/data/mods/_test.scriptinterface/exception/catch.js @@ -0,0 +1,8 @@ +try +{ + Engine.callback(); +} +catch (e) +{ + log(e.message); +} diff --git a/source/scriptinterface/FunctionWrapper.h b/source/scriptinterface/FunctionWrapper.h index b79fdf736c..7fc82ce9ac 100644 --- a/source/scriptinterface/FunctionWrapper.h +++ b/source/scriptinterface/FunctionWrapper.h @@ -324,20 +324,26 @@ public: if (!wentOk) return false; - /** - * TODO: error handling isn't standard, and since this can call any C++ function, - * there's no simple obvious way to deal with it. - * For now we check for pending JS exceptions, but it would probably be nicer - * to standardise on something, or perhaps provide an "errorHandler" here. - */ - if constexpr (std::is_same_v::return_type>) - call(obj, outs); - else if constexpr (std::is_same_v::return_type>) - args.rval().set(call(obj, outs)); - else - Script::ToJSVal(rq, args.rval(), call(obj, outs)); + try + { + if constexpr (std::is_same_v::return_type>) + call(obj, outs); + else if constexpr (std::is_same_v::return_type>) + args.rval().set(call(obj, outs)); + else + Script::ToJSVal(rq, args.rval(), call(obj, outs)); - return !ScriptException::IsPending(rq); + return !ScriptException::IsPending(rq); + } + catch (const std::exception& e) + { + ScriptException::Raise(rq, "%s", e.what()); + } + catch (...) + { + ScriptException::Raise(rq, "Unknown error occured in an Engine callback."); + } + return false; } /** diff --git a/source/scriptinterface/tests/test_FunctionWrapper.h b/source/scriptinterface/tests/test_FunctionWrapper.h index 659098fb6d..a54fe49932 100644 --- a/source/scriptinterface/tests/test_FunctionWrapper.h +++ b/source/scriptinterface/tests/test_FunctionWrapper.h @@ -18,6 +18,7 @@ #include "lib/self_test.h" #include "scriptinterface/FunctionWrapper.h" +#include "scriptinterface/ModuleLoader.h" #include "scriptinterface/ScriptContext.h" #include "scriptinterface/ScriptInterface.h" @@ -130,4 +131,26 @@ public: TS_ASSERT(!ScriptFunction::CallVoid(rq, nativeScope, name)); } + + void test_exception() + { + g_VFS = CreateVfs(); + TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "_test.scriptinterface" / "exception" / "", + VFS_MOUNT_MUST_EXIST)); + + ScriptInterface script{"Engine", "Test", g_ScriptContext, [](const VfsPath&){ + return true; + }}; + const ScriptRequest rq{script}; + + auto _ = ScriptFunction::Register(rq, "callback", [&](){ + throw std::runtime_error{"Testerror"}; + }); + + TestLogger logger; + std::ignore = script.GetModuleLoader().LoadModule(rq, "catch.js"); + TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "Testerror"); + + g_VFS.reset(); + } };