/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
//
// objc_exception.m
// Support minimal stand-alone implementation plus hooks for swapping
// in a richer implementation.
//
// Created by Blaine Garst on Fri Nov 01 2002.
// Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
//
#undef _BUILDING_OBJC
#import "objc-exception.h"
static objc_exception_functions_t xtab;
// forward declaration
static void set_default_handlers();
/*
* Exported functions
*/
// get table; version tells how many
void objc_exception_get_functions(objc_exception_functions_t *table) {
// only version 0 supported at this point
if (table && table->version == 0)
*table = xtab;
}
// set table
void objc_exception_set_functions(objc_exception_functions_t *table) {
// only version 0 supported at this point
if (table && table->version == 0)
xtab = *table;
}
/*
* The following functions are
* synthesized by the compiler upon encountering language constructs
*/
void objc_exception_throw(id exception) {
if (!xtab.throw_exc) {
set_default_handlers();
}
xtab.throw_exc(exception);
}
void objc_exception_try_enter(void *localExceptionData) {
if (!xtab.throw_exc) {
set_default_handlers();
}
xtab.try_enter(localExceptionData);
}
void objc_exception_try_exit(void *localExceptionData) {
if (!xtab.throw_exc) {
set_default_handlers();
}
xtab.try_exit(localExceptionData);
}
id objc_exception_extract(void *localExceptionData) {
if (!xtab.throw_exc) {
set_default_handlers();
}
return xtab.extract(localExceptionData);
}
int objc_exception_match(Class exceptionClass, id exception) {
if (!xtab.throw_exc) {
set_default_handlers();
}
return xtab.match(exceptionClass, exception);
}
// quick and dirty exception handling code
// default implementation - mostly a toy for use outside/before Foundation
// provides its implementation
// Perhaps the default implementation should just complain loudly and quit
#import <pthread.h>
#import <setjmp.h>
extern void _objc_inform(const char *fmt, ...);
typedef struct { jmp_buf buf; void *pointers[4]; } LocalData_t;
typedef struct _threadChain {
LocalData_t *topHandler;
void *perThreadID;
struct _threadChain *next;
}
ThreadChainLink_t;
#define _ExceptionDebug 0
static ThreadChainLink_t ThreadChainLink;
static ThreadChainLink_t *getChainLink() {
// follow links until thread_self() found (someday) XXX
pthread_t self = pthread_self();
ThreadChainLink_t *walker = &ThreadChainLink;
while (walker->perThreadID != (void *)self) {
if (walker->next != NULL) {
walker = walker->next;
continue;
}
// create a new one
// XXX not thread safe (!)
// XXX Also, we don't register to deallocate on thread death
walker->next = (ThreadChainLink_t *)malloc(sizeof(ThreadChainLink_t));
walker = walker->next;
walker->next = NULL;
walker->topHandler = NULL;
walker->perThreadID = self;
}
return walker;
}
static void default_try_enter(void *localExceptionData) {
ThreadChainLink_t *chainLink = getChainLink();
((LocalData_t *)localExceptionData)->pointers[1] = chainLink->topHandler;
chainLink->topHandler = localExceptionData;
if (_ExceptionDebug) _objc_inform("entered try block }
static void default_throw(id value) {
ThreadChainLink_t *chainLink = getChainLink();
if (value == nil) {
if (_ExceptionDebug) _objc_inform("objc_exception_throw with nil value\n");
return;
}
if (chainLink == NULL) {
if (_ExceptionDebug) _objc_inform("No handler in place!\n");
return;
}
if (_ExceptionDebug) _objc_inform("exception thrown, going to handler block LocalData_t *led = chainLink->topHandler;
chainLink->topHandler = led->pointers[1]; // pop top handler
led->pointers[0] = value; // store exception that is thrown
_longjmp(led->buf, 1);
}
static void default_try_exit(void *led) {
ThreadChainLink_t *chainLink = getChainLink();
if (!chainLink || led != chainLink->topHandler) {
if (_ExceptionDebug) _objc_inform("!!! mismatched try block exit handlers !!!\n");
return;
}
if (_ExceptionDebug) _objc_inform("removing try block handler chainLink->topHandler = chainLink->topHandler->pointers[1]; // pop top handler
}
static id default_extract(void *localExceptionData) {
LocalData_t *led = (LocalData_t *)localExceptionData;
return (id)led->pointers[0];
}
static int default_match(Class exceptionClass, id exception) {
//return [exception isKindOfClass:exceptionClass];
Class cls;
for (cls = exception->isa; nil != cls; cls = cls->super_class)
if (cls == exceptionClass) return 1;
return 0;
}
static void set_default_handlers() {
objc_exception_functions_t default_functions = {
0, default_throw, default_try_enter, default_try_exit, default_extract, default_match };
// should this always print?
if (_ExceptionDebug) _objc_inform("*** Setting default (non-Foundation) exception mechanism\n");
objc_exception_set_functions(&default_functions);
}