Fix RCTAssert logic

This commit is contained in:
Nick Lockwood 2015-08-07 06:06:17 -07:00
parent 1d452f3e4d
commit 3cef3010e6
2 changed files with 70 additions and 28 deletions

View File

@ -14,13 +14,13 @@
/** /**
* The default error domain to be used for React errors. * The default error domain to be used for React errors.
*/ */
extern NSString *const RCTErrorDomain; RCT_EXTERN NSString *const RCTErrorDomain;
/** /**
* A block signature to be used for custom assertion handling. * A block signature to be used for custom assertion handling.
*/ */
typedef void (^RCTAssertFunction)( typedef void (^RCTAssertFunction)(
BOOL condition, NSString *condition,
NSString *fileName, NSString *fileName,
NSNumber *lineNumber, NSNumber *lineNumber,
NSString *function, NSString *function,
@ -30,12 +30,18 @@ typedef void (^RCTAssertFunction)(
/** /**
* This is the main assert macro that you should use. * This is the main assert macro that you should use.
*/ */
#define RCTAssert(condition, ...) do { BOOL pass = ((condition) != 0); \ #define RCTAssert(condition, ...) do { \
if (RCT_NSASSERT && !pass) { [[NSAssertionHandler currentHandler] handleFailureInFunction:@(__func__) \ if ((condition) == 0) { \
file:@(__FILE__) lineNumber:__LINE__ description:__VA_ARGS__]; } \ _RCTAssertFormat(#condition, __FILE__, __LINE__, __func__, __VA_ARGS__); \
_RCTAssertFormat(pass, __FILE__, __LINE__, __func__, __VA_ARGS__); \ if (RCT_NSASSERT) { \
[[NSAssertionHandler currentHandler] handleFailureInFunction:@(__func__) \
file:@(__FILE__) lineNumber:__LINE__ description:__VA_ARGS__]; \
} \
} \
} while (false) } while (false)
RCT_EXTERN void _RCTAssertFormat(BOOL, const char *, int, const char *, NSString *, ...) NS_FORMAT_FUNCTION(5,6); RCT_EXTERN void _RCTAssertFormat(
const char *, const char *, int, const char *, NSString *, ...
) NS_FORMAT_FUNCTION(5,6);
/** /**
* Convenience macro for asserting that a parameter is non-nil/non-zero. * Convenience macro for asserting that a parameter is non-nil/non-zero.
@ -64,6 +70,13 @@ RCT_EXTERN RCTAssertFunction RCTGetAssertFunction(void);
*/ */
RCT_EXTERN void RCTAddAssertFunction(RCTAssertFunction assertFunction); RCT_EXTERN void RCTAddAssertFunction(RCTAssertFunction assertFunction);
/**
* 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);
/** /**
* Get the current thread's name (or the current queue, if in debug mode) * Get the current thread's name (or the current queue, if in debug mode)
*/ */

View File

@ -11,6 +11,8 @@
NSString *const RCTErrorDomain = @"RCTErrorDomain"; NSString *const RCTErrorDomain = @"RCTErrorDomain";
static NSString *const RCTAssertFunctionStack = @"RCTAssertFunctionStack";
RCTAssertFunction RCTCurrentAssertFunction = nil; RCTAssertFunction RCTCurrentAssertFunction = nil;
NSException *_RCTNotImplementedException(SEL, Class); NSException *_RCTNotImplementedException(SEL, Class);
@ -22,26 +24,6 @@ NSException *_RCTNotImplementedException(SEL cmd, Class cls)
reason:msg userInfo:nil]; reason:msg userInfo:nil];
} }
void _RCTAssertFormat(
BOOL condition,
const char *fileName,
int lineNumber,
const char *function,
NSString *format, ...)
{
if (RCTCurrentAssertFunction) {
va_list args;
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
RCTCurrentAssertFunction(
condition, @(fileName), @(lineNumber), @(function), message
);
}
}
void RCTSetAssertFunction(RCTAssertFunction assertFunction) void RCTSetAssertFunction(RCTAssertFunction assertFunction)
{ {
RCTCurrentAssertFunction = assertFunction; RCTCurrentAssertFunction = assertFunction;
@ -56,7 +38,7 @@ void RCTAddAssertFunction(RCTAssertFunction assertFunction)
{ {
RCTAssertFunction existing = RCTCurrentAssertFunction; RCTAssertFunction existing = RCTCurrentAssertFunction;
if (existing) { if (existing) {
RCTCurrentAssertFunction = ^(BOOL condition, RCTCurrentAssertFunction = ^(NSString *condition,
NSString *fileName, NSString *fileName,
NSNumber *lineNumber, NSNumber *lineNumber,
NSString *function, NSString *function,
@ -70,6 +52,34 @@ void RCTAddAssertFunction(RCTAssertFunction assertFunction)
} }
} }
/**
* returns the topmost stacked assert function for the current thread, which
* may not be the same as the current value of RCTCurrentAssertFunction.
*/
static RCTAssertFunction RCTGetLocalAssertFunction()
{
NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary;
NSArray *functionStack = threadDictionary[RCTAssertFunctionStack];
RCTAssertFunction assertFunction = [functionStack lastObject];
if (assertFunction) {
return assertFunction;
}
return RCTCurrentAssertFunction;
}
void RCTPerformBlockWithAssertFunction(void (^block)(void), RCTAssertFunction assertFunction)
{
NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary;
NSMutableArray *functionStack = threadDictionary[RCTAssertFunctionStack];
if (!functionStack) {
functionStack = [[NSMutableArray alloc] init];
threadDictionary[RCTAssertFunctionStack] = functionStack;
}
[functionStack addObject:assertFunction];
block();
[functionStack removeLastObject];
}
NSString *RCTCurrentThreadName(void) NSString *RCTCurrentThreadName(void)
{ {
NSThread *thread = [NSThread currentThread]; NSThread *thread = [NSThread currentThread];
@ -84,3 +94,22 @@ NSString *RCTCurrentThreadName(void)
} }
return threadName; return threadName;
} }
void _RCTAssertFormat(
const char *condition,
const char *fileName,
int lineNumber,
const char *function,
NSString *format, ...)
{
RCTAssertFunction assertFunction = RCTGetLocalAssertFunction();
if (assertFunction) {
va_list args;
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
assertFunction(@(condition), @(fileName), @(lineNumber), @(function), message);
}
}