mirror of
https://github.com/darlinghq/darling-libobjc2.git
synced 2024-11-27 14:10:33 +00:00
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:
parent
f9abfb022a
commit
e0719a9c62
13
GNUmakefile
13
GNUmakefile
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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
181
objcxx_eh.cc
Normal 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
19
objcxx_eh.h
Normal 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
|
Loading…
Reference in New Issue
Block a user