iOS C++: Add ICxxExceptionManager to access RCTExceptionsManager native module
Summary: It is sometimes useful to report soft/fatal errors from C++ (native) to the same RCTExceptionsManager that is already handling JS exceptions. `ICxxExceptionManager` is an approach to provide such access, which impl provided for ObjC++. Reviewed By: shergin Differential Revision: D7224944 fbshipit-source-id: 8c607226b67851d46f4c787f5b6e6c8cb6a1afea
This commit is contained in:
parent
263d04d756
commit
f42b5892a1
|
@ -120,6 +120,13 @@ RCT_EXTERN void RCTPerformBlockWithLogFunction(void (^block)(void), RCTLogFuncti
|
||||||
*/
|
*/
|
||||||
RCT_EXTERN void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix);
|
RCT_EXTERN void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method computes the current call stack, useful for error reporting.
|
||||||
|
*/
|
||||||
|
RCT_EXTERN NSArray<NSDictionary *> *RCTGetCallStack(const char *fileName, int lineNumber);
|
||||||
|
|
||||||
|
#define RCT_CALLSTACK RCTGetCallStack(__FILE__, __LINE__)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private logging function - ignore this.
|
* Private logging function - ignore this.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -197,6 +197,41 @@ static NSRegularExpression *nativeStackFrameRegex()
|
||||||
return _regex;
|
return _regex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSArray<NSDictionary *>* RCTGetCallStack(const char *fileName, int lineNumber)
|
||||||
|
{
|
||||||
|
NSArray<NSString *> *stackSymbols = [NSThread callStackSymbols];
|
||||||
|
NSMutableArray<NSDictionary *> *stack =
|
||||||
|
[NSMutableArray arrayWithCapacity:(stackSymbols.count - 1)];
|
||||||
|
[stackSymbols enumerateObjectsUsingBlock:^(NSString *frameSymbols, NSUInteger idx, __unused BOOL *stop) {
|
||||||
|
if (idx == 0) {
|
||||||
|
// don't include the current frame
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSRange range = NSMakeRange(0, frameSymbols.length);
|
||||||
|
NSTextCheckingResult *match = [nativeStackFrameRegex() firstMatchInString:frameSymbols options:0 range:range];
|
||||||
|
if (!match) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *methodName = [frameSymbols substringWithRange:[match rangeAtIndex:1]];
|
||||||
|
char *demangledName = abi::__cxa_demangle([methodName UTF8String], NULL, NULL, NULL);
|
||||||
|
if (demangledName) {
|
||||||
|
methodName = @(demangledName);
|
||||||
|
free(demangledName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx == 1 && fileName) {
|
||||||
|
NSString *file = [@(fileName) componentsSeparatedByString:@"/"].lastObject;
|
||||||
|
[stack addObject:@{@"methodName": methodName, @"file": file, @"lineNumber": @(lineNumber)}];
|
||||||
|
} else {
|
||||||
|
[stack addObject:@{@"methodName": methodName}];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
return [stack copy];
|
||||||
|
}
|
||||||
|
|
||||||
void _RCTLogNativeInternal(RCTLogLevel level, const char *fileName, int lineNumber, NSString *format, ...)
|
void _RCTLogNativeInternal(RCTLogLevel level, const char *fileName, int lineNumber, NSString *format, ...)
|
||||||
{
|
{
|
||||||
RCTLogFunction logFunction = RCTGetLocalLogFunction();
|
RCTLogFunction logFunction = RCTGetLocalLogFunction();
|
||||||
|
@ -217,35 +252,7 @@ void _RCTLogNativeInternal(RCTLogLevel level, const char *fileName, int lineNumb
|
||||||
|
|
||||||
// Log to red box in debug mode.
|
// Log to red box in debug mode.
|
||||||
if (RCTSharedApplication() && level >= RCTLOG_REDBOX_LEVEL) {
|
if (RCTSharedApplication() && level >= RCTLOG_REDBOX_LEVEL) {
|
||||||
NSArray<NSString *> *stackSymbols = [NSThread callStackSymbols];
|
NSArray<NSDictionary *> *stack = RCT_CALLSTACK;
|
||||||
NSMutableArray<NSDictionary *> *stack =
|
|
||||||
[NSMutableArray arrayWithCapacity:(stackSymbols.count - 1)];
|
|
||||||
[stackSymbols enumerateObjectsUsingBlock:^(NSString *frameSymbols, NSUInteger idx, __unused BOOL *stop) {
|
|
||||||
if (idx == 0) {
|
|
||||||
// don't include the current frame
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSRange range = NSMakeRange(0, frameSymbols.length);
|
|
||||||
NSTextCheckingResult *match = [nativeStackFrameRegex() firstMatchInString:frameSymbols options:0 range:range];
|
|
||||||
if (!match) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString *methodName = [frameSymbols substringWithRange:[match rangeAtIndex:1]];
|
|
||||||
char *demangledName = abi::__cxa_demangle([methodName UTF8String], NULL, NULL, NULL);
|
|
||||||
if (demangledName) {
|
|
||||||
methodName = @(demangledName);
|
|
||||||
free(demangledName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (idx == 1 && fileName) {
|
|
||||||
NSString *file = [@(fileName) componentsSeparatedByString:@"/"].lastObject;
|
|
||||||
[stack addObject:@{@"methodName": methodName, @"file": file, @"lineNumber": @(lineNumber)}];
|
|
||||||
} else {
|
|
||||||
[stack addObject:@{@"methodName": methodName}];
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
// red box is thread safe, but by deferring to main queue we avoid a startup
|
// red box is thread safe, but by deferring to main queue we avoid a startup
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cxxreact/ExceptionManager.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connector class (from C++ to ObjC++) to allow FabricUIManager to invoke native UI operations/updates.
|
||||||
|
* UIKit-related impl doesn't live here, but this class gets passed to the FabricUIManager C++ impl directly.
|
||||||
|
*/
|
||||||
|
class RCTCxxExceptionManager : public ExceptionManager {
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void handleSoftException(const std::exception& e) const override;
|
||||||
|
virtual void handleFatalException(const std::exception& e) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace react
|
||||||
|
} // namespace facebook
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "RCTCxxExceptionManager.h"
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import <React/RCTBridge.h>
|
||||||
|
#import <React/RCTBridge+Private.h>
|
||||||
|
#import <React/RCTExceptionsManager.h>
|
||||||
|
#import <React/RCTLog.h>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
void RCTCxxExceptionManager::handleSoftException(const std::exception& e) const {
|
||||||
|
RCTExceptionsManager *manager = [[RCTBridge currentBridge] moduleForClass:[RCTExceptionsManager class]];
|
||||||
|
[manager reportSoftException:[NSString stringWithUTF8String:e.what()] stack:RCT_CALLSTACK exceptionId:@0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void RCTCxxExceptionManager::handleFatalException(const std::exception& e) const {
|
||||||
|
RCTExceptionsManager *manager = [[RCTBridge currentBridge] moduleForClass:[RCTExceptionsManager class]];
|
||||||
|
[manager reportFatalException:[NSString stringWithUTF8String:e.what()] stack:RCT_CALLSTACK exceptionId:@0];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace react
|
||||||
|
} // namespace facebook
|
|
@ -23,6 +23,9 @@
|
||||||
|
|
||||||
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate;
|
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate;
|
||||||
|
|
||||||
|
- (void)reportSoftException:(NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(nonnull NSNumber *)exceptionId;
|
||||||
|
- (void)reportFatalException:(NSString *)message stack:(NSArray<NSDictionary *> *)stack exceptionId:(nonnull NSNumber *)exceptionId;
|
||||||
|
|
||||||
@property (nonatomic, weak) id<RCTExceptionsManagerDelegate> delegate;
|
@property (nonatomic, weak) id<RCTExceptionsManagerDelegate> delegate;
|
||||||
|
|
||||||
@property (nonatomic, assign) NSUInteger maxReloadAttempts;
|
@property (nonatomic, assign) NSUInteger maxReloadAttempts;
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
load("//configurations/buck/apple:flag_defs.bzl", "get_debug_preprocessor_flags")
|
||||||
|
load("//ReactNative:DEFS.bzl", "IS_OSS_BUILD", "react_native_xplat_target", "rn_xplat_cxx_library", "APPLE_INSPECTOR_FLAGS")
|
||||||
|
|
||||||
|
APPLE_COMPILER_FLAGS = []
|
||||||
|
|
||||||
|
if not IS_OSS_BUILD:
|
||||||
|
load("@xplat//configurations/buck/apple:flag_defs.bzl", "get_static_library_ios_flags", "flags")
|
||||||
|
APPLE_COMPILER_FLAGS = flags.get_flag_value(get_static_library_ios_flags(), 'compiler_flags')
|
||||||
|
|
||||||
|
rn_xplat_cxx_library(
|
||||||
|
name = "exceptions",
|
||||||
|
srcs = glob(["*.cpp"]),
|
||||||
|
header_namespace = "",
|
||||||
|
exported_headers = subdir_glob(
|
||||||
|
[
|
||||||
|
("", "*.h"),
|
||||||
|
],
|
||||||
|
prefix = "cxxreact",
|
||||||
|
),
|
||||||
|
compiler_flags = [
|
||||||
|
"-fexceptions",
|
||||||
|
"-frtti",
|
||||||
|
"-std=c++14",
|
||||||
|
"-Wall",
|
||||||
|
],
|
||||||
|
fbobjc_compiler_flags = APPLE_COMPILER_FLAGS,
|
||||||
|
fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + APPLE_INSPECTOR_FLAGS,
|
||||||
|
force_static = True,
|
||||||
|
preprocessor_flags = [
|
||||||
|
"-DLOG_TAG=\"ReactNative\"",
|
||||||
|
"-DWITH_FBSYSTRACE=1",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"PUBLIC",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"xplat//fbsystrace:fbsystrace",
|
||||||
|
"xplat//third-party/glog:glog",
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
namespace facebook {
|
||||||
|
namespace react {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract class for C++-based exception handling that differentiates
|
||||||
|
* soft exceptions from fatal exceptions.
|
||||||
|
*/
|
||||||
|
class ExceptionManager {
|
||||||
|
public:
|
||||||
|
virtual void handleSoftException(const std::exception &e) const = 0;
|
||||||
|
virtual void handleFatalException(const std::exception &e) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace react
|
||||||
|
} // namespace facebook
|
Loading…
Reference in New Issue