/ FoundationExceptions.m
FoundationExceptions.m
1 /** 2 * Copyright (C) 2017 Lubos Dolezel 3 * 4 * Darling is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * Foobar is distributed in the hope that it will be useful, 10 * Darling WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with Foobar. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #if !__OBJC2__ 19 20 #include <objc/runtime.h> 21 #include <objc/objc-exception.h> 22 #include "CFLogUtilities.h" 23 #include "CFInternal.h" 24 #include "ForFoundationOnly.h" 25 #include "FoundationExceptions.h" 26 #include <stdlib.h> 27 #include <dlfcn.h> 28 #import <Foundation/NSObject.h> 29 #import <Foundation/NSException.h> 30 31 static void (*__uncaughtExceptionHandler)(id exc) = NULL; 32 33 typedef struct { 34 jmp_buf buf; 35 void* pointers[4]; 36 } LocalData_t; 37 38 typedef struct ThreadChainLink 39 { 40 void** localExceptionData; 41 size_t localExceptionDataCount, localExceptionDataSize; 42 } ThreadChainLink_t; 43 44 static int objectIsKindOfNSException(id exc); 45 46 // Throw handler with Foundation support 47 static void __raiseError(id exc) 48 { 49 ThreadChainLink_t* chainLink = (ThreadChainLink_t*) _CFGetTSD(__CFTSDKeyExceptionData); 50 if (chainLink != NULL) 51 { 52 LocalData_t* led = (LocalData_t*) chainLink->localExceptionData[chainLink->localExceptionDataCount - 1]; 53 54 if (led != NULL) 55 { 56 chainLink->localExceptionDataCount--; 57 led->pointers[0] = exc; 58 _longjmp(led->buf, 1); 59 } 60 } 61 62 if (__uncaughtExceptionHandler != NULL && objectIsKindOfNSException(exc)) 63 { 64 __uncaughtExceptionHandler(exc); 65 } 66 else 67 { 68 if (!objectIsKindOfNSException(exc)) 69 { 70 CFLog(kCFLogLevelError, CFSTR("*** Terminating app due to uncaught exception of class %s"), object_getClassName(exc)); 71 } 72 else 73 { 74 CFLog(kCFLogLevelError, CFSTR("*** Terminating app due to uncaught exception of class %@, reason: \"%@\",\n" 75 "*** Call stack:\n%@\n"), 76 [exc name], [exc reason], [exc description]); 77 } 78 } 79 abort(); 80 } 81 82 static id __exceptionExtract(void* localExceptionData) 83 { 84 LocalData_t* led = (LocalData_t*)localExceptionData; 85 return (id) led->pointers[0]; 86 } 87 88 static int objectIsKindOfNSException(id exc) 89 { 90 Class c = [NSException class]; 91 if (class_respondsToSelector(object_getClass(exc), @selector(isKindOfClass:))) 92 { 93 return [exc isKindOfClass: c]; 94 } 95 else 96 { 97 // NSException implements isKindOfClass:, so this means it's not an NSException 98 return 0; 99 } 100 } 101 102 static void __exceptionFinalize(void* ptr) 103 { 104 ThreadChainLink_t* tsd = (ThreadChainLink_t*) ptr; 105 free(tsd->localExceptionData); 106 free(tsd); 107 } 108 109 static int __exceptionMatch(Class matchClass, id exc) 110 { 111 Class excClass = object_getClass(exc); 112 113 if (class_respondsToSelector(excClass, @selector(isKindOfClass:))) 114 { 115 return [exc isKindOfClass: matchClass]; 116 } 117 else 118 { 119 while (excClass != NULL) 120 { 121 if (excClass == matchClass) 122 return 1; 123 excClass = class_getSuperclass(excClass); 124 } 125 return 0; 126 } 127 } 128 129 static void __addHandler2(void* localExceptionData) 130 { 131 ThreadChainLink_t* tsd = (ThreadChainLink_t*) _CFGetTSD(__CFTSDKeyExceptionData); 132 133 if (!tsd) 134 { 135 tsd = (ThreadChainLink_t*) malloc(sizeof(ThreadChainLink_t)); 136 memset(tsd, 0, sizeof(*tsd)); 137 _CFSetTSD(__CFTSDKeyExceptionData, tsd, __exceptionFinalize); 138 } 139 140 if (tsd->localExceptionDataCount+1 > tsd->localExceptionDataSize) 141 { 142 tsd->localExceptionDataSize += 16; 143 tsd->localExceptionData = (void**) realloc(tsd->localExceptionData, sizeof(void*) * tsd->localExceptionDataSize); 144 } 145 146 // store pointer to localExceptionData in tsd 147 tsd->localExceptionData[tsd->localExceptionDataCount] = localExceptionData; 148 tsd->localExceptionDataCount++; 149 } 150 151 static void __removeHandler2(void* localExceptionData) 152 { 153 ThreadChainLink_t* tsd = (ThreadChainLink_t*) _CFGetTSD(__CFTSDKeyExceptionData); 154 tsd->localExceptionDataCount--; 155 } 156 157 void* _CFDoExceptionOperation(int op, void* arg) 158 { 159 switch (op) 160 { 161 case kCFDoExceptionOperationGetUncaughtHandler: 162 return __uncaughtExceptionHandler; 163 case kCFDoExceptionOperationSetUncaughtHandler: 164 __uncaughtExceptionHandler = arg; 165 break; 166 case kCFDoExceptionOperationRaiseError: 167 __raiseError((id) arg); 168 break; 169 case kCFDoExceptionOperationAddHandler: 170 __addHandler2(arg); 171 break; 172 case kCFDoExceptionOperationRemoveHandler: 173 __removeHandler2(arg); 174 break; 175 case kCFDoExceptionOperationExtractException: 176 return __exceptionExtract(arg); 177 } 178 return NULL; 179 } 180 181 static objc_exception_functions_t old_exc_funcs; 182 183 // Called from __CFInitialize 184 void __exceptionInit(void) 185 { 186 objc_exception_functions_t funcs = { 0, __raiseError, __addHandler2, __removeHandler2, __exceptionExtract, __exceptionMatch }; 187 objc_exception_get_functions(&old_exc_funcs); 188 objc_exception_set_functions(&funcs); 189 } 190 191 #endif // !__OBJC2__ 192