/** NSException - Object encapsulation of a general exception handler Copyright (C) 1993, 1994, 1996, 1997, 1999 Free Software Foundation, Inc. Written by: Adam Fedor Date: Mar 1995 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. $Date$ $Revision$ */ #include "config.h" #include "GNUstepBase/preface.h" #include "Foundation/NSException.h" #include "Foundation/NSString.h" #include "Foundation/NSArray.h" #include "Foundation/NSCoder.h" #include "Foundation/NSThread.h" #include "Foundation/NSDictionary.h" #include NSString* const NSGenericException = @"NSGenericException"; NSString* const NSInternalInconsistencyException = @"NSInternalInconsistencyException"; NSString* const NSInvalidArgumentException = @"NSInvalidArgumentException"; NSString* const NSMallocException = @"NSMallocException"; NSString* const NSRangeException = @"NSRangeException"; NSString* const NSCharacterConversionException = @"NSCharacterConversionException"; NSString* const NSParseErrorException = @"NSParseErrorException"; #include "GSPrivate.h" static void _terminate() { BOOL shouldAbort; #ifdef DEBUG shouldAbort = YES; // abort() by default. #else shouldAbort = NO; // exit() by default. #endif shouldAbort = GSEnvironmentFlag("CRASH_ON_ABORT", shouldAbort); if (shouldAbort == YES) { abort(); } else { exit(1); } } 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(); } @implementation NSException + (NSException*) exceptionWithName: (NSString*)name reason: (NSString*)reason userInfo: (NSDictionary*)userInfo { return AUTORELEASE([[self alloc] initWithName: name reason: reason userInfo: userInfo]); } + (void) raise: (NSString*)name format: (NSString*)format,... { va_list args; va_start(args, format); [self raise: name format: format arguments: args]; // This probably doesn't matter, but va_end won't get called va_end(args); } + (void) raise: (NSString*)name format: (NSString*)format arguments: (va_list)argList { NSString *reason; NSException *except; reason = [NSString stringWithFormat: format arguments: argList]; except = [self exceptionWithName: name reason: reason userInfo: nil]; [except raise]; } - (id) initWithName: (NSString*)name reason: (NSString*)reason userInfo: (NSDictionary*)userInfo { ASSIGN(_e_name, name); ASSIGN(_e_reason, reason); ASSIGN(_e_info, userInfo); return self; } - (void) dealloc { DESTROY(_e_name); DESTROY(_e_reason); DESTROY(_e_info); [super dealloc]; } - (void) raise { NSThread *thread; NSHandler *handler; thread = GSCurrentThread(); handler = thread->_exception_handler; if (handler == NULL) { 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; handler->exception = self; longjmp(handler->jumpState, 1); } - (NSString*) name { if (_e_name != nil) { return _e_name; } else { return NSStringFromClass([self class]); } } - (NSString*) reason { if (_e_reason != nil) { return _e_reason; } else { return @"unspecified reason"; } } - (NSDictionary*) userInfo { return _e_info; } - (Class) classForPortCoder { return [self class]; } - (id) replacementObjectForPortCoder: (NSPortCoder*)aCoder { return self; } - (void) encodeWithCoder: (NSCoder*)aCoder { [aCoder encodeValueOfObjCType: @encode(id) at: &_e_name]; [aCoder encodeValueOfObjCType: @encode(id) at: &_e_reason]; [aCoder encodeValueOfObjCType: @encode(id) at: &_e_info]; } - (id) initWithCoder: (NSCoder*)aDecoder { [aDecoder decodeValueOfObjCType: @encode(id) at: &_e_name]; [aDecoder decodeValueOfObjCType: @encode(id) at: &_e_reason]; [aDecoder decodeValueOfObjCType: @encode(id) at: &_e_info]; return self; } - (id) deepen { _e_name = [_e_name copyWithZone: [self zone]]; _e_reason = [_e_reason copyWithZone: [self zone]]; _e_info = [_e_info copyWithZone: [self zone]]; return self; } - (id) copyWithZone: (NSZone*)zone { if (NSShouldRetainWithZone(self, zone)) return RETAIN(self); else return [(NSException*)NSCopyObject(self, 0, zone) deepen]; } - (NSString*) description { if (_e_info) return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@ INFO:%@", [super description], _e_name, _e_reason, _e_info]; else return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@", [super description], _e_name, _e_reason]; } @end void _NSAddHandler (NSHandler* handler) { NSThread *thread; thread = GSCurrentThread(); #if defined(__MINGW32__) && defined(DEBUG) if (thread->_exception_handler && IsBadReadPtr(thread->_exception_handler, sizeof(NSHandler))) { fprintf(stderr, "ERROR: Current exception handler is bogus.\n"); } #endif handler->next = thread->_exception_handler; thread->_exception_handler = handler; } void _NSRemoveHandler (NSHandler* handler) { NSThread *thread; thread = GSCurrentThread(); #if defined(DEBUG) if (thread->_exception_handler != handler) { fprintf(stderr, "ERROR: Removing exception handler that is not on top " "of the stack. (You probably called return in an NS_DURING block.)\n"); } #if defined(__MINGW32__) if (IsBadReadPtr(handler, sizeof(NSHandler))) { fprintf(stderr, "ERROR: Could not remove exception handler, " "handler is bad pointer.\n"); thread->_exception_handler = 0; return; } if (handler->next && IsBadReadPtr(handler->next, sizeof(NSHandler))) { fprintf(stderr, "ERROR: Could not restore exception handler, " "handler->next is bad pointer.\n"); thread->_exception_handler = 0; return; } #endif #endif thread->_exception_handler = handler->next; }