diff --git a/packages/react-native/Libraries/Core/ExceptionsManager.js b/packages/react-native/Libraries/Core/ExceptionsManager.js index e64a3a0fd6e5..09c5941f0624 100644 --- a/packages/react-native/Libraries/Core/ExceptionsManager.js +++ b/packages/react-native/Libraries/Core/ExceptionsManager.js @@ -141,24 +141,27 @@ let inExceptionHandler = false; * Logs exceptions to the (native) console and displays them */ function handleException(e: mixed, isFatal: boolean) { - let error: Error; - if (e instanceof Error) { - error = e; - } else { - // Workaround for reporting errors caused by `throw 'some string'` - // Unfortunately there is no way to figure out the stacktrace in this - // case, so if you ended up here trying to trace an error, look for - // `throw ''` somewhere in your codebase. - error = new SyntheticError(e); - } - try { - inExceptionHandler = true; - /* $FlowFixMe[class-object-subtyping] added when improving typing for this - * parameters */ - // $FlowFixMe[incompatible-call] - reportException(error, isFatal, /*reportToConsole*/ true); - } finally { - inExceptionHandler = false; + // TODO(T196834299): We should really use a c++ turbomodule for this + if (!global.RN$handleException || !global.RN$handleException(e, isFatal)) { + let error: Error; + if (e instanceof Error) { + error = e; + } else { + // Workaround for reporting errors caused by `throw 'some string'` + // Unfortunately there is no way to figure out the stacktrace in this + // case, so if you ended up here trying to trace an error, look for + // `throw ''` somewhere in your codebase. + error = new SyntheticError(e); + } + try { + inExceptionHandler = true; + /* $FlowFixMe[class-object-subtyping] added when improving typing for this + * parameters */ + // $FlowFixMe[incompatible-call] + reportException(error, isFatal, /*reportToConsole*/ true); + } finally { + inExceptionHandler = false; + } } } diff --git a/packages/react-native/Libraries/Core/__mocks__/NativeExceptionsManager.js b/packages/react-native/Libraries/Core/__mocks__/NativeExceptionsManager.js index a43017ce012e..cfe7f55e61b1 100644 --- a/packages/react-native/Libraries/Core/__mocks__/NativeExceptionsManager.js +++ b/packages/react-native/Libraries/Core/__mocks__/NativeExceptionsManager.js @@ -14,7 +14,6 @@ import typeof NativeExceptionsManager from '../NativeExceptionsManager'; export default ({ reportFatalException: jest.fn(), reportSoftException: jest.fn(), - updateExceptionMessage: jest.fn(), dismissRedbox: jest.fn(), reportException: jest.fn(), }: NativeExceptionsManager); diff --git a/packages/react-native/Libraries/Core/__tests__/ExceptionsManager-test.js b/packages/react-native/Libraries/Core/__tests__/ExceptionsManager-test.js index 2dde1759c3df..ee4bf4ebfb66 100644 --- a/packages/react-native/Libraries/Core/__tests__/ExceptionsManager-test.js +++ b/packages/react-native/Libraries/Core/__tests__/ExceptionsManager-test.js @@ -67,8 +67,6 @@ function runExceptionsManagerTests() { return { default: { reportException: jest.fn(), - // Used to show symbolicated messages, not part of this test. - updateExceptionMessage: () => {}, }, }; }); diff --git a/packages/react-native/Libraries/Core/setUpErrorHandling.js b/packages/react-native/Libraries/Core/setUpErrorHandling.js index d1aaad737940..32846d42f835 100644 --- a/packages/react-native/Libraries/Core/setUpErrorHandling.js +++ b/packages/react-native/Libraries/Core/setUpErrorHandling.js @@ -21,13 +21,7 @@ ExceptionsManager.installConsoleErrorReporter(); if (!global.__fbDisableExceptionsManager) { const handleError = (e: mixed, isFatal: boolean) => { try { - // TODO(T196834299): We should really use a c++ turbomodule for this - if ( - !global.RN$handleException || - !global.RN$handleException(e, isFatal) - ) { - ExceptionsManager.handleException(e, isFatal); - } + ExceptionsManager.handleException(e, isFatal); } catch (ee) { console.log('Failed to print error: ', ee.message); throw e; diff --git a/packages/react-native/React/CoreModules/RCTExceptionsManager.h b/packages/react-native/React/CoreModules/RCTExceptionsManager.h index f23bb2153b80..d4b350961c5a 100644 --- a/packages/react-native/React/CoreModules/RCTExceptionsManager.h +++ b/packages/react-native/React/CoreModules/RCTExceptionsManager.h @@ -11,7 +11,6 @@ NS_ASSUME_NONNULL_BEGIN @protocol RCTExceptionsManagerDelegate - - (void)handleSoftJSExceptionWithMessage:(nullable NSString *)message stack:(nullable NSArray *)stack exceptionId:(NSNumber *)exceptionId @@ -20,12 +19,6 @@ NS_ASSUME_NONNULL_BEGIN stack:(nullable NSArray *)stack exceptionId:(NSNumber *)exceptionId extraDataAsJSON:(nullable NSString *)extraDataAsJSON; - -@optional -- (void)updateJSExceptionWithMessage:(nullable NSString *)message - stack:(nullable NSArray *)stack - exceptionId:(NSNumber *)exceptionId; - @end @interface RCTExceptionsManager : NSObject diff --git a/packages/react-native/React/CoreModules/RCTExceptionsManager.mm b/packages/react-native/React/CoreModules/RCTExceptionsManager.mm index 7a5de3849e2b..d7f8f647604a 100644 --- a/packages/react-native/React/CoreModules/RCTExceptionsManager.mm +++ b/packages/react-native/React/CoreModules/RCTExceptionsManager.mm @@ -99,27 +99,6 @@ - (void)reportFatal:(NSString *)message [self reportFatal:message stack:stack exceptionId:exceptionId extraDataAsJSON:nil]; } -RCT_EXPORT_METHOD(updateExceptionMessage - : (NSString *)message stack - : (NSArray *)stack exceptionId - : (double)exceptionId) -{ - if (RCTRedBoxGetEnabled()) { - RCTRedBox *redbox = [_moduleRegistry moduleForName:"RedBox"]; - [redbox updateErrorMessage:message withStack:stack errorCookie:(int)exceptionId]; - } - - if (_delegate && [_delegate respondsToSelector:@selector(updateJSExceptionWithMessage:stack:exceptionId:)]) { - [_delegate updateJSExceptionWithMessage:message stack:stack exceptionId:[NSNumber numberWithDouble:exceptionId]]; - } -} - -// Deprecated. Use reportFatalException directly instead. -RCT_EXPORT_METHOD(reportUnhandledException : (NSString *)message stack : (NSArray *)stack) -{ - [self reportFatalException:message stack:stack exceptionId:-1]; -} - RCT_EXPORT_METHOD(dismissRedbox) {} RCT_EXPORT_METHOD(reportException : (JS::NativeExceptionsManager::ExceptionData &)data) diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 5626ce74dd4d..9dac0c2d58ad 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2212,7 +2212,6 @@ public abstract class com/facebook/react/devsupport/DevSupportManagerBase : com/ public fun startInspector ()V public fun stopInspector ()V public fun toggleElementInspector ()V - public fun updateJSError (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;I)V } public abstract interface class com/facebook/react/devsupport/DevSupportManagerBase$CallbackWithBundleLoader { @@ -2380,7 +2379,6 @@ public class com/facebook/react/devsupport/ReleaseDevSupportManager : com/facebo public fun startInspector ()V public fun stopInspector ()V public fun toggleElementInspector ()V - public fun updateJSError (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;I)V } public class com/facebook/react/devsupport/StackTraceHelper { @@ -2516,7 +2514,6 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp public abstract fun startInspector ()V public abstract fun stopInspector ()V public abstract fun toggleElementInspector ()V - public abstract fun updateJSError (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;I)V } public abstract interface class com/facebook/react/devsupport/interfaces/DevSupportManager$PackagerLocationCustomizer { @@ -3138,7 +3135,6 @@ public class com/facebook/react/modules/core/ExceptionsManagerModule : com/faceb public fun reportException (Lcom/facebook/react/bridge/ReadableMap;)V public fun reportFatalException (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;D)V public fun reportSoftException (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;D)V - public fun updateExceptionMessage (Ljava/lang/String;Lcom/facebook/react/bridge/ReadableArray;D)V } public class com/facebook/react/modules/core/HeadlessJsTaskSupportModule : com/facebook/fbreact/specs/NativeHeadlessJsTaskSupportSpec { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java index 35dba5ee6e3e..f91d9add8493 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevSupportManagerBase.java @@ -279,26 +279,6 @@ public Pair processErrorCustomizers(Pair { - // Since we only show the first JS error in a succession of JS errors, make sure we only - // update the error message for that error message. This assumes that updateJSError - // belongs to the most recent showNewJSError - if ((mRedBoxSurfaceDelegate != null && !mRedBoxSurfaceDelegate.isShowing()) - || errorCookie != mLastErrorCookie) { - return; - } - - // The RedBox surface delegate will always show the latest error - updateLastErrorInfo( - message, StackTraceHelper.convertJsStackTrace(details), errorCookie, ErrorType.JS); - mRedBoxSurfaceDelegate.show(); - }); - } - @Override public void hideRedboxDialog() { if (mRedBoxSurfaceDelegate == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReleaseDevSupportManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReleaseDevSupportManager.kt index ba9e0aa03c52..02398843112d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReleaseDevSupportManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/ReleaseDevSupportManager.kt @@ -53,12 +53,6 @@ public open class ReleaseDevSupportManager : DevSupportManager { override public fun destroyRootView(rootView: View?): Unit = Unit - override public fun updateJSError( - message: String?, - details: ReadableArray?, - errorCookie: Int - ): Unit = Unit - override public fun hideRedboxDialog(): Unit = Unit override public fun showDevOptionsDialog(): Unit = Unit diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt index 67bc3b0e21bf..a778ac545776 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportManager.kt @@ -48,8 +48,6 @@ public interface DevSupportManager : JSExceptionHandler { public fun showNewJSError(message: String?, details: ReadableArray?, errorCookie: Int) - public fun updateJSError(message: String?, details: ReadableArray?, errorCookie: Int) - public fun hideRedboxDialog() public fun showDevOptionsDialog() diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/ExceptionsManagerModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/ExceptionsManagerModule.kt index 60c143a2eb34..863c87b33eb0 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/ExceptionsManagerModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/ExceptionsManagerModule.kt @@ -60,17 +60,6 @@ public open class ExceptionsManagerModule(private val devSupportManager: DevSupp } } - override fun updateExceptionMessage( - title: String?, - details: ReadableArray?, - exceptionIdDouble: Double - ) { - val exceptionId = exceptionIdDouble.toInt() - if (devSupportManager.devSupportEnabled) { - devSupportManager.updateJSError(title, details, exceptionId) - } - } - override fun dismissRedbox() { if (devSupportManager.devSupportEnabled) { devSupportManager.hideRedboxDialog() diff --git a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp index 8bcbe849d1fe..69652a173caa 100644 --- a/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp +++ b/packages/react-native/ReactCommon/jserrorhandler/JsErrorHandler.cpp @@ -50,6 +50,18 @@ void objectAssign( auto assign = Object.getPropertyAsFunction(runtime, "assign"); assign.callWithThis(runtime, Object, target, value); } + +jsi::Object wrapInErrorIfNecessary( + jsi::Runtime& runtime, + const jsi::Value& value) { + auto Error = runtime.global().getPropertyAsFunction(runtime, "Error"); + auto isError = + value.isObject() && value.asObject(runtime).instanceOf(runtime, Error); + auto error = isError + ? value.getObject(runtime) + : Error.callAsConstructor(runtime, value).getObject(runtime); + return error; +} } // namespace namespace facebook::react { @@ -187,7 +199,7 @@ void JsErrorHandler::emitError( jsi::JSError& error, bool isFatal) { auto message = error.getMessage(); - auto errorObj = error.value().getObject(runtime); + auto errorObj = wrapInErrorIfNecessary(runtime, error.value()); auto componentStackValue = errorObj.getProperty(runtime, "componentStack"); if (!isLooselyNull(componentStackValue)) { message += "\n" + stringifyToCpp(runtime, componentStackValue); diff --git a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp index 291cfa2caa4e..15ac4c8465c3 100644 --- a/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp +++ b/packages/react-native/ReactCommon/react/runtime/ReactInstance.cpp @@ -391,17 +391,6 @@ bool isTruthy(jsi::Runtime& runtime, const jsi::Value& value) { return Boolean.call(runtime, value).getBool(); } -jsi::Value wrapInErrorIfNecessary( - jsi::Runtime& runtime, - const jsi::Value& value) { - auto Error = runtime.global().getPropertyAsFunction(runtime, "Error"); - auto isError = - value.isObject() && value.asObject(runtime).instanceOf(runtime, Error); - auto error = isError ? value.getObject(runtime) - : Error.callAsConstructor(runtime, value); - return jsi::Value(runtime, error); -} - } // namespace void ReactInstance::initializeRuntime( @@ -448,8 +437,8 @@ void ReactInstance::initializeRuntime( return jsi::Value(false); } - auto jsError = jsi::JSError( - runtime, wrapInErrorIfNecessary(runtime, args[0])); + auto jsError = + jsi::JSError(runtime, jsi::Value(runtime, args[0])); jsErrorHandler->handleError(runtime, jsError, isFatal); return jsi::Value(true); diff --git a/packages/react-native/src/private/specs/modules/NativeExceptionsManager.js b/packages/react-native/src/private/specs/modules/NativeExceptionsManager.js index 1da0633dc17d..0b684c681e70 100644 --- a/packages/react-native/src/private/specs/modules/NativeExceptionsManager.js +++ b/packages/react-native/src/private/specs/modules/NativeExceptionsManager.js @@ -47,11 +47,6 @@ export interface Spec extends TurboModule { exceptionId: number, ) => void; +reportException?: (data: ExceptionData) => void; - +updateExceptionMessage: ( - message: string, - stack: Array, - exceptionId: number, - ) => void; // TODO(T53311281): This is a noop on iOS now. Implement it. +dismissRedbox?: () => void; } @@ -74,13 +69,6 @@ const ExceptionsManager = { ) { NativeModule.reportSoftException(message, stack, exceptionId); }, - updateExceptionMessage( - message: string, - stack: Array, - exceptionId: number, - ) { - NativeModule.updateExceptionMessage(message, stack, exceptionId); - }, dismissRedbox(): void { if (Platform.OS !== 'ios' && NativeModule.dismissRedbox) { // TODO(T53311281): This is a noop on iOS now. Implement it.