typedef struct objc_object* id; #include #include #include "dwarf_eh.h" #include "objcxx_eh.h" #include #include "objc/runtime.h" namespace __cxxabiv1 { struct __class_type_info; } namespace std { struct type_info; } extern "C" void __cxa_throw(void *thrown_exception, std::type_info *tinfo, void (*dest)(void *)); #ifdef __GLIBCXX__ #include static void *__cxa_current_primary_exception() { std::exception_ptr p = std::current_exception(); void *obj = *(void**)&p; *(void**)&p = nullptr; return obj; } #else extern "C" void *__cxa_current_primary_exception(); #endif using namespace __cxxabiv1; // Define some C++ ABI types here, rather than including them. This prevents // conflicts with the libstdc++ headers, which expose only a subset of the // type_info class (the part required for standards compliance, not the // implementation details). typedef void (*unexpected_handler)(); typedef void (*terminate_handler)(); namespace std { /** * std::type_info defined with the GCC ABI. This may not be exposed in * public headers, but is required for correctly implementing the unified * exception model. */ class type_info { public: virtual ~type_info(); bool operator==(const type_info &) const; bool operator!=(const type_info &) const; bool before(const type_info &) const; type_info(); private: type_info(const type_info& rhs); type_info& operator= (const type_info& rhs); const char *__type_name; protected: type_info(const char *name): __type_name(name) { } public: const char* name() const { return __type_name; } virtual bool __is_pointer_p() const; virtual bool __is_function_p() const; virtual bool __do_catch(const type_info *thrown_type, void **thrown_object, unsigned outer) const; virtual bool __do_upcast( const __class_type_info *target, void **thrown_object) const; }; } using namespace std; static std::atomic exception_object_offset; static std::atomic exception_type_offset; static BOOL isKindOfClass(Class thrown, Class type) { do { if (thrown == type) { return YES; } thrown = class_getSuperclass(thrown); } while (Nil != thrown); return NO; } 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; }; } }; static bool AppleCompatibleMode = true; extern "C" int objc_set_apple_compatible_objcxx_exceptions(int newValue) { bool old = AppleCompatibleMode; AppleCompatibleMode = newValue; return old; } 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 = nullptr; bool found = false; // Id throw matches any ObjC catch. This may be a silly idea! if (dynamic_cast(thrownType) || (AppleCompatibleMode && dynamic_cast(thrownType))) { thrown = **(id**)obj; // nil only matches id catch handlers in Apple-compatible mode, or when thrown as an id if (0 == thrown) { return false; } // Check whether the real thrown object matches the catch type. found = isKindOfClass(object_getClass(thrown), (Class)objc_getClass(name())); } else if (dynamic_cast(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(thrownType)) { *obj = **(id**)obj; return true; } if (dynamic_cast(thrownType)) { *obj = **(id**)obj; return true; } return false; }; /** * Public interface to the Objective-C++ exception mechanism */ extern "C" { /** * The public symbol that the compiler uses to indicate the Objective-C id type. */ gnustep::libobjc::__objc_id_type_info __objc_id_type_info; struct _Unwind_Exception *objc_init_cxx_exception(id obj) { void *cxxexception = nullptr; try { id *exception_object = static_cast(__cxa_allocate_exception(sizeof(id))); *exception_object = obj; __cxa_throw(exception_object, &__objc_id_type_info, nullptr); } catch (...) { cxxexception = __cxa_current_primary_exception(); } assert(cxxexception); uint64_t *ehcls = reinterpret_cast(cxxexception); ehcls--; int count = 1; while (*ehcls != cxx_exception_class) { ehcls--; count++; assert((count < 8) && "Exception structure appears to be corrupt"); } ptrdiff_t displacement = reinterpret_cast(cxxexception) - reinterpret_cast(ehcls); assert((exception_object_offset == 0) || (exception_object_offset == displacement)); exception_object_offset = displacement; std::type_info **ehtype = reinterpret_cast(ehcls); ehtype--; count = 1; while (*ehtype != &__objc_id_type_info) { ehtype--; count++; assert((count < 32) && "Exception structure appears to be corrupt"); } displacement = reinterpret_cast(ehtype) - reinterpret_cast(ehcls); assert((exception_type_offset == 0) || (exception_type_offset == displacement)); exception_type_offset = displacement; return reinterpret_cast<_Unwind_Exception*>(ehcls); } void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid) { ptrdiff_t type_offset = exception_type_offset; if (type_offset == 0) { *isValid = 0; return nullptr; } const std::type_info *thrownType = *reinterpret_cast(reinterpret_cast(thrown_exception) + type_offset); if (!dynamic_cast(thrownType) && !dynamic_cast(thrownType)) { *isValid = 0; return 0; } *isValid = 1; return *reinterpret_cast(reinterpret_cast(thrown_exception) + exception_object_offset); } /* 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"