/ 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