Repository navigation
Node-API-JSI: rethrow native errors from host functions as their JS value - #252
matthargett wants to merge 2 commits into
Conversation
…alue
Native code throws Napi::Error, a C++ exception carrying the JavaScript
error object. JSI reports any std::exception that escapes a host
function as a fresh Error("Exception in HostFunction: " + what()), so
on the JSI backend a polyfill's TypeError reached scripts as a plain
Error with a prefixed message: `instanceof TypeError` was false and
messages no longer compared equal, unlike every other backend.
Route every host-function trampoline (Function::New callbacks, class
constructors, static and instance methods and accessors) through a
helper that catches Napi::Error and rethrows jsi::JSError with the
original value, the same conversion Napi::Eval already does in the
other direction (BabylonJS#246).
Regression test: "native exceptions reach scripts as the thrown error
object, with its class and message, on every engine".
JS-to-native callbacks can report an error either by throwing Napi::Error or by calling ThrowAsJavaScriptException, which records an exception in the environment. The JSI trampoline converted only the first form, allowing pending exceptions to escape their JavaScript call site and surface later as uncaught dispatcher failures.\n\nCheck and clear the pending slot before every JSI host callback returns, then throw its original JavaScript value through JSI. Add a reentrant callback regression that exercises the pending-exception form without depending on a particular polyfill.
|
[Commented by Copilot on behalf of @bghgary] Superseded by #264, which preserves @matthargett’s authorship and resolves the conflicts with current |
*[Created by Copilot on behalf of @bghgary]* Native errors on the JavaScript Interface (JSI) backend reach scripts as generic errors instead of retaining their original class, message, and value. Pending exceptions from reentrant callbacks can also escape the call site. Supersedes [#252](#252), originally authored by @matthargett. Both original commits retain Matt Hargett's authorship; the tests have been relocated to match current `main`. This replacement is needed because maintainers cannot update the original branch in its organization-owned fork. --------- Co-authored-by: Matt Hargett <plaztiksyke@gmail.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 27231fa6-6686-460a-86dc-642f10ba32b5
Thanks for doing this! I've given you write permission to my org's forks so that you can push directly to the branches in the future. If there's anyone else that would be useful to give those permissions, let me know. |
Problem
On the JSI backend, an error thrown by native code — every
Napi::TypeError/RangeError/Errora polyfill raises — reaches scripts as a genericError("Exception in HostFunction: <message>"): JSI wraps anystd::exceptionescaping a host function that way.e instanceof TypeErroris false ande.messagecarries the prefix, unlike every other backend. #251's twin hit exactly this (expected [Function] to throw TypeError but Error: Exception in HostFunction: URL.createObjectURL: argument is not a Blob was thrown), andtests.tsalready carries a comment working around the prefix.Change
Every host-function trampoline in
Core/Node-API-JSI/Include/napi/napi-inl.h(Function::Newcallbacks, class constructors, static/instance methods and accessors) now runs throughdetails::CallHost, which catchesNapi::Errorand rethrowsjsi::JSErrorwith the original JS value — the same conversionNapi::Evalalready performs in the other direction (#246). JSI propagates aJSError's value unchanged, so scripts observe the same exception on JSI as elsewhere.Regression test (
native exceptionssuite): aTypeErrorfrom a static method and one from a constructor arrive asTypeErrorwith their message and noHostFunctionprefix. Passes on JavaScriptCore locally; the fork twin exercises the JSI matrix. Fork twin: rebeckerspecialties#36.Note: this latent issue was discovered by the ChatGPT DayBreak model when I was doing adversarial testing on my webgpu branch.