Add support for catching C++ (and Java / Ada) exceptions in Objective-C.

main
theraven 16 years ago
parent 121e14a3b8
commit f375d20d7a

@ -20,9 +20,9 @@ libobjc_C_FILES = \
category_loader.c\ category_loader.c\
class_table.c\ class_table.c\
dtable.c\ dtable.c\
eh_personality.c\
encoding2.c\ encoding2.c\
hash_table.c\ hash_table.c\
eh_personality.c\
hooks.c\ hooks.c\
ivar.c\ ivar.c\
loader.c\ loader.c\
@ -43,6 +43,7 @@ ifneq ($(install_headers), no)
libobjc_HEADER_FILES = \ libobjc_HEADER_FILES = \
Availability.h\ Availability.h\
blocks_runtime.h\ blocks_runtime.h\
hooks.h\
runtime.h\ runtime.h\
slot.h slot.h
endif endif
@ -55,8 +56,8 @@ libobjc_CPPFLAGS += -D__OBJC_RUNTIME_INTERNAL__=1 -D_XOPEN_SOURCE=500
# Note to Riccardo. Please do not 'fix' C99isms in this. The new ABI is only # Note to Riccardo. Please do not 'fix' C99isms in this. The new ABI is only
# useful on compilers that support C99 (currently only clang), so there is no # useful on compilers that support C99 (currently only clang), so there is no
# benefit from supporting platforms with no C99 compiler. # benefit from supporting platforms with no C99 compiler.
libobjc_CFLAGS += -Werror -std=c99 -g -march=native -fexceptions #-fno-inline libobjc_CFLAGS += -std=c99 -g -march=native -fexceptions -fno-inline
libobjc_OBJCFLAGS += -g -std=c99 -march=native libobjc_OBJCFLAGS += $(libobjc_CFLAGS)
libobjc_LDFLAGS += -g -ltoydispatch libobjc_LDFLAGS += -g -ltoydispatch
libobjc_LIB_DIRS += -L toydispatch/obj libobjc_LIB_DIRS += -L toydispatch/obj

@ -76,39 +76,33 @@ Class get_type_table_entry(struct _Unwind_Context *context,
return (Class)objc_getClass(class_name); return (Class)objc_getClass(class_name);
} }
static BOOL isKindOfClass(struct _Unwind_Exception *ex, Class type) static BOOL isKindOfClass(Class thrown, Class type, BOOL foreignException)
{ {
// Nil is a catchall, but we only want to catch things that are not foreign // Nil is a catchall, but we only want to catch things that are not foreign
// exceptions in it. // exceptions in it.
if (Nil == type) if (Nil == type)
{ {
return ex->exception_class == objc_exception_class; return (Nil != thrown) && !foreignException;
} }
if (ex->exception_class != objc_exception_class)
{
// FIXME: Box and stuff.
return NO;
}
id object = *(id*)(ex + 1);
do do
{ {
if (object->isa == type) if (thrown == type)
{ {
return YES; return YES;
} }
type = class_getSuperclass(type); thrown = class_getSuperclass(thrown);
} while (Nil != type); } while (Nil != thrown);
return NO; return NO;
} }
static BOOL check_action_record(struct _Unwind_Context *context, static BOOL check_action_record(struct _Unwind_Context *context,
int64_t exceptionClass, BOOL foreignException,
struct dwarf_eh_lsda *lsda, struct dwarf_eh_lsda *lsda,
dw_eh_ptr_t action_record, dw_eh_ptr_t action_record,
struct _Unwind_Exception *ex, Class thrown_class,
unsigned long *selector) unsigned long *selector)
{ {
while (action_record) while (action_record)
@ -120,7 +114,7 @@ static BOOL check_action_record(struct _Unwind_Context *context,
if (filter > 0) if (filter > 0)
{ {
Class type = get_type_table_entry(context, lsda, filter); Class type = get_type_table_entry(context, lsda, filter);
if (ex && isKindOfClass(ex, type)) if (isKindOfClass(thrown_class, type, foreignException))
{ {
return YES; return YES;
} }
@ -161,14 +155,21 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
// then we ignore it (for now) // then we ignore it (for now)
BOOL foreignException = exceptionClass != objc_exception_class; BOOL foreignException = exceptionClass != objc_exception_class;
Class thrown_class = Nil;
// If it's not a foreign exception, then we know the layout of the // If it's not a foreign exception, then we know the layout of the
// language-specific exception stuff. // language-specific exception stuff.
if (!foreignException) if (!foreignException)
{ {
ex = (struct objc_exception*) ((char*)exceptionObject - ex = (struct objc_exception*) ((char*)exceptionObject -
offsetof(struct objc_exception, unwindHeader)); offsetof(struct objc_exception, unwindHeader));
}
thrown_class = ex->object->isa;
}
else if (_objc_class_for_boxing_foreign_exception)
{
thrown_class = _objc_class_for_boxing_foreign_exception(exceptionClass);
}
unsigned char *lsda_addr = (void*)_Unwind_GetLanguageSpecificData(context); unsigned char *lsda_addr = (void*)_Unwind_GetLanguageSpecificData(context);
// No LSDA implies no landing pads - try the next frame // No LSDA implies no landing pads - try the next frame
@ -182,8 +183,8 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
{ {
struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr);
action = dwarf_eh_find_callsite(context, &lsda); action = dwarf_eh_find_callsite(context, &lsda);
BOOL found_handler = check_action_record(context, exceptionClass, &lsda, BOOL found_handler = check_action_record(context, foreignException,
action.action_record, exceptionObject, &selector); &lsda, action.action_record, thrown_class, &selector);
// If there's no action record, we've only found a cleanup, so keep // If there's no action record, we've only found a cleanup, so keep
// searching for something real // searching for something real
if (found_handler) if (found_handler)
@ -200,18 +201,33 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
return _URC_CONTINUE_UNWIND; return _URC_CONTINUE_UNWIND;
} }
if (!(actions & _UA_HANDLER_FRAME) || foreignException) // TODO: If this is a C++ exception, we can cache the lookup and cheat a
// bit
id object = nil;
if (!(actions & _UA_HANDLER_FRAME))
{ {
struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr); struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr);
action = dwarf_eh_find_callsite(context, &lsda); action = dwarf_eh_find_callsite(context, &lsda);
if (0 == action.landing_pad) { return _URC_CONTINUE_UNWIND; } if (0 == action.landing_pad) { return _URC_CONTINUE_UNWIND; }
selector = 0; selector = 0;
} }
else if (foreignException)
{
struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr);
action = dwarf_eh_find_callsite(context, &lsda);
check_action_record(context, foreignException, &lsda,
action.action_record, thrown_class, &selector);
//[thrown_class exceptionWithForeignException: exceptionObject];
SEL box_sel = sel_registerName("exceptionWithForeignException:");
IMP boxfunction = objc_msg_lookup((id)thrown_class, box_sel);
object = boxfunction((id)thrown_class, box_sel, exceptionObject);
}
else else
{ {
// Restore the saved info if we saved some last time. // Restore the saved info if we saved some last time.
action.landing_pad = ex->landingPad; action.landing_pad = ex->landingPad;
selector = ex->handlerSwitchValue; selector = ex->handlerSwitchValue;
object = ex->object;
} }

@ -21,7 +21,7 @@ OBJC_HOOK Class (*_objc_lookup_class)(const char *name);
/** /**
* Class load callback. * Class load callback.
*/ */
OBJC_HOOK void (*_objc_load_callback)(Class class, struct objc_category *category); OBJC_HOOK void (*_objc_load_callback)(Class cls, struct objc_category *category);
/** /**
* The hook used for fast proxy lookups. This takes an object and a selector * The hook used for fast proxy lookups. This takes an object and a selector
* and returns the instance that the message should be forwarded to. * and returns the instance that the message should be forwarded to.
@ -44,3 +44,15 @@ OBJC_HOOK IMP (*__objc_msg_forward2)(id, SEL);
* called. * called.
*/ */
OBJC_HOOK void (*_objc_unexpected_exception)(id exception); OBJC_HOOK void (*_objc_unexpected_exception)(id exception);
/**
* Hook defined to return the class to be used for boxing a foreign exception
* type. The class must implement:
*
* + (id)exceptionWithForeignException: (_Unwind_Exception*)ex;
*
* This will return an instance of the class that encapsulates the exception.
*
* Note: Due to limitations of the current ABI, there is no way for the handler
* to
*/
OBJC_HOOK Class (*_objc_class_for_boxing_foreign_exception)(int64_t exceptionClass);

Loading…
Cancel
Save