Handle C++ exceptions in Engine functions

`JSNatives` passed to SpiderMonkey must not throw exceptions. Most
callbacks are wrapped in `ScriptFunction::ToJSNative`.
This commit adds exception handling to `ScriptFunction::ToJSNative` so
that exceptions thrown in the wrapped callbacks are catched and rethrown
as JavaScript `Error`s.
This commit is contained in:
phosit
2025-06-18 07:22:10 +02:00
committed by Phosit
parent eeeba977ea
commit 616fa4a006
3 changed files with 50 additions and 13 deletions
@@ -0,0 +1,8 @@
try
{
Engine.callback();
}
catch (e)
{
log(e.message);
}
+19 -13
View File
@@ -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<void, typename args_info<decltype(callable)>::return_type>)
call<callable>(obj, outs);
else if constexpr (std::is_same_v<JS::Value, typename args_info<decltype(callable)>::return_type>)
args.rval().set(call<callable>(obj, outs));
else
Script::ToJSVal(rq, args.rval(), call<callable>(obj, outs));
try
{
if constexpr (std::is_same_v<void, typename args_info<decltype(callable)>::return_type>)
call<callable>(obj, outs);
else if constexpr (std::is_same_v<JS::Value, typename args_info<decltype(callable)>::return_type>)
args.rval().set(call<callable>(obj, outs));
else
Script::ToJSVal(rq, args.rval(), call<callable>(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;
}
/**
@@ -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();
}
};