Skip to content

Commit 81ad810

Browse files
sahrensvjeux
authored andcommitted
[ReactNative] differentiate fatal and soft exceptions
1 parent 484f63b commit 81ad810

File tree

6 files changed

+57
-15
lines changed

6 files changed

+57
-15
lines changed

Libraries/JavaScriptAppEngine/Initialization/ExceptionsManager.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,23 @@ type Exception = {
2525
message: string;
2626
}
2727

28-
function reportException(e: Exception, stack?: any) {
28+
function reportException(e: Exception, isFatal: bool, stack?: any) {
2929
if (RCTExceptionsManager) {
3030
if (!stack) {
3131
stack = parseErrorStack(e);
3232
}
33-
RCTExceptionsManager.reportUnhandledException(e.message, stack);
33+
if (!RCTExceptionsManager.reportFatalException ||
34+
!RCTExceptionsManager.reportSoftException) {
35+
// Backwards compatibility - no differentiation
36+
// TODO(#7049989): deprecate reportUnhandledException on Android
37+
RCTExceptionsManager.reportUnhandledException(e.message, stack);
38+
} else {
39+
if (isFatal) {
40+
RCTExceptionsManager.reportFatalException(e.message, stack);
41+
} else {
42+
RCTExceptionsManager.reportSoftException(e.message, stack);
43+
}
44+
}
3445
if (__DEV__) {
3546
(sourceMapPromise = sourceMapPromise || loadSourceMap())
3647
.then(map => {
@@ -44,7 +55,7 @@ function reportException(e: Exception, stack?: any) {
4455
}
4556
}
4657

47-
function handleException(e: Exception) {
58+
function handleException(e: Exception, isFatal: boolean) {
4859
var stack = parseErrorStack(e);
4960
var msg =
5061
'Error: ' + e.message +
@@ -57,7 +68,7 @@ function handleException(e: Exception) {
5768
} else {
5869
console.error(msg);
5970
}
60-
reportException(e, stack);
71+
reportException(e, isFatal, stack);
6172
}
6273

6374
/**
@@ -78,7 +89,7 @@ function installConsoleErrorReporter() {
7889
var str = Array.prototype.map.call(arguments, stringifySafe).join(', ');
7990
var error: any = new Error('console.error: ' + str);
8091
error.framesToPop = 1;
81-
reportException(error);
92+
reportException(error, /* isFatal */ false);
8293
};
8394
if (console.reportErrorsAsExceptions === undefined) {
8495
console.reportErrorsAsExceptions = true; // Individual apps can disable this

Libraries/JavaScriptAppEngine/Initialization/InitializeJavaScriptAppEngine.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ function setupDocumentShim() {
6666
GLOBAL.MutationObserver = undefined;
6767
}
6868

69-
function handleErrorWithRedBox(e) {
69+
function handleErrorWithRedBox(e, isFatal) {
7070
try {
71-
require('ExceptionsManager').handleException(e);
71+
require('ExceptionsManager').handleException(e, isFatal);
7272
} catch(ee) {
7373
console.log('Failed to print error: ', ee.message);
7474
}

Libraries/Utilities/MessageQueue.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ var REQUEST_PARAMSS = 2;
8181
var RESPONSE_CBIDS = 3;
8282
var RESPONSE_RETURN_VALUES = 4;
8383

84+
var applyWithErrorReporter = function(fun: Function, context: ?any, args: ?any) {
85+
try {
86+
return fun.apply(context, args);
87+
} catch (e) {
88+
ErrorUtils.reportFatalError(e);
89+
}
90+
};
91+
8492
/**
8593
* Utility to catch errors and prevent having to bind, or execute a bound
8694
* function, while catching errors in a process and returning a resulting
@@ -97,10 +105,10 @@ var RESPONSE_RETURN_VALUES = 4;
97105
*/
98106
var guardReturn = function(operation, operationArguments, getReturnValue, context) {
99107
if (operation) {
100-
ErrorUtils.applyWithGuard(operation, context, operationArguments);
108+
applyWithErrorReporter(operation, context, operationArguments);
101109
}
102110
if (getReturnValue) {
103-
return ErrorUtils.applyWithGuard(getReturnValue, context, null);
111+
return applyWithErrorReporter(getReturnValue, context, null);
104112
}
105113
return null;
106114
};

React/Modules/RCTExceptionsManager.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313

1414
@protocol RCTExceptionsManagerDelegate <NSObject>
1515

16-
- (void)unhandledJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
16+
- (void)handleSoftJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
17+
- (void)handleFatalJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
18+
- (void)updateJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack;
1719

1820
@end
1921

React/Modules/RCTExceptionsManager.m

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,23 @@ - (instancetype)init
3636
return [self initWithDelegate:nil];
3737
}
3838

39-
RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
39+
RCT_EXPORT_METHOD(reportSoftException:(NSString *)message
40+
stack:(NSArray *)stack)
41+
{
42+
// TODO(#7070533): report a soft error to the server
43+
if (_delegate) {
44+
[_delegate handleSoftJSExceptionWithMessage:message stack:stack];
45+
return;
46+
}
47+
48+
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
49+
}
50+
51+
RCT_EXPORT_METHOD(reportFatalException:(NSString *)message
4052
stack:(NSArray *)stack)
4153
{
4254
if (_delegate) {
43-
[_delegate unhandledJSExceptionWithMessage:message stack:stack];
55+
[_delegate handleFatalJSExceptionWithMessage:message stack:stack];
4456
return;
4557
}
4658

@@ -78,11 +90,17 @@ - (instancetype)init
7890
stack:(NSArray *)stack)
7991
{
8092
if (_delegate) {
81-
[_delegate unhandledJSExceptionWithMessage:message stack:stack];
93+
[_delegate updateJSExceptionWithMessage:message stack:stack];
8294
return;
8395
}
8496

8597
[[RCTRedBox sharedInstance] updateErrorMessage:message withStack:stack];
8698
}
8799

100+
// Deprecated. Use reportFatalException directly instead.
101+
RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
102+
stack:(NSArray *)stack)
103+
{
104+
[self reportFatalException:message stack:stack];
105+
}
88106
@end

packager/react-packager/src/DependencyResolver/haste/polyfills/error-guard.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@
2222
ErrorUtils._globalHandler = fun;
2323
},
2424
reportError: function(error) {
25-
Error._globalHandler && ErrorUtils._globalHandler(error);
25+
ErrorUtils._globalHandler && ErrorUtils._globalHandler(error);
26+
},
27+
reportFatalError: function(error) {
28+
ErrorUtils._globalHandler && ErrorUtils._globalHandler(error, true);
2629
},
2730
applyWithGuard: function(fun, context, args) {
2831
try {
2932
ErrorUtils._inGuard++;
3033
return fun.apply(context, args);
3134
} catch (e) {
32-
ErrorUtils._globalHandler && ErrorUtils._globalHandler(e);
35+
ErrorUtils.reportError(e);
3336
} finally {
3437
ErrorUtils._inGuard--;
3538
}

0 commit comments

Comments
 (0)