/ NSException.m
NSException.m
1 // 2 // NSException.m 3 // CoreFoundation 4 // 5 // Copyright (c) 2014 Apportable. All rights reserved. 6 // 7 8 #import <Foundation/NSException.h> 9 #import <Foundation/NSDictionary.h> 10 #import <Foundation/NSString.h> 11 #import <Foundation/NSArray.h> 12 #import "CFString.h" 13 #import "CFNumber.h" 14 #import <dispatch/dispatch.h> 15 #import <objc/runtime.h> 16 #import <execinfo.h> 17 18 @interface NSException () 19 - (BOOL)_installStackTraceKeyIfNeeded; 20 @end 21 22 typedef id (*objc_exception_preprocessor)(id exception); 23 extern objc_exception_preprocessor objc_setExceptionPreprocessor(objc_exception_preprocessor fn); 24 25 static NSException *__exceptionPreprocess(NSException *exception) 26 { 27 // this is quite expensive (1/3 sec lag), when it can be made more performant (under 1/60 sec) this should be re-enabled 28 // UPDATE(facekapow): i've re-enabled this even though nothing has really changed in the code, but from limited testing 29 // it seems that this is pretty performant. besides, exceptions are just that: *exceptions*. 30 // you shouldn't be constantly throwing around exceptions. 31 #if 1 32 [exception _installStackTraceKeyIfNeeded]; 33 #endif 34 return exception; 35 } 36 37 static void NSExceptionInitializer() __attribute__((constructor)); 38 static void NSExceptionInitializer() 39 { 40 #ifndef __i386__ 41 objc_setExceptionPreprocessor(&__exceptionPreprocess); 42 #endif 43 } 44 45 NSString *const NSGenericException = @"NSGenericException"; 46 NSString *const NSRangeException = @"NSRangeException"; 47 NSString *const NSInvalidArgumentException = @"NSInvalidArgumentException"; 48 NSString *const NSInternalInconsistencyException = @"NSInternalInconsistencyException"; 49 NSString *const NSMallocException = @"NSMallocException"; 50 NSString *const NSObjectInaccessibleException = @"NSObjectInaccessibleException"; 51 NSString *const NSObjectNotAvailableException = @"NSObjectNotAvailableException"; 52 NSString *const NSDestinationInvalidException = @"NSDestinationInvalidException"; 53 NSString *const NSPortTimeoutException = @"NSPortTimeoutException"; 54 NSString *const NSInvalidSendPortException = @"NSInvalidSendPortException"; 55 NSString *const NSInvalidReceivePortException = @"NSInvalidReceivePortException"; 56 NSString *const NSPortSendException = @"NSPortSendException"; 57 NSString *const NSPortReceiveException = @"NSPortReceiveException"; 58 NSString *const NSCharacterConversionException = @"NSCharacterConversionException"; 59 NSString *const NSFileHandleOperationException = @"NSFileHandleOperationException"; 60 61 @implementation NSException 62 63 - (instancetype) init { 64 [self release]; // initWithName:reason:userInfo: is the only acceptable init method 65 return nil; 66 } 67 68 - (instancetype) initWithName: (NSExceptionName) name 69 reason: (NSString *) reason 70 userInfo: (NSDictionary *) userInfo 71 { 72 self = [super init]; 73 if (self) { 74 _name = [name copy]; 75 _reason = [reason copy]; 76 _userInfo = [userInfo copy]; 77 } 78 return self; 79 } 80 81 - (instancetype) copyWithZone: (NSZone *) zone { 82 return [self retain]; 83 } 84 85 - (void) dealloc { 86 [_name release]; 87 [_reason release]; 88 [_userInfo release]; 89 [_reserved release]; 90 [super dealloc]; 91 } 92 93 - (void) raise { 94 @throw self; 95 } 96 97 + (void) raise: (NSExceptionName) name 98 format: (NSString *) format, ... 99 { 100 va_list args; 101 va_start(args, format); 102 CFStringRef reason = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, (CFStringRef) format, args); 103 va_end(args); 104 NSException *exc = [self exceptionWithName: name 105 reason: reason 106 userInfo: nil]; 107 [exc raise]; 108 CFRelease(reason); 109 } 110 111 + (void) raise: (NSExceptionName) name 112 format: (NSString *) format 113 arguments: (va_list) args 114 { 115 CFStringRef reason = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, NULL, (CFStringRef) format, args); 116 NSException *exc = [self exceptionWithName: name 117 reason: reason 118 userInfo: nil]; 119 [exc raise]; 120 CFRelease(reason); 121 } 122 123 + (NSException *) exceptionWithName: (NSExceptionName) name 124 reason: (NSString *) reason 125 userInfo: (NSDictionary *) userInfo 126 { 127 return [[[self alloc] initWithName: name 128 reason: reason 129 userInfo: userInfo] autorelease]; 130 } 131 132 - (NSExceptionName) name { 133 return _name; 134 } 135 136 - (NSString *) reason { 137 return _reason; 138 } 139 140 - (NSDictionary *) userInfo { 141 return _userInfo; 142 } 143 144 - (BOOL) _installStackTraceKeyIfNeeded { 145 if (_reserved == NULL) { 146 _reserved = [[NSMutableDictionary alloc] init]; 147 } 148 149 NSArray *callStackSymbols = nil; 150 if (_userInfo != nil) { 151 callStackSymbols = _userInfo[@"NSStackTraceKey"]; 152 } 153 154 if (callStackSymbols == nil) { 155 callStackSymbols = _reserved[@"callStackSymbols"]; 156 } else { 157 _reserved[@"callStackSymbols"] = callStackSymbols; 158 } 159 160 if (callStackSymbols == nil) { 161 void *stack[128] = { NULL }; 162 CFStringRef symbols[128] = { nil }; 163 CFNumberRef returnAddresses[128] = { nil }; 164 165 int count = backtrace(stack, sizeof(stack) / sizeof(stack[0])); 166 char **sym = backtrace_symbols(stack, count); 167 if (sym == NULL) { 168 return NO; 169 } 170 171 // Make sure to skip this frame since it is just an instantiator. 172 for (int i = 1; i < count; i++) { 173 returnAddresses[i - 1] = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &stack[i]); 174 symbols[i - 1] = CFStringCreateWithCString(kCFAllocatorDefault, sym[i], kCFStringEncodingUTF8); 175 } 176 177 free(sym); 178 callStackSymbols = [[NSArray alloc] initWithObjects: (id *) symbols 179 count: count - 1]; 180 NSArray *callStackReturnAddresses = [[NSArray alloc] initWithObjects: (id *) returnAddresses 181 count: count - 1]; 182 _reserved[@"callStackSymbols"] = callStackSymbols; 183 _reserved[@"callStackReturnAddresses"] = callStackReturnAddresses; 184 185 for (int i = 1; i < count; i++) { 186 CFRelease(returnAddresses[i - 1]); 187 CFRelease(symbols[i - 1]); 188 } 189 190 [callStackSymbols release]; 191 [callStackReturnAddresses release]; 192 } 193 194 return callStackSymbols != nil; 195 } 196 197 - (NSArray *) callStackReturnAddresses { 198 return _reserved[@"callStackReturnAddresses"]; 199 } 200 201 - (NSArray *) callStackSymbols { 202 return _reserved[@"callStackSymbols"]; 203 } 204 205 - (NSString *) description { 206 if (_reason != nil) { 207 return _reason; 208 } 209 return _name; 210 } 211 212 @end