feat(tm-ios): Reject Promise if Turbo Module method throws an Error#40764
feat(tm-ios): Reject Promise if Turbo Module method throws an Error#40764krystofwoldrich wants to merge 67 commits intofacebook:mainfrom
Conversation
…or (#37484) Summary: ### [iOS change here](#40764) This PR builds upon the previous work done in #36925, which introduced native stack traces to the JSError for synchronous functions. The current modifications concentrate on functions that return Promises. Prior to this PR, errors within Promise-returning functions would be thrown at the platform layer crashing the app without a link to the JS stack. After the implementation of this PR, errors thrown within Promise-returning functions are now captured and transformed into rejected Promises. These rejected Promises contain a JS Error object that contains both the JS stack trace and the cause, along with the platform stack trace. Additionally, this PR ensures that rejections from native functions are now linked to the JS stack trace, providing a more comprehensive view of the rejection flow. ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [GENERAL][ADDED] - Turbo Modules Promise-returning functions reject with JS and platform stack traces information Pull Request resolved: #37484 Test Plan: | Android | |--------| |  | Example of intentionally rejected promise on Android: ``` { "name": "Error", "message": "Exception in HostFunction: intentional promise rejection", "stack": "[native code]\ntryCallTwo@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:25844:9\ndoResolve@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:25975:25\nPromise@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:25863:14\n[native code]\nrejectPromise@http://10.0.2.2:8081/js/examples/TurboModule/SampleTurboModuleExample.bundle?platform=android&lazy=true&app=com.facebook.react.uiapp&modulesOnly=true&dev=true&minify=false&runModule=true&shallow=true:42:70\nonPress@http://10.0.2.2:8081/js/examples/TurboModule/SampleTurboModuleExample.bundle?platform=android&lazy=true&app=com.facebook.react.uiapp&modulesOnly=true&dev=true&minify=false&runModule=true&shallow=true:242:71\n_performTransitionSideEffects@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:51896:22\n_receiveSignal@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:51852:45\nonResponderRelease@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:51715:34\ninvokeGuardedCallbackProd@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:2962:21\ninvokeGuardedCallback@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:3048:42\ninvokeGuardedCallbackAndCatchFirstError@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:3051:36\nexecuteDispatch@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:3115:48\nexecuteDispatchesInOrder@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:3132:26\nexecuteDispatchesAndRelease@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:4856:35\nforEach@[native code]\nforEachAccumulated@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:3574:22\nrunEventsInBatch@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:4874:27\nrunExtractedPluginEventsInBatch@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:4896:25\nhttp://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:4914:42\nbatchedUpdates$1@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:14750:20\nbatchedUpdates@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:4845:36\ndispatchEvent@http://10.0.2.2:8081/js/RNTesterApp.android.bundle?platform=android&dev=true&lazy=true&minify=false&app=com.facebook.react.uiapp&modulesOnly=false&runModule=true:4907:23", "cause": { "nativeStackAndroid": [ { "lineNumber": 173, "file": "SampleTurboModule.java", "methodName": "getValueWithPromise", "class": "com.facebook.fbreact.specs.SampleTurboModule" }, { "lineNumber": -2, "file": "NativeRunnable.java", "methodName": "run", "class": "com.facebook.jni.NativeRunnable" }, { "lineNumber": 942, "file": "Handler.java", "methodName": "handleCallback", "class": "android.os.Handler" }, { "lineNumber": 99, "file": "Handler.java", "methodName": "dispatchMessage", "class": "android.os.Handler" }, { "lineNumber": 27, "file": "MessageQueueThreadHandler.java", "methodName": "dispatchMessage", "class": "com.facebook.react.bridge.queue.MessageQueueThreadHandler" }, { "lineNumber": 201, "file": "Looper.java", "methodName": "loopOnce", "class": "android.os.Looper" }, { "lineNumber": 288, "file": "Looper.java", "methodName": "loop", "class": "android.os.Looper" }, { "lineNumber": 228, "file": "MessageQueueThreadImpl.java", "methodName": "run", "class": "com.facebook.react.bridge.queue.MessageQueueThreadImpl$4" }, { "lineNumber": 1012, "file": "Thread.java", "methodName": "run", "class": "java.lang.Thread" } ], "userInfo": null, "message": "intentional promise rejection", "code": "code 1" } } ``` How I logged out the Errors: ```js console.log('Error in JS:', JSON.stringify({ name: e.name, message: e.message, stack: e.stack, ...e, }, null, 2)); ``` Reviewed By: RSNara Differential Revision: D50613349 Pulled By: javache fbshipit-source-id: b49c469118c8d8d27c43164f110dfe57ddd592d9
|
@javache Hi, is there anything blocking this PR? |
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
javache
left a comment
There was a problem hiding this comment.
Looks good to me (and sorry for the delay). Just some nits, but should be good to import.
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Show resolved
Hide resolved
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
|
@javache Thank you 🙏, no worries, will update the PR. |
|
Hi @javache, |
javache
left a comment
There was a problem hiding this comment.
Just a few remaining nits and and some conflicts with main to resolve, and I'll try to get this landed internally :)
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
| error.asObject(runtime).setProperty(runtime, "cause", std::move(cause)); | ||
| return {runtime, std::move(error)}; | ||
| return jsi::JSError(runtime, std::move(error)); | ||
| } |
There was a problem hiding this comment.
Is this method now unused? Can it be removed?
There was a problem hiding this comment.
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
.../react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.mm
Outdated
Show resolved
Hide resolved
|
@javache Thank you very much, I've applied the suggestions. |
|
This PR is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days. |
|
This PR was closed because it has been stalled for 7 days with no activity. |
Summary:
Android change here
This PR builds upon the previous work done in #36925, which introduced native stack traces to the JSError for synchronous functions.
The current modifications concentrate on functions that return Promises. Prior to this PR, errors within Promise-returning functions would be thrown at the platform layer crashing the app without a link to the JS stack.
After the implementation of this PR, errors thrown within Promise-returning functions are now captured and transformed into rejected Promises. These rejected Promises contain a JS Error object that contains both the JS stack trace and the cause, along with the platform stack trace.
Additionally, this PR ensures that rejections from native functions are now linked to the JS stack trace, providing a more comprehensive view of the rejection flow.
Changelog:
[GENERAL][ADDED] - Turbo Modules Promise-returning functions reject with JS and platform stack traces information
Test Plan:
Example of intentionally rejected promise on iOS:
How I logged out the Errors: