react-native-macos/React/Base/RCTAssert.h

217 строки
8.7 KiB
C
Исходник Обычный вид История

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import <React/RCTDefines.h>
/*
* Defined in RCTUtils.m
*/
RCT_EXTERN BOOL RCTIsMainQueue(void);
2015-04-12 01:08:00 +03:00
/**
* This is the main assert macro that you should use. Asserts should be compiled out
* in production builds. You can customize the assert behaviour by setting a custom
* assert handler through `RCTSetAssertFunction`.
2015-04-12 01:08:00 +03:00
*/
#ifndef NS_BLOCK_ASSERTIONS
#define RCTAssert(condition, ...) \
do { \
if ((condition) == 0) { \
_RCTAssertFormat(#condition, __FILE__, __LINE__, __func__, __VA_ARGS__); \
if (RCT_NSASSERT) { \
[[NSAssertionHandler currentHandler] handleFailureInFunction:(NSString *_Nonnull)@(__func__) \
file:(NSString *_Nonnull)@(__FILE__) \
lineNumber:__LINE__ \
description:__VA_ARGS__]; \
} \
} \
} while (false)
#else
#define RCTAssert(condition, ...) \
do { \
} while (false)
#endif
RCT_EXTERN void _RCTAssertFormat(const char *, const char *, int, const char *, NSString *, ...)
NS_FORMAT_FUNCTION(5, 6);
/**
* Report a fatal condition when executing. These calls will _NOT_ be compiled out
* in production, and crash the app by default. You can customize the fatal behaviour
Crash reporting heaven (#23691) Summary: <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> I have used RN for a long time, and for all this time, crash reporting has been less great than native development crash reporting. At some point, companies like sentry, bugsnag and a bunch of others started supporting sourcemaps for js crashes in RN, which helped a lot. But native crashes were (and still are) much harder to diagnose. ..Until now :D I have make a repo of a sample RN app, included this PR in it, and some code and screenshots to help. The repo is [here](https://github.com/pvinis/react-native-project-with-crash-heaven-pr). I was trying to get good crash reports from native crashes in iOS for a looong time. I spoke with people in sentry, in bugsnag and more, and I could not get this solved. There was no clear way to get the **native** crashed to display correctly. I made two repos here, one for [sentry](https://github.com/pvinis/SentryBadStack) and one for [bugsnag](https://github.com/pvinis/BugsnagBadStack), demonstrating the correct js handling and the bad native handling. After all this, and talks with their support, twitter etc, I investigated further, on **why** this was happening. I thought there must be some reason that native crashes look bad in all the tools, and in the same way. Maybe it's not their fault, or up to them to fix it, or maybe they didn't have the experience to fix it. In a test project I created, I checked what's up with the `RCTFatalException`, and I found out that the React Native code is catching the `NSException`s that come from any native modules of a RN app and converting it into an string and sending it to `RCTFatal` that created an `NSError` out of that string. Then it checks if the app has set a fatal error handler and if not, goes ahead and throws that `NSError`. The problem here is that `NSException` has a bunch more info that the resulting `NSError` is missing or is altering. Turning the callstack into a string renders crash reporting tools useless as they are missing the original place the exception was thrown, symbols, return addresses etc. In both repos above it can be seen that both tools were thinking that the error happened somewhere in the `RCTFatal` function, and it did, since we create it there, losing all the previous useful info of the original exception. That leaves us with just a very long name including a callstack, but very hard to actually map this to the code and dsym. I added a fatal exception handler, that mirrors the fatal error handler, as the error handler is used around React Native internal code. Then I stopped making a string out of the original `NSException` and calling `RCTFatal`, and I simply throw the exception. This way no info is lost! Finally, I added some code examples of native and js crashes and added a part in the `RNTester` app, so people can see how a js and a native error look like while debugging, as well as try to compile the app in release mode and see how the crash report would look like if they connect it to bugsnag or sentry or their tool of choice. I have attached some images at the bottom of this PR, and you can find some in the 3 repos I linked above. [iOS] [Fixed] - Changed the way iOS native module exceptions get handled. Instead of making them into an `NSError` and lose the context and callstack, we keep them as `NSException`s and propagate them. [General] [Added] - Example code for native crashes in iOS and Android, with buttons on RNTester, so developers can see how these look when debugging, as well as the crash reports in release mode. Pull Request resolved: https://github.com/facebook/react-native/pull/23691 Reviewed By: fkgozali Differential Revision: D14276366 Pulled By: cpojer fbshipit-source-id: b308d5608e1432d7676447347ae77c0721094e62
2019-03-13 05:24:07 +03:00
* by setting a custom fatal handler through `RCTSetFatalHandler` and
* `RCTSetFatalExceptionHandler`.
*/
RCT_EXTERN void RCTFatal(NSError *error);
Crash reporting heaven (#23691) Summary: <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> I have used RN for a long time, and for all this time, crash reporting has been less great than native development crash reporting. At some point, companies like sentry, bugsnag and a bunch of others started supporting sourcemaps for js crashes in RN, which helped a lot. But native crashes were (and still are) much harder to diagnose. ..Until now :D I have make a repo of a sample RN app, included this PR in it, and some code and screenshots to help. The repo is [here](https://github.com/pvinis/react-native-project-with-crash-heaven-pr). I was trying to get good crash reports from native crashes in iOS for a looong time. I spoke with people in sentry, in bugsnag and more, and I could not get this solved. There was no clear way to get the **native** crashed to display correctly. I made two repos here, one for [sentry](https://github.com/pvinis/SentryBadStack) and one for [bugsnag](https://github.com/pvinis/BugsnagBadStack), demonstrating the correct js handling and the bad native handling. After all this, and talks with their support, twitter etc, I investigated further, on **why** this was happening. I thought there must be some reason that native crashes look bad in all the tools, and in the same way. Maybe it's not their fault, or up to them to fix it, or maybe they didn't have the experience to fix it. In a test project I created, I checked what's up with the `RCTFatalException`, and I found out that the React Native code is catching the `NSException`s that come from any native modules of a RN app and converting it into an string and sending it to `RCTFatal` that created an `NSError` out of that string. Then it checks if the app has set a fatal error handler and if not, goes ahead and throws that `NSError`. The problem here is that `NSException` has a bunch more info that the resulting `NSError` is missing or is altering. Turning the callstack into a string renders crash reporting tools useless as they are missing the original place the exception was thrown, symbols, return addresses etc. In both repos above it can be seen that both tools were thinking that the error happened somewhere in the `RCTFatal` function, and it did, since we create it there, losing all the previous useful info of the original exception. That leaves us with just a very long name including a callstack, but very hard to actually map this to the code and dsym. I added a fatal exception handler, that mirrors the fatal error handler, as the error handler is used around React Native internal code. Then I stopped making a string out of the original `NSException` and calling `RCTFatal`, and I simply throw the exception. This way no info is lost! Finally, I added some code examples of native and js crashes and added a part in the `RNTester` app, so people can see how a js and a native error look like while debugging, as well as try to compile the app in release mode and see how the crash report would look like if they connect it to bugsnag or sentry or their tool of choice. I have attached some images at the bottom of this PR, and you can find some in the 3 repos I linked above. [iOS] [Fixed] - Changed the way iOS native module exceptions get handled. Instead of making them into an `NSError` and lose the context and callstack, we keep them as `NSException`s and propagate them. [General] [Added] - Example code for native crashes in iOS and Android, with buttons on RNTester, so developers can see how these look when debugging, as well as the crash reports in release mode. Pull Request resolved: https://github.com/facebook/react-native/pull/23691 Reviewed By: fkgozali Differential Revision: D14276366 Pulled By: cpojer fbshipit-source-id: b308d5608e1432d7676447347ae77c0721094e62
2019-03-13 05:24:07 +03:00
RCT_EXTERN void RCTFatalException(NSException *exception);
/**
* The default error domain to be used for React errors.
*/
RCT_EXTERN NSString *const RCTErrorDomain;
/**
* JS Stack trace provided as part of an NSError's userInfo
*/
RCT_EXTERN NSString *const RCTJSStackTraceKey;
/**
* Raw JS Stack trace string provided as part of an NSError's userInfo
*/
RCT_EXTERN NSString *const RCTJSRawStackTraceKey;
/**
* Objective-C stack trace string provided as part of an NSError's userInfo
*/
RCT_EXTERN NSString *const RCTObjCStackTraceKey;
/**
* Name of fatal exceptions generated by RCTFatal
*/
RCT_EXTERN NSString *const RCTFatalExceptionName;
/**
* Stringified JSON object containing extra data to attach to the error from JavaScript.
*/
RCT_EXTERN NSString *const RCTJSExtraDataKey;
/**
* A block signature to be used for custom assertion handling.
*/
typedef void (^RCTAssertFunction)(
NSString *condition,
NSString *fileName,
NSNumber *lineNumber,
NSString *function,
NSString *message);
typedef void (^RCTFatalHandler)(NSError *error);
Crash reporting heaven (#23691) Summary: <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> I have used RN for a long time, and for all this time, crash reporting has been less great than native development crash reporting. At some point, companies like sentry, bugsnag and a bunch of others started supporting sourcemaps for js crashes in RN, which helped a lot. But native crashes were (and still are) much harder to diagnose. ..Until now :D I have make a repo of a sample RN app, included this PR in it, and some code and screenshots to help. The repo is [here](https://github.com/pvinis/react-native-project-with-crash-heaven-pr). I was trying to get good crash reports from native crashes in iOS for a looong time. I spoke with people in sentry, in bugsnag and more, and I could not get this solved. There was no clear way to get the **native** crashed to display correctly. I made two repos here, one for [sentry](https://github.com/pvinis/SentryBadStack) and one for [bugsnag](https://github.com/pvinis/BugsnagBadStack), demonstrating the correct js handling and the bad native handling. After all this, and talks with their support, twitter etc, I investigated further, on **why** this was happening. I thought there must be some reason that native crashes look bad in all the tools, and in the same way. Maybe it's not their fault, or up to them to fix it, or maybe they didn't have the experience to fix it. In a test project I created, I checked what's up with the `RCTFatalException`, and I found out that the React Native code is catching the `NSException`s that come from any native modules of a RN app and converting it into an string and sending it to `RCTFatal` that created an `NSError` out of that string. Then it checks if the app has set a fatal error handler and if not, goes ahead and throws that `NSError`. The problem here is that `NSException` has a bunch more info that the resulting `NSError` is missing or is altering. Turning the callstack into a string renders crash reporting tools useless as they are missing the original place the exception was thrown, symbols, return addresses etc. In both repos above it can be seen that both tools were thinking that the error happened somewhere in the `RCTFatal` function, and it did, since we create it there, losing all the previous useful info of the original exception. That leaves us with just a very long name including a callstack, but very hard to actually map this to the code and dsym. I added a fatal exception handler, that mirrors the fatal error handler, as the error handler is used around React Native internal code. Then I stopped making a string out of the original `NSException` and calling `RCTFatal`, and I simply throw the exception. This way no info is lost! Finally, I added some code examples of native and js crashes and added a part in the `RNTester` app, so people can see how a js and a native error look like while debugging, as well as try to compile the app in release mode and see how the crash report would look like if they connect it to bugsnag or sentry or their tool of choice. I have attached some images at the bottom of this PR, and you can find some in the 3 repos I linked above. [iOS] [Fixed] - Changed the way iOS native module exceptions get handled. Instead of making them into an `NSError` and lose the context and callstack, we keep them as `NSException`s and propagate them. [General] [Added] - Example code for native crashes in iOS and Android, with buttons on RNTester, so developers can see how these look when debugging, as well as the crash reports in release mode. Pull Request resolved: https://github.com/facebook/react-native/pull/23691 Reviewed By: fkgozali Differential Revision: D14276366 Pulled By: cpojer fbshipit-source-id: b308d5608e1432d7676447347ae77c0721094e62
2019-03-13 05:24:07 +03:00
typedef void (^RCTFatalExceptionHandler)(NSException *exception);
/**
* Convenience macro for asserting that a parameter is non-nil/non-zero.
*/
#define RCTAssertParam(name) RCTAssert(name, @"'%s' is a required parameter", #name)
2015-04-12 01:08:00 +03:00
/**
* Convenience macro for asserting that we're running on main queue.
2015-04-12 01:08:00 +03:00
*/
#define RCTAssertMainQueue() RCTAssert(RCTIsMainQueue(), @"This function must be called on the main queue")
2015-04-12 01:08:00 +03:00
/**
* Convenience macro for asserting that we're running off the main queue.
*/
#define RCTAssertNotMainQueue() RCTAssert(!RCTIsMainQueue(), @"This function must not be called on the main queue")
2015-04-12 01:08:00 +03:00
/**
* These methods get and set the current assert function called by the RCTAssert
* macros. You can use these to replace the standard behavior with custom assert
2015-04-12 01:08:00 +03:00
* functionality.
*/
2015-04-21 15:26:51 +03:00
RCT_EXTERN void RCTSetAssertFunction(RCTAssertFunction assertFunction);
RCT_EXTERN RCTAssertFunction RCTGetAssertFunction(void);
2015-04-12 01:08:00 +03:00
/**
* This appends additional code to the existing assert function, without
* replacing the existing functionality. Useful if you just want to forward
* assert info to an extra service without changing the default behavior.
*/
2015-04-21 15:26:51 +03:00
RCT_EXTERN void RCTAddAssertFunction(RCTAssertFunction assertFunction);
2015-08-07 16:06:17 +03:00
/**
* This method temporarily overrides the assert function while performing the
* specified block. This is useful for testing purposes (to detect if a given
* function asserts something) or to suppress or override assertions temporarily.
*/
RCT_EXTERN void RCTPerformBlockWithAssertFunction(void (^block)(void), RCTAssertFunction assertFunction);
/**
Crash reporting heaven (#23691) Summary: <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> I have used RN for a long time, and for all this time, crash reporting has been less great than native development crash reporting. At some point, companies like sentry, bugsnag and a bunch of others started supporting sourcemaps for js crashes in RN, which helped a lot. But native crashes were (and still are) much harder to diagnose. ..Until now :D I have make a repo of a sample RN app, included this PR in it, and some code and screenshots to help. The repo is [here](https://github.com/pvinis/react-native-project-with-crash-heaven-pr). I was trying to get good crash reports from native crashes in iOS for a looong time. I spoke with people in sentry, in bugsnag and more, and I could not get this solved. There was no clear way to get the **native** crashed to display correctly. I made two repos here, one for [sentry](https://github.com/pvinis/SentryBadStack) and one for [bugsnag](https://github.com/pvinis/BugsnagBadStack), demonstrating the correct js handling and the bad native handling. After all this, and talks with their support, twitter etc, I investigated further, on **why** this was happening. I thought there must be some reason that native crashes look bad in all the tools, and in the same way. Maybe it's not their fault, or up to them to fix it, or maybe they didn't have the experience to fix it. In a test project I created, I checked what's up with the `RCTFatalException`, and I found out that the React Native code is catching the `NSException`s that come from any native modules of a RN app and converting it into an string and sending it to `RCTFatal` that created an `NSError` out of that string. Then it checks if the app has set a fatal error handler and if not, goes ahead and throws that `NSError`. The problem here is that `NSException` has a bunch more info that the resulting `NSError` is missing or is altering. Turning the callstack into a string renders crash reporting tools useless as they are missing the original place the exception was thrown, symbols, return addresses etc. In both repos above it can be seen that both tools were thinking that the error happened somewhere in the `RCTFatal` function, and it did, since we create it there, losing all the previous useful info of the original exception. That leaves us with just a very long name including a callstack, but very hard to actually map this to the code and dsym. I added a fatal exception handler, that mirrors the fatal error handler, as the error handler is used around React Native internal code. Then I stopped making a string out of the original `NSException` and calling `RCTFatal`, and I simply throw the exception. This way no info is lost! Finally, I added some code examples of native and js crashes and added a part in the `RNTester` app, so people can see how a js and a native error look like while debugging, as well as try to compile the app in release mode and see how the crash report would look like if they connect it to bugsnag or sentry or their tool of choice. I have attached some images at the bottom of this PR, and you can find some in the 3 repos I linked above. [iOS] [Fixed] - Changed the way iOS native module exceptions get handled. Instead of making them into an `NSError` and lose the context and callstack, we keep them as `NSException`s and propagate them. [General] [Added] - Example code for native crashes in iOS and Android, with buttons on RNTester, so developers can see how these look when debugging, as well as the crash reports in release mode. Pull Request resolved: https://github.com/facebook/react-native/pull/23691 Reviewed By: fkgozali Differential Revision: D14276366 Pulled By: cpojer fbshipit-source-id: b308d5608e1432d7676447347ae77c0721094e62
2019-03-13 05:24:07 +03:00
* These methods get and set the current fatal handler called by the `RCTFatal`
* and `RCTFatalException` methods.
*/
RCT_EXTERN void RCTSetFatalHandler(RCTFatalHandler fatalHandler);
RCT_EXTERN RCTFatalHandler RCTGetFatalHandler(void);
Crash reporting heaven (#23691) Summary: <!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? --> I have used RN for a long time, and for all this time, crash reporting has been less great than native development crash reporting. At some point, companies like sentry, bugsnag and a bunch of others started supporting sourcemaps for js crashes in RN, which helped a lot. But native crashes were (and still are) much harder to diagnose. ..Until now :D I have make a repo of a sample RN app, included this PR in it, and some code and screenshots to help. The repo is [here](https://github.com/pvinis/react-native-project-with-crash-heaven-pr). I was trying to get good crash reports from native crashes in iOS for a looong time. I spoke with people in sentry, in bugsnag and more, and I could not get this solved. There was no clear way to get the **native** crashed to display correctly. I made two repos here, one for [sentry](https://github.com/pvinis/SentryBadStack) and one for [bugsnag](https://github.com/pvinis/BugsnagBadStack), demonstrating the correct js handling and the bad native handling. After all this, and talks with their support, twitter etc, I investigated further, on **why** this was happening. I thought there must be some reason that native crashes look bad in all the tools, and in the same way. Maybe it's not their fault, or up to them to fix it, or maybe they didn't have the experience to fix it. In a test project I created, I checked what's up with the `RCTFatalException`, and I found out that the React Native code is catching the `NSException`s that come from any native modules of a RN app and converting it into an string and sending it to `RCTFatal` that created an `NSError` out of that string. Then it checks if the app has set a fatal error handler and if not, goes ahead and throws that `NSError`. The problem here is that `NSException` has a bunch more info that the resulting `NSError` is missing or is altering. Turning the callstack into a string renders crash reporting tools useless as they are missing the original place the exception was thrown, symbols, return addresses etc. In both repos above it can be seen that both tools were thinking that the error happened somewhere in the `RCTFatal` function, and it did, since we create it there, losing all the previous useful info of the original exception. That leaves us with just a very long name including a callstack, but very hard to actually map this to the code and dsym. I added a fatal exception handler, that mirrors the fatal error handler, as the error handler is used around React Native internal code. Then I stopped making a string out of the original `NSException` and calling `RCTFatal`, and I simply throw the exception. This way no info is lost! Finally, I added some code examples of native and js crashes and added a part in the `RNTester` app, so people can see how a js and a native error look like while debugging, as well as try to compile the app in release mode and see how the crash report would look like if they connect it to bugsnag or sentry or their tool of choice. I have attached some images at the bottom of this PR, and you can find some in the 3 repos I linked above. [iOS] [Fixed] - Changed the way iOS native module exceptions get handled. Instead of making them into an `NSError` and lose the context and callstack, we keep them as `NSException`s and propagate them. [General] [Added] - Example code for native crashes in iOS and Android, with buttons on RNTester, so developers can see how these look when debugging, as well as the crash reports in release mode. Pull Request resolved: https://github.com/facebook/react-native/pull/23691 Reviewed By: fkgozali Differential Revision: D14276366 Pulled By: cpojer fbshipit-source-id: b308d5608e1432d7676447347ae77c0721094e62
2019-03-13 05:24:07 +03:00
RCT_EXTERN void RCTSetFatalExceptionHandler(RCTFatalExceptionHandler fatalExceptionHandler);
RCT_EXTERN RCTFatalExceptionHandler RCTGetFatalExceptionHandler(void);
/**
* Get the current thread's name (or the current queue, if in debug mode)
*/
RCT_EXTERN NSString *RCTCurrentThreadName(void);
/**
* Helper to get generate exception message from NSError
*/
RCT_EXTERN NSString *
RCTFormatError(NSString *message, NSArray<NSDictionary<NSString *, id> *> *stacktrace, NSUInteger maxMessageLength);
/**
* Formats a JS stack trace for logging.
*/
RCT_EXTERN NSString *RCTFormatStackTrace(NSArray<NSDictionary<NSString *, id> *> *stackTrace);
/**
* Convenience macro to assert which thread is currently running (DEBUG mode only)
*/
#if DEBUG
#define RCTAssertThread(thread, format...) \
_Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") RCTAssert( \
[(id)thread isKindOfClass:[NSString class]] ? [RCTCurrentThreadName() isEqualToString:(NSString *)thread] \
: [(id)thread isKindOfClass:[NSThread class]] ? [NSThread currentThread] == (NSThread *)thread \
: dispatch_get_current_queue() == (dispatch_queue_t)thread, \
format); \
_Pragma("clang diagnostic pop")
#else
#define RCTAssertThread(thread, format...) \
do { \
} while (0)
#endif
// MARK: - New Architecture Validation
typedef enum {
RCTNotAllowedInBridgeless = 1,
RCTNotAllowedInFabricWithoutLegacy = 2,
RCTNotAllowedValidationDisabled = 3,
} RCTNotAllowedValidation;
/**
* // TODO: (T125626909) Only validate legacy architecture usages in Bridgeless mode, not Bridged Fabric mode
*
* Ensure runtime assumptions holds for the new architecture by reporting when assumptions are violated.
* Note: this is work in progress.
*
* When level is RCTNotAllowedInFabricWithoutLegacy, validate Fabric assumptions.
* i.e. Report legacy pre-Fabric call sites that should not be used while Fabric is enabled on all surfaces.
*
* When level is RCTNotAllowedInBridgeless, validate Fabric or Bridgeless assumptions.
* i.e. Report Bridge call sites that should not be used while Bridgeless mode is enabled.
*
* Note: enabling this at runtime is not early enough to report issues within ObjC class +load execution.
*/
__attribute__((used)) RCT_EXTERN void RCTNewArchitectureSetMinValidationLevel(RCTNotAllowedValidation level);
// When new architecture validation reporting is enabled, trigger an assertion and crash.
__attribute__((used)) RCT_EXTERN void
RCTEnforceNewArchitectureValidation(RCTNotAllowedValidation type, id context, NSString *extra);
// When new architecture validation reporting is enabled, trigger an error but do not crash.
// When ready, switch to stricter variant above.
__attribute__((used)) RCT_EXTERN void
RCTErrorNewArchitectureValidation(RCTNotAllowedValidation type, id context, NSString *extra);
// When new architecture validation reporting is enabled, log an message.
// When ready, switch to stricter variant above.
__attribute__((used)) RCT_EXTERN void
RCTLogNewArchitectureValidation(RCTNotAllowedValidation type, id context, NSString *extra);
// A placeholder for callsites that frequently fail validation.
// When ready, switch to stricter variant above.
__attribute__((used)) RCT_EXTERN void
RCTNewArchitectureValidationPlaceholder(RCTNotAllowedValidation type, id context, NSString *extra);