1/* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 */ 7 8#import "ABI47_0_0RCTExceptionsManager.h" 9 10#import <ABI47_0_0FBReactNativeSpec/ABI47_0_0FBReactNativeSpec.h> 11#import <ABI47_0_0React/ABI47_0_0RCTConvert.h> 12#import <ABI47_0_0React/ABI47_0_0RCTDefines.h> 13#import <ABI47_0_0React/ABI47_0_0RCTLog.h> 14#import <ABI47_0_0React/ABI47_0_0RCTRedBox.h> 15#import <ABI47_0_0React/ABI47_0_0RCTReloadCommand.h> 16#import <ABI47_0_0React/ABI47_0_0RCTRootView.h> 17 18#import "ABI47_0_0CoreModulesPlugins.h" 19 20@interface ABI47_0_0RCTExceptionsManager () <ABI47_0_0NativeExceptionsManagerSpec> 21 22@end 23 24@implementation ABI47_0_0RCTExceptionsManager 25 26@synthesize moduleRegistry = _moduleRegistry; 27 28ABI47_0_0RCT_EXPORT_MODULE() 29 30- (instancetype)initWithDelegate:(id<ABI47_0_0RCTExceptionsManagerDelegate>)delegate 31{ 32 if ((self = [self init])) { 33 _delegate = delegate; 34 } 35 return self; 36} 37 38- (void)reportSoft:(NSString *)message 39 stack:(NSArray<NSDictionary *> *)stack 40 exceptionId:(double)exceptionId 41 extraDataAsJSON:(nullable NSString *)extraDataAsJSON 42{ 43 ABI47_0_0RCTRedBox *redbox = [_moduleRegistry moduleForName:"RedBox"]; 44 [redbox showErrorMessage:message withStack:stack errorCookie:(int)exceptionId]; 45 46 if (_delegate) { 47 [_delegate handleSoftJSExceptionWithMessage:message 48 stack:stack 49 exceptionId:[NSNumber numberWithDouble:exceptionId] 50 extraDataAsJSON:extraDataAsJSON]; 51 } 52} 53 54- (void)reportFatal:(NSString *)message 55 stack:(NSArray<NSDictionary *> *)stack 56 exceptionId:(double)exceptionId 57 extraDataAsJSON:(nullable NSString *)extraDataAsJSON 58{ 59 ABI47_0_0RCTRedBox *redbox = [_moduleRegistry moduleForName:"RedBox"]; 60 [redbox showErrorMessage:message withStack:stack errorCookie:(int)exceptionId]; 61 62 if (_delegate) { 63 [_delegate handleFatalJSExceptionWithMessage:message 64 stack:stack 65 exceptionId:[NSNumber numberWithDouble:exceptionId] 66 extraDataAsJSON:extraDataAsJSON]; 67 } 68 69 static NSUInteger reloadRetries = 0; 70 if (!ABI47_0_0RCT_DEBUG && reloadRetries < _maxReloadAttempts) { 71 reloadRetries++; 72 ABI47_0_0RCTTriggerReloadCommandListeners(@"JS Crash Reload"); 73 } else if (!ABI47_0_0RCT_DEV) { 74 NSString *description = [@"Unhandled JS Exception: " stringByAppendingString:message]; 75 NSDictionary *errorInfo = 76 @{NSLocalizedDescriptionKey : description, ABI47_0_0RCTJSStackTraceKey : stack, ABI47_0_0RCTJSExtraDataKey : extraDataAsJSON}; 77 ABI47_0_0RCTFatal([NSError errorWithDomain:ABI47_0_0RCTErrorDomain code:0 userInfo:errorInfo]); 78 } 79} 80 81ABI47_0_0RCT_EXPORT_METHOD(reportSoftException 82 : (NSString *)message stack 83 : (NSArray<NSDictionary *> *)stack exceptionId 84 : (double)exceptionId) 85{ 86 [self reportSoft:message stack:stack exceptionId:exceptionId extraDataAsJSON:nil]; 87} 88 89ABI47_0_0RCT_EXPORT_METHOD(reportFatalException 90 : (NSString *)message stack 91 : (NSArray<NSDictionary *> *)stack exceptionId 92 : (double)exceptionId) 93{ 94 [self reportFatal:message stack:stack exceptionId:exceptionId extraDataAsJSON:nil]; 95} 96 97ABI47_0_0RCT_EXPORT_METHOD(updateExceptionMessage 98 : (NSString *)message stack 99 : (NSArray<NSDictionary *> *)stack exceptionId 100 : (double)exceptionId) 101{ 102 ABI47_0_0RCTRedBox *redbox = [_moduleRegistry moduleForName:"RedBox"]; 103 [redbox updateErrorMessage:message withStack:stack errorCookie:(int)exceptionId]; 104 105 if (_delegate && [_delegate respondsToSelector:@selector(updateJSExceptionWithMessage:stack:exceptionId:)]) { 106 [_delegate updateJSExceptionWithMessage:message stack:stack exceptionId:[NSNumber numberWithDouble:exceptionId]]; 107 } 108} 109 110// Deprecated. Use reportFatalException directly instead. 111ABI47_0_0RCT_EXPORT_METHOD(reportUnhandledException : (NSString *)message stack : (NSArray<NSDictionary *> *)stack) 112{ 113 [self reportFatalException:message stack:stack exceptionId:-1]; 114} 115 116ABI47_0_0RCT_EXPORT_METHOD(dismissRedbox) {} 117 118ABI47_0_0RCT_EXPORT_METHOD(reportException : (ABI47_0_0JS::NativeExceptionsManager::ExceptionData &)data) 119{ 120 NSString *message = data.message(); 121 double exceptionId = data.id_(); 122 123 // Reserialize data.stack() into an array of untyped dictionaries. 124 // TODO: (moti) T53588496 Replace `(NSArray<NSDictionary *> *)stack` in 125 // reportFatalException etc with a typed interface. 126 NSMutableArray<NSDictionary *> *stackArray = [NSMutableArray<NSDictionary *> new]; 127 for (auto frame : data.stack()) { 128 NSMutableDictionary *frameDict = [NSMutableDictionary new]; 129 if (frame.column().has_value()) { 130 frameDict[@"column"] = @(frame.column().value()); 131 } 132 frameDict[@"file"] = frame.file(); 133 if (frame.lineNumber().has_value()) { 134 frameDict[@"lineNumber"] = @(frame.lineNumber().value()); 135 } 136 frameDict[@"methodName"] = frame.methodName(); 137 if (frame.collapse().has_value()) { 138 frameDict[@"collapse"] = @(frame.collapse().value()); 139 } 140 [stackArray addObject:frameDict]; 141 } 142 143 NSDictionary *extraData = (NSDictionary *)data.extraData(); 144 NSString *extraDataAsJSON = ABI47_0_0RCTJSONStringify(extraData, NULL); 145 146 if (data.isFatal()) { 147 [self reportFatal:message stack:stackArray exceptionId:exceptionId extraDataAsJSON:extraDataAsJSON]; 148 } else { 149 [self reportSoft:message stack:stackArray exceptionId:exceptionId extraDataAsJSON:extraDataAsJSON]; 150 } 151} 152 153- (void)reportJsException:(NSString *)errorStr 154{ 155 NSData *jsonData = [errorStr dataUsingEncoding:NSUTF8StringEncoding]; 156 NSError *jsonError; 157 NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData 158 options:NSJSONWritingPrettyPrinted 159 error:&jsonError]; 160 161 NSString *message = [dict objectForKey:@"message"]; 162 double exceptionId = [[dict objectForKey:@"id"] doubleValue]; 163 NSArray *stack = [dict objectForKey:@"stack"]; 164 BOOL isFatal = [[dict objectForKey:@"isFatal"] boolValue]; 165 166 if (isFatal) { 167 [self reportFatalException:message stack:stack exceptionId:exceptionId]; 168 } else { 169 [self reportSoftException:message stack:stack exceptionId:exceptionId]; 170 } 171} 172 173- (std::shared_ptr<ABI47_0_0facebook::ABI47_0_0React::TurboModule>)getTurboModule: 174 (const ABI47_0_0facebook::ABI47_0_0React::ObjCTurboModule::InitParams &)params 175{ 176 return std::make_shared<ABI47_0_0facebook::ABI47_0_0React::NativeExceptionsManagerSpecJSI>(params); 177} 178 179@end 180 181Class ABI47_0_0RCTExceptionsManagerCls(void) 182{ 183 return ABI47_0_0RCTExceptionsManager.class; 184} 185