Added Objective-C++ exception handling support. Allows throwing Objective-C objects with throw or @throw and catching them with catch() or @catch.

This commit is contained in:
theraven 2011-03-20 20:38:12 +00:00
parent f9abfb022a
commit e0719a9c62
6 changed files with 294 additions and 12 deletions

View File

@ -1,6 +1,6 @@
PACKAGE_NAME = gnustep-objc2
VERSION=1.2.0
VERSION=1.3.0
SVN_MODULE_NAME = libobjc2
SVN_BASE_URL = svn+ssh://svn.gna.org/svn/gnustep/libs
SVN_TAG_NAME=objc2
@ -40,6 +40,12 @@ libobjc_C_FILES = \
statics_loader.c\
toydispatch.c
ifeq ($(objective-cxx), yes)
libobjc_CC_FILES = objcxx_eh.cc
#libobjc_LDFLAGS = -lc++sup
endif
ifneq ($(enable_legacy), no)
libobjc_C_FILES += legacy_malloc.c
endif
@ -75,11 +81,14 @@ libobjc_CPPFLAGS += -D__OBJC_RUNTIME_INTERNAL__=1 -D_XOPEN_SOURCE=500
# useful on compilers that support C99 (currently only clang), so there is no
# benefit from supporting platforms with no C99 compiler.
libobjc_CFLAGS += -std=gnu99 -g -fexceptions #-fvisibility=hidden
libobjc_CFLAGS += -Wno-unused-function
libobjc_CCFLAGS += -std=c++98 -g -fexceptions #-fvisibility=hidden
# Uncomment this when debugging - it makes everything slow, but means that the
# debugger actually works...
#libobjc_CFLAGS += -fno-inline
libobjc_OBJCFLAGS += $(libobjc_CFLAGS) $(libobjc_CFLAGS)
libobjc_LDFLAGS += -g
libobjc_LDFLAGS += -g
libobjc_LIB_DIRS += -L toydispatch/obj
libobjc_CFLAGS += -O3

View File

@ -120,6 +120,7 @@ void class_table_insert(Class class)
Class class_table_get_safe(const char *class_name)
{
if (NULL == class_name) { return Nil; }
return class_table_internal_table_get(class_table, class_name);
}

View File

@ -315,3 +315,5 @@ static struct dwarf_eh_action
}
return result;
}
#define EXCEPTION_CLASS(a,b,c,d,e,f,g,h) (((uint64_t)a << 56) + ((uint64_t)b << 48) + ((uint64_t)c << 40) + ((uint64_t)d << 32) + ((uint64_t)e << 24) + ((uint64_t)f << 16) + ((uint64_t)g << 8) + ((uint64_t)h))

View File

@ -6,13 +6,15 @@
#include "objc/runtime.h"
#include "objc/hooks.h"
#include "class.h"
#include "objcxx_eh.h"
#define fprintf(...)
/**
* Class of exceptions to distinguish between this and other exception types.
*/
#define objc_exception_class (*(int64_t*)"GNUCOBJC")
const uint64_t objc_exception_class = EXCEPTION_CLASS('G','N','U','C','O','B','J','C');
const uint64_t cxx_exception_class = EXCEPTION_CLASS('G','N','U','C','C','+','+','\0');
/**
* Structure used as a header on thrown exceptions.
@ -31,6 +33,11 @@ struct objc_exception
/** Thrown object. This is after the unwind header so that the C++
* exception handler can catch this as a foreign exception. */
id object;
/** C++ exception structure. Used for mixed exceptions. When we are in
* Objective-C++ code, we create this structure for passing to the C++
* exception personality function. It will then handle installing
* exceptions for us. */
struct _Unwind_Exception *cxx_exception;
};
typedef enum
@ -44,8 +51,13 @@ typedef enum
static void cleanup(_Unwind_Reason_Code reason, struct _Unwind_Exception *e)
{
/*
if (header->exceptionDestructor)
header->exceptionDestructor (e + 1);
free((struct objc_exception*) ((char*)e - offsetof(struct objc_exception,
unwindHeader)));
*/
}
/**
* Throws an Objective-C exception. This function is, unfortunately, used for
@ -189,7 +201,7 @@ static handler_type check_action_record(struct _Unwind_Context *context,
}
/**
* The exception personality function.
* The Objective-C exception personality function.
*/
_Unwind_Reason_Code __gnu_objc_personality_v0(int version,
_Unwind_Action actions,
@ -207,18 +219,39 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
struct objc_exception *ex = 0;
//char *cls = (char*)&exceptionClass;
fprintf(stderr, "Class: %c%c%c%c%c%c%c%c\n", cls[0], cls[1], cls[2], cls[3], cls[4], cls[5], cls[6], cls[7]);
fprintf(stderr, "Class: %c%c%c%c%c%c%c%c\n", cls[7], cls[6], cls[5], cls[4], cls[3], cls[2], cls[1], cls[0]);
// Check if this is a foreign exception. If it is a C++ exception, then we
// have to box it. If it's something else, like a LanguageKit exception
// then we ignore it (for now)
BOOL foreignException = exceptionClass != objc_exception_class;
// Is this a C++ exception containing an Objective-C++ object?
BOOL objcxxException = NO;
// The object to return
void *object = NULL;
if (exceptionClass == cxx_exception_class)
{
id obj = objc_object_for_cxx_exception(exceptionObject);
if (obj != (id)-1)
{
object = obj;
objcxxException = YES;
// This is a foreign exception, buy for the purposes of exception
// matching, we pretend that it isn't.
foreignException = NO;
}
}
Class thrown_class = Nil;
if (objcxxException)
{
thrown_class = (object == 0) ? Nil : ((id)object)->isa;
}
// If it's not a foreign exception, then we know the layout of the
// language-specific exception stuff.
if (!foreignException)
else if (!foreignException)
{
ex = (struct objc_exception*) ((char*)exceptionObject -
offsetof(struct objc_exception, unwindHeader));
@ -267,7 +300,6 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
// TODO: If this is a C++ exception, we can cache the lookup and cheat a
// bit
void *object = nil;
if (!(actions & _UA_HANDLER_FRAME))
{
struct dwarf_eh_lsda lsda = parse_lsda(context, lsda_addr);
@ -294,17 +326,26 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
object = exceptionObject;
//selector = 0;
}
else if (foreignException)
else if (foreignException || objcxxException)
{
fprintf(stderr, "Doing the foreign exception thing...\n");
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);
// If it's a foreign exception, then box it. If it's an Objective-C++
// exception, then we need to delete the exception object.
if (foreignException)
{
//[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 // ObjCXX exception
{
_Unwind_DeleteException(exceptionObject);
}
fprintf(stderr, "Boxed as %p\n", object);
}
else
@ -323,3 +364,32 @@ _Unwind_Reason_Code __gnu_objc_personality_v0(int version,
return _URC_INSTALL_CONTEXT;
}
_Unwind_Reason_Code __gnustep_objcxx_personality_v0(int version,
_Unwind_Action actions,
uint64_t exceptionClass,
struct _Unwind_Exception *exceptionObject,
struct _Unwind_Context *context)
{
if (exceptionClass == objc_exception_class)
{
struct objc_exception *ex = (struct objc_exception*)
((char*)exceptionObject - offsetof(struct objc_exception,
unwindHeader));
if (0 == ex->cxx_exception)
{
id *newEx = __cxa_allocate_exception(sizeof(id));
*newEx = ex->object;
ex->cxx_exception = objc_init_cxx_exception(newEx);
ex->cxx_exception->exception_class = cxx_exception_class;
ex->cxx_exception->exception_cleanup = cleanup;
ex->cxx_exception->private_1 = exceptionObject->private_1;
ex->cxx_exception->private_2 = exceptionObject->private_2;
}
exceptionObject = ex->cxx_exception;
exceptionClass = cxx_exception_class;
}
return __gxx_personality_v0(version, actions, exceptionClass,
exceptionObject, context);
}

181
objcxx_eh.cc Normal file
View File

@ -0,0 +1,181 @@
#include <stdlib.h>
#include <stdio.h>
#include "dwarf_eh.h"
#include <typeinfo>
#include <exception>
#include "objcxx_eh.h"
extern "C"
{
#include "objc/runtime.h"
};
using namespace std;
static BOOL isKindOfClass(Class thrown, Class type)
{
do
{
if (thrown == type)
{
return YES;
}
thrown = class_getSuperclass(thrown);
} while (Nil != thrown);
return NO;
}
/**
* C++ Exception structure. From the Itanium ABI spec
*/
struct __cxa_exception
{
std::type_info *exceptionType;
void (*exceptionDestructor) (void *);
unexpected_handler unexpectedHandler;
terminate_handler terminateHandler;
__cxa_exception *nextException;
unsigned int handlerCount;
int handlerSwitchValue;
const char *actionRecord;
const char *languageSpecificData;
void *catchTemp;
void *adjustedPtr;
_Unwind_Exception unwindHeader;
};
namespace gnustep
{
namespace libobjc
{
struct __objc_id_type_info : std::type_info
{
__objc_id_type_info() : type_info("@id") {};
virtual ~__objc_id_type_info();
virtual bool __do_catch(const type_info *thrownType,
void **obj,
unsigned outer) const;
};
struct __objc_class_type_info : std::type_info
{
virtual ~__objc_class_type_info();
virtual bool __do_catch(const type_info *thrownType,
void **obj,
unsigned outer) const;
};
}
};
gnustep::libobjc::__objc_class_type_info::~__objc_class_type_info() {}
gnustep::libobjc::__objc_id_type_info::~__objc_id_type_info() {}
bool gnustep::libobjc::__objc_class_type_info::__do_catch(const type_info *thrownType,
void **obj,
unsigned outer) const
{
id thrown = (id)obj;
bool found = false;
// Id throw matches any ObjC catch. This may be a silly idea!
if (dynamic_cast<const __objc_id_type_info*>(thrownType))
{
thrown = **(id**)obj;
// nil matches any handler
if (0 == thrown)
{
found = true;
}
else
{
// Check whether the real thrown object matches the catch type.
found = isKindOfClass(object_getClass(thrown), (Class)objc_getClass(name()));
}
}
if (dynamic_cast<const __objc_class_type_info*>(thrownType))
{
thrown = **(id**)obj;
found = isKindOfClass((Class)objc_getClass(thrownType->name()),
(Class)objc_getClass(name()));
}
if (found)
{
*obj = (void*)thrown;
}
return found;
};
bool gnustep::libobjc::__objc_id_type_info::__do_catch(const type_info *thrownType,
void **obj,
unsigned outer) const
{
// Id catch matches any ObjC throw
if (dynamic_cast<const __objc_class_type_info*>(thrownType))
{
return true;
}
if (dynamic_cast<const __objc_id_type_info*>(thrownType))
{
return true;
}
return false;
};
//static gnustep::libobjc::__objc_id_type_info objc_id_type_info;
extern "C"
{
//gnustep::libobjc::__objc_id_type_info *__objc_id_type_info = &objc_id_type_info;
gnustep::libobjc::__objc_id_type_info __objc_id_type_info;
static void exception_cleanup(_Unwind_Reason_Code reason,
struct _Unwind_Exception *ex)
{
__cxa_exception *cxxex = (__cxa_exception*) ((char*)ex - offsetof(struct __cxa_exception, unwindHeader));
if (cxxex->exceptionType != &__objc_id_type_info)
{
delete cxxex->exceptionType;
}
__cxa_free_exception((void*)ex);
}
struct _Unwind_Exception *objc_init_cxx_exception(void *thrown_exception)
{
__cxa_exception *ex = ((__cxa_exception*)thrown_exception) - 1;
std::type_info *tinfo = &__objc_id_type_info;
ex->exceptionType = tinfo;
ex->exceptionDestructor = 0;
ex->unwindHeader.exception_class = EXCEPTION_CLASS('G','N','U','C','C','+','+','\0');
ex->unwindHeader.exception_cleanup = exception_cleanup;
return &ex->unwindHeader;
}
void* objc_object_for_cxx_exception(void *thrown_exception)
{
__cxa_exception *ex = (__cxa_exception*) ((char*)thrown_exception -
offsetof(struct __cxa_exception, unwindHeader));
const std::type_info *thrownType = ex->exceptionType;
if (!dynamic_cast<const gnustep::libobjc::__objc_id_type_info*>(thrownType) &&
!dynamic_cast<const gnustep::libobjc::__objc_class_type_info*>(thrownType))
{
return (id)-1;
}
return (id)(ex-1);
}
void print_type_info(void *thrown_exception)
{
__cxa_exception *ex = (__cxa_exception*) ((char*)thrown_exception -
offsetof(struct __cxa_exception, unwindHeader));
fprintf(stderr, "Type info: %s\n", ex->exceptionType->name());
fprintf(stderr, "offset is: %d\n", offsetof(struct __cxa_exception, unwindHeader));
}
} // extern "C"

19
objcxx_eh.h Normal file
View File

@ -0,0 +1,19 @@
#ifdef __cplusplus
extern "C" {
#endif
void *__cxa_allocate_exception(size_t thrown_size);
struct _Unwind_Exception *objc_init_cxx_exception(void *thrown_exception);
_Unwind_Reason_Code __gxx_personality_v0(int version,
_Unwind_Action actions,
uint64_t exceptionClass,
struct _Unwind_Exception *exceptionObject,
struct _Unwind_Context *context);
void __cxa_free_exception(void *thrown_exception);
void *objc_object_for_cxx_exception(void *thrown_exception);
void print_type_info(void *thrown_exception);
#ifdef __cplusplus
}
#endif