diff --git a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js index 2677a0029..91afe2008 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js +++ b/Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js @@ -25,12 +25,23 @@ type Exception = { message: string; } -function reportException(e: Exception, stack?: any) { +function reportException(e: Exception, isFatal: bool, stack?: any) { if (RCTExceptionsManager) { if (!stack) { stack = parseErrorStack(e); } - RCTExceptionsManager.reportUnhandledException(e.message, stack); + if (!RCTExceptionsManager.reportFatalException || + !RCTExceptionsManager.reportSoftException) { + // Backwards compatibility - no differentiation + // TODO(#7049989): deprecate reportUnhandledException on Android + RCTExceptionsManager.reportUnhandledException(e.message, stack); + } else { + if (isFatal) { + RCTExceptionsManager.reportFatalException(e.message, stack); + } else { + RCTExceptionsManager.reportSoftException(e.message, stack); + } + } if (__DEV__) { (sourceMapPromise = sourceMapPromise || loadSourceMap()) .then(map => { @@ -44,7 +55,7 @@ function reportException(e: Exception, stack?: any) { } } -function handleException(e: Exception) { +function handleException(e: Exception, isFatal: boolean) { var stack = parseErrorStack(e); var msg = 'Error: ' + e.message + @@ -57,7 +68,7 @@ function handleException(e: Exception) { } else { console.error(msg); } - reportException(e, stack); + reportException(e, isFatal, stack); } /** @@ -78,7 +89,7 @@ function installConsoleErrorReporter() { var str = Array.prototype.map.call(arguments, stringifySafe).join(', '); var error: any = new Error('console.error: ' + str); error.framesToPop = 1; - reportException(error); + reportException(error, /* isFatal */ false); }; if (console.reportErrorsAsExceptions === undefined) { console.reportErrorsAsExceptions = true; // Individual apps can disable this diff --git a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js index 7bddf87b6..217fd93e7 100644 --- a/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js +++ b/Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js @@ -66,9 +66,9 @@ function setupDocumentShim() { GLOBAL.MutationObserver = undefined; } -function handleErrorWithRedBox(e) { +function handleErrorWithRedBox(e, isFatal) { try { - require('ExceptionsManager').handleException(e); + require('ExceptionsManager').handleException(e, isFatal); } catch(ee) { console.log('Failed to print error: ', ee.message); } diff --git a/Libraries/Utilities/MessageQueue.js b/Libraries/Utilities/MessageQueue.js index f15dd70e0..df34dde06 100644 --- a/Libraries/Utilities/MessageQueue.js +++ b/Libraries/Utilities/MessageQueue.js @@ -81,6 +81,14 @@ var REQUEST_PARAMSS = 2; var RESPONSE_CBIDS = 3; var RESPONSE_RETURN_VALUES = 4; +var applyWithErrorReporter = function(fun: Function, context: ?any, args: ?any) { + try { + return fun.apply(context, args); + } catch (e) { + ErrorUtils.reportFatalError(e); + } +}; + /** * Utility to catch errors and prevent having to bind, or execute a bound * function, while catching errors in a process and returning a resulting @@ -97,10 +105,10 @@ var RESPONSE_RETURN_VALUES = 4; */ var guardReturn = function(operation, operationArguments, getReturnValue, context) { if (operation) { - ErrorUtils.applyWithGuard(operation, context, operationArguments); + applyWithErrorReporter(operation, context, operationArguments); } if (getReturnValue) { - return ErrorUtils.applyWithGuard(getReturnValue, context, null); + return applyWithErrorReporter(getReturnValue, context, null); } return null; }; diff --git a/React/Modules/RCTExceptionsManager.h b/React/Modules/RCTExceptionsManager.h index 25e0fbefc..79fd59eac 100644 --- a/React/Modules/RCTExceptionsManager.h +++ b/React/Modules/RCTExceptionsManager.h @@ -13,7 +13,9 @@ @protocol RCTExceptionsManagerDelegate -- (void)unhandledJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack; +- (void)handleSoftJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack; +- (void)handleFatalJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack; +- (void)updateJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack; @end diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m index ddea1275a..7512c540d 100644 --- a/React/Modules/RCTExceptionsManager.m +++ b/React/Modules/RCTExceptionsManager.m @@ -36,11 +36,23 @@ RCT_EXPORT_MODULE() return [self initWithDelegate:nil]; } -RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message +RCT_EXPORT_METHOD(reportSoftException:(NSString *)message + stack:(NSArray *)stack) +{ + // TODO(#7070533): report a soft error to the server + if (_delegate) { + [_delegate handleSoftJSExceptionWithMessage:message stack:stack]; + return; + } + + [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; +} + +RCT_EXPORT_METHOD(reportFatalException:(NSString *)message stack:(NSArray *)stack) { if (_delegate) { - [_delegate unhandledJSExceptionWithMessage:message stack:stack]; + [_delegate handleFatalJSExceptionWithMessage:message stack:stack]; return; } @@ -78,11 +90,17 @@ RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message stack:(NSArray *)stack) { if (_delegate) { - [_delegate unhandledJSExceptionWithMessage:message stack:stack]; + [_delegate updateJSExceptionWithMessage:message stack:stack]; return; } [[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack]; } +// Deprecated. Use reportFatalException directly instead. +RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message + stack:(NSArray *)stack) +{ + [self reportFatalException:message stack:stack]; +} @end diff --git a/packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js b/packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js index 3816617f2..d4aa41225 100644 --- a/packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js +++ b/packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js @@ -22,14 +22,17 @@ ErrorUtils._globalHandler = fun; }, reportError: function(error) { - Error._globalHandler && ErrorUtils._globalHandler(error); + ErrorUtils._globalHandler && ErrorUtils._globalHandler(error); + }, + reportFatalError: function(error) { + ErrorUtils._globalHandler && ErrorUtils._globalHandler(error, true); }, applyWithGuard: function(fun, context, args) { try { ErrorUtils._inGuard++; return fun.apply(context, args); } catch (e) { - ErrorUtils._globalHandler && ErrorUtils._globalHandler(e); + ErrorUtils.reportError(e); } finally { ErrorUtils._inGuard--; }