diff --git a/ChangeLog b/ChangeLog index f731ade46..31c29facf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2005-07-01 Richard Frith-Macdonald + + * Source/NSException.m: ([-raise]) Fix behavior to be the same as in + MacOS-X (ie call the default uncaught exception handler after + whatever handler is set, in case the set handler does not terminate). + Ensures that the -raise method cannot return. + * Testing/basic.m: Add test for uncaught exception handler. + 2005-06-30 Richard Frith-Macdonald * Source/NSSerializer.m: deserializeFromInfo() check that cross diff --git a/Source/NSException.m b/Source/NSException.m index ba489c32f..46ea7c75d 100644 --- a/Source/NSException.m +++ b/Source/NSException.m @@ -79,36 +79,17 @@ NSString* const NSParseErrorException #include "GSPrivate.h" -static void -_preventRecursion (NSException *exception) +static void _terminate() { - fprintf(stderr, "recursion encountered handling uncaught exception\n"); - fflush(stderr); /* NEEDED UNDER MINGW */ -} - -static void -_NSFoundationUncaughtExceptionHandler (NSException *exception) -{ - BOOL a; - extern const char* GSArgZero(void); - - _NSUncaughtExceptionHandler = _preventRecursion; -#if 1 - fprintf(stderr, "%s: Uncaught exception %s, reason: %s\n", GSArgZero(), - [[exception name] lossyCString], [[exception reason] lossyCString]); - fflush(stderr); /* NEEDED UNDER MINGW */ -#else - NSLog("Uncaught exception %@, reason: %@", - [exception name], [exception reason]); -#endif + BOOL shouldAbort; #ifdef DEBUG - a = YES; // abort() by default. + shouldAbort = YES; // abort() by default. #else - a = NO; // exit() by default. + shouldAbort = NO; // exit() by default. #endif - a = GSEnvironmentFlag("CRASH_ON_ABORT", a); - if (a == YES) + shouldAbort = GSEnvironmentFlag("CRASH_ON_ABORT", shouldAbort); + if (shouldAbort == YES) { abort(); } @@ -118,6 +99,18 @@ _NSFoundationUncaughtExceptionHandler (NSException *exception) } } +static void +_NSFoundationUncaughtExceptionHandler (NSException *exception) +{ + extern const char* GSArgZero(void); + + fprintf(stderr, "%s: Uncaught exception %s, reason: %s\n", GSArgZero(), + [[exception name] lossyCString], [[exception reason] lossyCString]); + fflush(stderr); /* NEEDED UNDER MINGW */ + + _terminate(); +} + /**

The NSException class helps manage errors in a program. It @@ -223,25 +216,56 @@ _NSFoundationUncaughtExceptionHandler (NSException *exception) Raises the exception. All code following the raise will not be executed and program control will be transfered to the closest calling method which encapsulates the exception code in an - NS_DURING macro, or to the uncaught exception handler if there is no - other handling code. + NS_DURING macro.
+ If the exception was not caught in a macro, the currently set + uncaught exception handler is called to perform final logging + and handle program termination.
+ If the uncaught exception handler fails to terminate the program, + then the default builtin uncaught exception handler will do so.
+ NB. all other exception raising methods call this one, so if you + want to set a breakpoint when debugging, set it in this method. */ - (void) raise { NSThread *thread; NSHandler *handler; - if (_NSUncaughtExceptionHandler == NULL) - { - _NSUncaughtExceptionHandler = _NSFoundationUncaughtExceptionHandler; - } - thread = GSCurrentThread(); handler = thread->_exception_handler; if (handler == NULL) { - _NSUncaughtExceptionHandler(self); - return; + static BOOL recursion = NO; + + /* + * Set a flag to prevent recursive uncaught exceptions. + */ + if (recursion == NO) + { + recursion = YES; + } + else + { + fprintf(stderr, + "recursion encountered handling uncaught exception\n"); + fflush(stderr); /* NEEDED UNDER MINGW */ + _terminate(); + } + + /* + * Call the uncaught exception handler (if there is one). + */ + if (_NSUncaughtExceptionHandler != NULL) + { + (*_NSUncaughtExceptionHandler)(self); + } + + /* + * The uncaught exception handler which is set has not + * exited, so we call the builtin handler, (undocumented + * behavior of MacOS-X). + * The standard handler is guaranteed to exit/abort. + */ + _NSFoundationUncaughtExceptionHandler(self); } thread->_exception_handler = handler->next; diff --git a/Testing/basic.m b/Testing/basic.m index 6398974c5..9a35bf4b5 100644 --- a/Testing/basic.m +++ b/Testing/basic.m @@ -6,6 +6,12 @@ #if 1 +static void uncaught(NSException* e) +{ + fprintf(stderr, "In uncaught exception handler.\n"); + [NSException raise: NSGenericException format: @"Recursive exception"]; +} + static void test1(void) { NSURL *baseURL = [NSURL fileURLWithPath:@"/usr/local/bin"]; @@ -138,7 +144,15 @@ int main () printf ("Hello from object at 0x%x\n", (unsigned)[o self]); - NSLog(@"Value for foo is %@", [a valueForKey: @"foo"]); + NS_DURING + { + NSLog(@"Value for foo is %@", [a valueForKey: @"foo"]); + } + NS_HANDLER + { + NSLog(@"Caught expected exception: %@", localException); + } + NS_ENDHANDLER [o release]; o = [NSString stringWithFormat: @"/proc/%d/status", getpid()]; @@ -146,6 +160,10 @@ int main () o = [NSString stringWithContentsOfFile: o]; NSLog(@"'%@'", o); + NSLog(@"This test should now cause program termination after a recursive exception"); + + NSSetUncaughtExceptionHandler(uncaught); + [NSException raise: NSGenericException format: @"an artifical exception"]; exit (0); } #else