Skip to content

Use createError in JSError and forward it in RuntimeDecorator (#58961) - #58961

Closed
lavenzg wants to merge 1 commit into
react:mainfrom
lavenzg:export-D124178378
Closed

lavenzg wants to merge 1 commit into
react:mainfrom
lavenzg:export-D124178378

Conversation

@lavenzg

@lavenzg lavenzg commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary:

JSError(rt, message) still creates its value by looking up globalThis.Error and calling it, which predates IRuntime::createError. It now uses rt.createError(...) like the JSError::createXError factories, with the same try/catch fallback. The default Runtime::createError makes the same global call, so runtimes without a native implementation behave as before. On Hermes every JSError thrown from native code skips a global lookup and a JS call, and message, stack and what() come out the same.

RuntimeDecorator and WithRuntimeDecorator didn't forward the createError family, so decorated runtimes (the thread-safe runtime, TracingRuntime) quietly fell back to the global constructor calls. They forward to plain() now, through Around for WithRuntimeDecorator, and they're public like in jsi::Runtime.

One visible difference on Hermes, same as the other createXError APIs: if user code replaced or deleted globalThis.Error, JSError(rt, message) no longer calls the replacement and creates a real Error anyway.

X-link: facebook/hermes#2203

Test Plan:
Two new tests in testlib.cpp. JSErrorMessageTest checks the value is an Error with the right message and a string stack, and that a counting globalThis.Error replacement gets called exactly as often as rt.createError calls it, so it holds for native and default implementations. DecoratorCreateErrorTest checks that all seven createXError calls, and JSError, reach the wrapped runtime once through both decorators, and that Around runs once.

JSErrorDoesNotInfinitelyRecurse and CreateJSErrorPropagatesOOMExceptionTest run through decorators that use the default Runtime::createError. This keeps the missing-constructor fallback and OOM propagation covered, since both tests depend on changing globalThis.Error.

APITests (ASan + Debug, macOS arm64, HERMESVM_EXCEPTION_ON_OOM=ON): 362/362 pass, including SynthTrace and all three OOM tests. Built with -O1 and HERMES_ENABLE_WERROR=ON; the existing GoogleTest uninitialized-const-pointer warning is demoted for AppleClang 21.

cmake --build cmake-build-asan-oom --target APITests -j 8
cmake-build-asan-oom/unittests/API/APITests

Before the OOM test adaptation, CreateJSErrorPropagatesOOMExceptionTest fails because it throws JSError instead of JSOutOfMemoryError. It passes with the decorator. The earlier checks with the jsi.cpp and decorator.h changes reverted also made the new JSI tests fail.

Reviewed By: javache

Differential Revision: D124178378

Pulled By: lavenzg

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 8, 2026
@facebook-github-tools facebook-github-tools Bot added p: Facebook Partner: Facebook Partner labels Oct 8, 2026
@meta-codesync

meta-codesync Bot commented Oct 8, 2026

Copy link
Copy Markdown

@lavenzg has exported this pull request. If you are a Meta employee, you can view the originating Diff in D124178378.

@meta-codesync meta-codesync Bot changed the title Use createError in JSError and forward it in RuntimeDecorator Use createError in JSError and forward it in RuntimeDecorator (#58961) Oct 13, 2026
lavenzg pushed a commit to lavenzg/react-native that referenced this pull request Oct 13, 2026
…58961)

Summary:


`JSError(rt, message)` still creates its value by looking up `globalThis.Error` and calling it, which predates `IRuntime::createError`. It now uses `rt.createError(...)` like the `JSError::createXError` factories, with the same try/catch fallback. The default `Runtime::createError` makes the same global call, so runtimes without a native implementation behave as before. On Hermes every `JSError` thrown from native code skips a global lookup and a JS call, and `message`, `stack` and `what()` come out the same.

`RuntimeDecorator` and `WithRuntimeDecorator` didn't forward the `createError` family, so decorated runtimes (the thread-safe runtime, `TracingRuntime`) quietly fell back to the global constructor calls. They forward to `plain()` now, through `Around` for `WithRuntimeDecorator`, and they're public like in `jsi::Runtime`.

One visible difference on Hermes, same as the other `createXError` APIs: if user code replaced or deleted `globalThis.Error`, `JSError(rt, message)` no longer calls the replacement and creates a real `Error` anyway.

X-link: facebook/hermes#2203

Test Plan:
Two new tests in testlib.cpp. `JSErrorMessageTest` checks the value is an `Error` with the right message and a string stack, and that a counting `globalThis.Error` replacement gets called exactly as often as `rt.createError` calls it, so it holds for native and default implementations. `DecoratorCreateErrorTest` checks that all seven `createXError` calls, and `JSError`, reach the wrapped runtime once through both decorators, and that `Around` runs once.

`JSErrorDoesNotInfinitelyRecurse` and `CreateJSErrorPropagatesOOMExceptionTest` run through decorators that use the default `Runtime::createError`. This keeps the missing-constructor fallback and OOM propagation covered, since both tests depend on changing `globalThis.Error`.

`APITests` (ASan + Debug, macOS arm64, `HERMESVM_EXCEPTION_ON_OOM=ON`): 362/362 pass, including SynthTrace and all three OOM tests. Built with `-O1` and `HERMES_ENABLE_WERROR=ON`; the existing GoogleTest `uninitialized-const-pointer` warning is demoted for AppleClang 21.

```sh
cmake --build cmake-build-asan-oom --target APITests -j 8
cmake-build-asan-oom/unittests/API/APITests
```

Before the OOM test adaptation, `CreateJSErrorPropagatesOOMExceptionTest` fails because it throws `JSError` instead of `JSOutOfMemoryError`. It passes with the decorator. The earlier checks with the `jsi.cpp` and `decorator.h` changes reverted also made the new JSI tests fail.

Reviewed By: javache

Differential Revision: D124178378

Pulled By: lavenzg
…58961)

Summary:
Pull Request resolved: react#58961

`JSError(rt, message)` still creates its value by looking up `globalThis.Error` and calling it, which predates `IRuntime::createError`. It now uses `rt.createError(...)` like the `JSError::createXError` factories, with the same try/catch fallback. The default `Runtime::createError` makes the same global call, so runtimes without a native implementation behave as before. On Hermes every `JSError` thrown from native code skips a global lookup and a JS call, and `message`, `stack` and `what()` come out the same.

`RuntimeDecorator` and `WithRuntimeDecorator` didn't forward the `createError` family, so decorated runtimes (the thread-safe runtime, `TracingRuntime`) quietly fell back to the global constructor calls. They forward to `plain()` now, through `Around` for `WithRuntimeDecorator`, and they're public like in `jsi::Runtime`.

One visible difference on Hermes, same as the other `createXError` APIs: if user code replaced or deleted `globalThis.Error`, `JSError(rt, message)` no longer calls the replacement and creates a real `Error` anyway.

X-link: facebook/hermes#2203

Test Plan:
Two new tests in testlib.cpp. `JSErrorMessageTest` checks the value is an `Error` with the right message and a string stack, and that a counting `globalThis.Error` replacement gets called exactly as often as `rt.createError` calls it, so it holds for native and default implementations. `DecoratorCreateErrorTest` checks that all seven `createXError` calls, and `JSError`, reach the wrapped runtime once through both decorators, and that `Around` runs once.

`JSErrorDoesNotInfinitelyRecurse` and `CreateJSErrorPropagatesOOMExceptionTest` run through decorators that use the default `Runtime::createError`. This keeps the missing-constructor fallback and OOM propagation covered, since both tests depend on changing `globalThis.Error`.

`APITests` (ASan + Debug, macOS arm64, `HERMESVM_EXCEPTION_ON_OOM=ON`): 362/362 pass, including SynthTrace and all three OOM tests. Built with `-O1` and `HERMES_ENABLE_WERROR=ON`; the existing GoogleTest `uninitialized-const-pointer` warning is demoted for AppleClang 21.

```sh
cmake --build cmake-build-asan-oom --target APITests -j 8
cmake-build-asan-oom/unittests/API/APITests
```

Before the OOM test adaptation, `CreateJSErrorPropagatesOOMExceptionTest` fails because it throws `JSError` instead of `JSOutOfMemoryError`. It passes with the decorator. The earlier checks with the `jsi.cpp` and `decorator.h` changes reverted also made the new JSI tests fail.

Reviewed By: javache

Differential Revision: D124178378

Pulled By: lavenzg
meta-codesync Bot pushed a commit to facebook/hermes that referenced this pull request Oct 13, 2026
Summary:
X-link: react/react-native#58961

`JSError(rt, message)` still creates its value by looking up `globalThis.Error` and calling it, which predates `IRuntime::createError`. It now uses `rt.createError(...)` like the `JSError::createXError` factories, with the same try/catch fallback. The default `Runtime::createError` makes the same global call, so runtimes without a native implementation behave as before. On Hermes every `JSError` thrown from native code skips a global lookup and a JS call, and `message`, `stack` and `what()` come out the same.

`RuntimeDecorator` and `WithRuntimeDecorator` didn't forward the `createError` family, so decorated runtimes (the thread-safe runtime, `TracingRuntime`) quietly fell back to the global constructor calls. They forward to `plain()` now, through `Around` for `WithRuntimeDecorator`, and they're public like in `jsi::Runtime`.

One visible difference on Hermes, same as the other `createXError` APIs: if user code replaced or deleted `globalThis.Error`, `JSError(rt, message)` no longer calls the replacement and creates a real `Error` anyway.

Pull Request resolved: #2203

Test Plan:
Two new tests in testlib.cpp. `JSErrorMessageTest` checks the value is an `Error` with the right message and a string stack, and that a counting `globalThis.Error` replacement gets called exactly as often as `rt.createError` calls it, so it holds for native and default implementations. `DecoratorCreateErrorTest` checks that all seven `createXError` calls, and `JSError`, reach the wrapped runtime once through both decorators, and that `Around` runs once.

`JSErrorDoesNotInfinitelyRecurse` and `CreateJSErrorPropagatesOOMExceptionTest` run through decorators that use the default `Runtime::createError`. This keeps the missing-constructor fallback and OOM propagation covered, since both tests depend on changing `globalThis.Error`.

`APITests` (ASan + Debug, macOS arm64, `HERMESVM_EXCEPTION_ON_OOM=ON`): 362/362 pass, including SynthTrace and all three OOM tests. Built with `-O1` and `HERMES_ENABLE_WERROR=ON`; the existing GoogleTest `uninitialized-const-pointer` warning is demoted for AppleClang 21.

```sh
cmake --build cmake-build-asan-oom --target APITests -j 8
cmake-build-asan-oom/unittests/API/APITests
```

Before the OOM test adaptation, `CreateJSErrorPropagatesOOMExceptionTest` fails because it throws `JSError` instead of `JSOutOfMemoryError`. It passes with the decorator. The earlier checks with the `jsi.cpp` and `decorator.h` changes reverted also made the new JSI tests fail.

Reviewed By: javache

Differential Revision: D124178378

Pulled By: lavenzg

fbshipit-source-id: 33fa348abc52e21626f68d6509093313ab438d87
meta-codesync Bot pushed a commit to facebook/hermes that referenced this pull request Oct 13, 2026
Summary:
X-link: react/react-native#58961

`JSError(rt, message)` still creates its value by looking up `globalThis.Error` and calling it, which predates `IRuntime::createError`. It now uses `rt.createError(...)` like the `JSError::createXError` factories, with the same try/catch fallback. The default `Runtime::createError` makes the same global call, so runtimes without a native implementation behave as before. On Hermes every `JSError` thrown from native code skips a global lookup and a JS call, and `message`, `stack` and `what()` come out the same.

`RuntimeDecorator` and `WithRuntimeDecorator` didn't forward the `createError` family, so decorated runtimes (the thread-safe runtime, `TracingRuntime`) quietly fell back to the global constructor calls. They forward to `plain()` now, through `Around` for `WithRuntimeDecorator`, and they're public like in `jsi::Runtime`.

One visible difference on Hermes, same as the other `createXError` APIs: if user code replaced or deleted `globalThis.Error`, `JSError(rt, message)` no longer calls the replacement and creates a real `Error` anyway.

Pull Request resolved: #2203

Test Plan:
Two new tests in testlib.cpp. `JSErrorMessageTest` checks the value is an `Error` with the right message and a string stack, and that a counting `globalThis.Error` replacement gets called exactly as often as `rt.createError` calls it, so it holds for native and default implementations. `DecoratorCreateErrorTest` checks that all seven `createXError` calls, and `JSError`, reach the wrapped runtime once through both decorators, and that `Around` runs once.

`JSErrorDoesNotInfinitelyRecurse` and `CreateJSErrorPropagatesOOMExceptionTest` run through decorators that use the default `Runtime::createError`. This keeps the missing-constructor fallback and OOM propagation covered, since both tests depend on changing `globalThis.Error`.

`APITests` (ASan + Debug, macOS arm64, `HERMESVM_EXCEPTION_ON_OOM=ON`): 362/362 pass, including SynthTrace and all three OOM tests. Built with `-O1` and `HERMES_ENABLE_WERROR=ON`; the existing GoogleTest `uninitialized-const-pointer` warning is demoted for AppleClang 21.

```sh
cmake --build cmake-build-asan-oom --target APITests -j 8
cmake-build-asan-oom/unittests/API/APITests
```

Before the OOM test adaptation, `CreateJSErrorPropagatesOOMExceptionTest` fails because it throws `JSError` instead of `JSOutOfMemoryError`. It passes with the decorator. The earlier checks with the `jsi.cpp` and `decorator.h` changes reverted also made the new JSI tests fail.

Reviewed By: javache

Differential Revision: D124178378

Pulled By: lavenzg

fbshipit-source-id: 33fa348abc52e21626f68d6509093313ab438d87
meta-codesync Bot pushed a commit to facebook/hermes that referenced this pull request Oct 13, 2026
Summary:
X-link: react/react-native#58961

`JSError(rt, message)` still creates its value by looking up `globalThis.Error` and calling it, which predates `IRuntime::createError`. It now uses `rt.createError(...)` like the `JSError::createXError` factories, with the same try/catch fallback. The default `Runtime::createError` makes the same global call, so runtimes without a native implementation behave as before. On Hermes every `JSError` thrown from native code skips a global lookup and a JS call, and `message`, `stack` and `what()` come out the same.

`RuntimeDecorator` and `WithRuntimeDecorator` didn't forward the `createError` family, so decorated runtimes (the thread-safe runtime, `TracingRuntime`) quietly fell back to the global constructor calls. They forward to `plain()` now, through `Around` for `WithRuntimeDecorator`, and they're public like in `jsi::Runtime`.

One visible difference on Hermes, same as the other `createXError` APIs: if user code replaced or deleted `globalThis.Error`, `JSError(rt, message)` no longer calls the replacement and creates a real `Error` anyway.

Pull Request resolved: #2203

Test Plan:
Two new tests in testlib.cpp. `JSErrorMessageTest` checks the value is an `Error` with the right message and a string stack, and that a counting `globalThis.Error` replacement gets called exactly as often as `rt.createError` calls it, so it holds for native and default implementations. `DecoratorCreateErrorTest` checks that all seven `createXError` calls, and `JSError`, reach the wrapped runtime once through both decorators, and that `Around` runs once.

`JSErrorDoesNotInfinitelyRecurse` and `CreateJSErrorPropagatesOOMExceptionTest` run through decorators that use the default `Runtime::createError`. This keeps the missing-constructor fallback and OOM propagation covered, since both tests depend on changing `globalThis.Error`.

`APITests` (ASan + Debug, macOS arm64, `HERMESVM_EXCEPTION_ON_OOM=ON`): 362/362 pass, including SynthTrace and all three OOM tests. Built with `-O1` and `HERMES_ENABLE_WERROR=ON`; the existing GoogleTest `uninitialized-const-pointer` warning is demoted for AppleClang 21.

```sh
cmake --build cmake-build-asan-oom --target APITests -j 8
cmake-build-asan-oom/unittests/API/APITests
```

Before the OOM test adaptation, `CreateJSErrorPropagatesOOMExceptionTest` fails because it throws `JSError` instead of `JSOutOfMemoryError`. It passes with the decorator. The earlier checks with the `jsi.cpp` and `decorator.h` changes reverted also made the new JSI tests fail.

Reviewed By: javache

Differential Revision: D124178378

Pulled By: lavenzg

fbshipit-source-id: 33fa348abc52e21626f68d6509093313ab438d87
@meta-codesync meta-codesync Bot closed this in 0cca3a1 Oct 13, 2026
@meta-codesync meta-codesync Bot added the Merged This PR has been merged. label Oct 13, 2026
@meta-codesync

meta-codesync Bot commented Oct 13, 2026

Copy link
Copy Markdown

@lavenzg merged this pull request in 0cca3a1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged. meta-exported p: Facebook Partner: Facebook Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants