Create RCTFatal for reporting fatal React events

Summary: public

Add RCTFatal for reporting fatal runtime conditions. This centralizes failure handling to one function and allows you to customize how they should be handled. RCTFatal will be logged to the console and as a redbox and will also be triggered by fatal exceptions coming from RCTExceptionsManager.

Note that there is no RCTLogFatal, since just logging the fatal condition does not allow us to handle it consistently.

Reviewed By: nicklockwood

Differential Revision: D2615490

fb-gh-sync-id: 7d8e134419e10a8fb549297054ad955db3f6bee0
This commit is contained in:
Pieter De Baets
2015-11-05 12:19:56 -08:00
committed by facebook-github-bot-5
parent d8dd330d41
commit 31b5b0ac01
10 changed files with 140 additions and 124 deletions

View File

@@ -13,16 +13,11 @@
@protocol RCTExceptionsManagerDelegate <NSObject>
// NOTE: Remove these three methods and the @optional directive after updating the codebase to use only the three below
- (void)handleSoftJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack exceptionId:(NSNumber *)exceptionId;
- (void)handleFatalJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack exceptionId:(NSNumber *)exceptionId;
@optional
- (void)handleSoftJSExceptionWithMessage:(NSString *)message stack:(NSArray<NSDictionary *> *)stack;
- (void)handleFatalJSExceptionWithMessage:(NSString *)message stack:(NSArray<NSDictionary *> *)stack;
- (void)updateJSExceptionWithMessage:(NSString *)message stack:(NSArray<NSDictionary *> *)stack;
- (void)handleSoftJSExceptionWithMessage:(NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(NSNumber *)exceptionId;
- (void)handleFatalJSExceptionWithMessage:(NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(NSNumber *)exceptionId;
- (void)updateJSExceptionWithMessage:(NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(NSNumber *)exceptionId;
- (void)updateJSExceptionWithMessage:(NSString *)message stack:(NSArray *)stack exceptionId:(NSNumber *)exceptionId;
@end

View File

@@ -43,58 +43,31 @@ RCT_EXPORT_METHOD(reportSoftException:(NSString *)message
stack:(NSDictionaryArray *)stack
exceptionId:(nonnull NSNumber *)exceptionId)
{
// TODO(#7070533): report a soft error to the server
if (_delegate) {
if ([_delegate respondsToSelector:@selector(handleSoftJSExceptionWithMessage:stack:exceptionId:)]) {
[_delegate handleSoftJSExceptionWithMessage:message stack:stack exceptionId:exceptionId];
} else {
[_delegate handleSoftJSExceptionWithMessage:message stack:stack];
}
return;
}
[_bridge.redBox showErrorMessage:message withStack:stack];
if (_delegate) {
[_delegate handleSoftJSExceptionWithMessage:message stack:stack exceptionId:exceptionId];
}
}
RCT_EXPORT_METHOD(reportFatalException:(NSString *)message
stack:(NSDictionaryArray *)stack
exceptionId:(nonnull NSNumber *)exceptionId)
{
if (_delegate) {
if ([_delegate respondsToSelector:@selector(handleFatalJSExceptionWithMessage:stack:exceptionId:)]) {
[_delegate handleFatalJSExceptionWithMessage:message stack:stack exceptionId:exceptionId];
} else {
[_delegate handleFatalJSExceptionWithMessage:message stack:stack];
}
return;
}
[_bridge.redBox showErrorMessage:message withStack:stack];
if (!RCT_DEBUG) {
if (_delegate) {
[_delegate handleFatalJSExceptionWithMessage:message stack:stack exceptionId:exceptionId];
}
static NSUInteger reloadRetries = 0;
const NSUInteger maxMessageLength = 75;
if (reloadRetries < _maxReloadAttempts) {
reloadRetries++;
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification
object:nil];
} else {
if (message.length > maxMessageLength) {
message = [[message substringToIndex:maxMessageLength] stringByAppendingString:@"..."];
}
NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"];
for (NSDictionary *frame in stack) {
[prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]];
}
NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:message];
[NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack];
}
static NSUInteger reloadRetries = 0;
if (!RCT_DEBUG && reloadRetries < _maxReloadAttempts) {
reloadRetries++;
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil];
} else {
NSString *description = [@"Unhandled JS Exception: " stringByAppendingString:message];
NSDictionary *errorInfo = @{ NSLocalizedDescriptionKey: description, RCTJSStackTraceKey: stack };
RCTFatal([NSError errorWithDomain:RCTErrorDomain code:0 userInfo:errorInfo]);
}
}
@@ -102,16 +75,11 @@ RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message
stack:(NSDictionaryArray *)stack
exceptionId:(nonnull NSNumber *)exceptionId)
{
if (_delegate) {
if ([_delegate respondsToSelector:@selector(updateJSExceptionWithMessage:stack:exceptionId:)]) {
[_delegate updateJSExceptionWithMessage:message stack:stack exceptionId:exceptionId];
} else {
[_delegate updateJSExceptionWithMessage:message stack:stack];
}
return;
}
[_bridge.redBox updateErrorMessage:message withStack:stack];
if (_delegate && [_delegate respondsToSelector:@selector(updateJSExceptionWithMessage:stack:exceptionId:)]) {
[_delegate updateJSExceptionWithMessage:message stack:stack exceptionId:exceptionId];
}
}
// Deprecated. Use reportFatalException directly instead.
@@ -120,4 +88,5 @@ RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
{
[self reportFatalException:message stack:stack exceptionId:@-1];
}
@end

View File

@@ -647,8 +647,9 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe
}
}
if (removedChildren.count != atIndices.count) {
RCTLogMustFix(@"removedChildren count (%tu) was not what we expected (%tu)",
removedChildren.count, atIndices.count);
NSString *message = [NSString stringWithFormat:@"removedChildren count (%tu) was not what we expected (%tu)",
removedChildren.count, atIndices.count];
RCTFatal(RCTErrorWithMessage(message));
}
return removedChildren;
}