2015-03-23 20:28:42 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
#import "RCTAssert.h"
|
2015-11-05 20:19:56 +00:00
|
|
|
#import "RCTLog.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-04-11 22:08:00 +00:00
|
|
|
NSString *const RCTErrorDomain = @"RCTErrorDomain";
|
2015-11-05 20:19:56 +00:00
|
|
|
NSString *const RCTJSStackTraceKey = @"RCTJSStackTraceKey";
|
2015-11-10 12:29:39 +00:00
|
|
|
NSString *const RCTFatalExceptionName = @"RCTFatalException";
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-08-07 13:06:17 +00:00
|
|
|
static NSString *const RCTAssertFunctionStack = @"RCTAssertFunctionStack";
|
|
|
|
|
2015-04-11 22:08:00 +00:00
|
|
|
RCTAssertFunction RCTCurrentAssertFunction = nil;
|
2015-11-05 20:19:56 +00:00
|
|
|
RCTFatalHandler RCTCurrentFatalHandler = nil;
|
2015-04-11 22:08:00 +00:00
|
|
|
|
2015-06-15 14:53:45 +00:00
|
|
|
NSException *_RCTNotImplementedException(SEL, Class);
|
|
|
|
NSException *_RCTNotImplementedException(SEL cmd, Class cls)
|
|
|
|
{
|
|
|
|
NSString *msg = [NSString stringWithFormat:@"%s is not implemented "
|
|
|
|
"for the class %@", sel_getName(cmd), cls];
|
|
|
|
return [NSException exceptionWithName:@"RCTNotDesignatedInitializerException"
|
|
|
|
reason:msg userInfo:nil];
|
|
|
|
}
|
|
|
|
|
2015-04-11 22:08:00 +00:00
|
|
|
void RCTSetAssertFunction(RCTAssertFunction assertFunction)
|
|
|
|
{
|
|
|
|
RCTCurrentAssertFunction = assertFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
RCTAssertFunction RCTGetAssertFunction(void)
|
|
|
|
{
|
|
|
|
return RCTCurrentAssertFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RCTAddAssertFunction(RCTAssertFunction assertFunction)
|
|
|
|
{
|
|
|
|
RCTAssertFunction existing = RCTCurrentAssertFunction;
|
|
|
|
if (existing) {
|
2015-08-07 13:06:17 +00:00
|
|
|
RCTCurrentAssertFunction = ^(NSString *condition,
|
2015-04-11 22:08:00 +00:00
|
|
|
NSString *fileName,
|
|
|
|
NSNumber *lineNumber,
|
|
|
|
NSString *function,
|
|
|
|
NSString *message) {
|
|
|
|
|
|
|
|
existing(condition, fileName, lineNumber, function, message);
|
|
|
|
assertFunction(condition, fileName, lineNumber, function, message);
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
RCTCurrentAssertFunction = assertFunction;
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-05-25 12:19:53 +00:00
|
|
|
|
2015-08-07 13:06:17 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
2015-11-03 22:45:46 +00:00
|
|
|
NSArray<RCTAssertFunction> *functionStack = threadDictionary[RCTAssertFunctionStack];
|
2015-08-24 10:14:33 +00:00
|
|
|
RCTAssertFunction assertFunction = functionStack.lastObject;
|
2015-08-07 13:06:17 +00:00
|
|
|
if (assertFunction) {
|
|
|
|
return assertFunction;
|
|
|
|
}
|
|
|
|
return RCTCurrentAssertFunction;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RCTPerformBlockWithAssertFunction(void (^block)(void), RCTAssertFunction assertFunction)
|
|
|
|
{
|
|
|
|
NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary;
|
2015-11-03 22:45:46 +00:00
|
|
|
NSMutableArray<RCTAssertFunction> *functionStack = threadDictionary[RCTAssertFunctionStack];
|
2015-08-07 13:06:17 +00:00
|
|
|
if (!functionStack) {
|
2015-08-17 14:35:34 +00:00
|
|
|
functionStack = [NSMutableArray new];
|
2015-08-07 13:06:17 +00:00
|
|
|
threadDictionary[RCTAssertFunctionStack] = functionStack;
|
|
|
|
}
|
|
|
|
[functionStack addObject:assertFunction];
|
|
|
|
block();
|
|
|
|
[functionStack removeLastObject];
|
|
|
|
}
|
|
|
|
|
2015-05-25 12:19:53 +00:00
|
|
|
NSString *RCTCurrentThreadName(void)
|
|
|
|
{
|
|
|
|
NSThread *thread = [NSThread currentThread];
|
2015-08-24 10:14:33 +00:00
|
|
|
NSString *threadName = thread.isMainThread ? @"main" : thread.name;
|
2015-05-25 12:19:53 +00:00
|
|
|
if (threadName.length == 0) {
|
2015-07-06 10:15:10 +00:00
|
|
|
const char *label = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
|
2015-07-03 09:03:33 +00:00
|
|
|
if (label && strlen(label) > 0) {
|
|
|
|
threadName = @(label);
|
|
|
|
} else {
|
|
|
|
threadName = [NSString stringWithFormat:@"%p", thread];
|
|
|
|
}
|
2015-05-25 12:19:53 +00:00
|
|
|
}
|
|
|
|
return threadName;
|
|
|
|
}
|
2015-08-07 13:06:17 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2015-11-05 20:19:56 +00:00
|
|
|
|
|
|
|
void RCTFatal(NSError *error)
|
|
|
|
{
|
2015-11-11 14:42:27 +00:00
|
|
|
_RCTLogNativeInternal(RCTLogLevelFatal, NULL, 0, @"%@", [error localizedDescription]);
|
2015-11-05 20:19:56 +00:00
|
|
|
|
|
|
|
RCTFatalHandler fatalHandler = RCTGetFatalHandler();
|
|
|
|
if (fatalHandler) {
|
|
|
|
fatalHandler(error);
|
|
|
|
} else {
|
|
|
|
#if DEBUG
|
|
|
|
@try {
|
|
|
|
#endif
|
2015-11-05 20:20:02 +00:00
|
|
|
NSString *message = RCTFormatError([error localizedDescription], error.userInfo[RCTJSStackTraceKey], 75);
|
2015-11-10 12:29:39 +00:00
|
|
|
[NSException raise:RCTFatalExceptionName format:@"%@", message];
|
2015-11-05 20:19:56 +00:00
|
|
|
#if DEBUG
|
|
|
|
} @catch (NSException *e) {}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RCTSetFatalHandler(RCTFatalHandler fatalhandler)
|
|
|
|
{
|
|
|
|
RCTCurrentFatalHandler = fatalhandler;
|
|
|
|
}
|
|
|
|
|
|
|
|
RCTFatalHandler RCTGetFatalHandler(void)
|
|
|
|
{
|
|
|
|
return RCTCurrentFatalHandler;
|
|
|
|
}
|
2015-11-05 20:20:02 +00:00
|
|
|
|
|
|
|
NSString *RCTFormatError(NSString *message, NSArray *stackTrace, NSUInteger maxMessageLength)
|
|
|
|
{
|
|
|
|
if (maxMessageLength > 0 && message.length > maxMessageLength) {
|
|
|
|
message = [[message substringToIndex:maxMessageLength] stringByAppendingString:@"..."];
|
|
|
|
}
|
|
|
|
|
|
|
|
NSMutableString *prettyStack = [NSMutableString string];
|
|
|
|
if (stackTrace) {
|
|
|
|
[prettyStack appendString:@", stack:\n"];
|
|
|
|
for (NSDictionary *frame in stackTrace) {
|
|
|
|
[prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return [NSString stringWithFormat:@"Message: %@%@", message, prettyStack];
|
|
|
|
}
|