forked from gnustep/libobjc2
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a MSVC/VS2017-compatible objc_exception_{,re}throw
This implementation of objc_exception_throw constructs a _CxxThrowException-compatible ThrowInfo based on the runtime type of the passed-in exception. Everything pertaining to the exception is allocated on the stack before the call and destroyed when/if the stack frame is aborted. The construction of ThrowInfo, CatchableTypes, and TypeDescriptors, as well as mangling, is compatible with Clang/LLVM's MicrosoftCXXABI exception generator. This ties Objective-C exceptions on Windows to the C++ exception model, which allows us to support (unboxed) foreign exceptions and C++ catch of Objective-C types. objc_exception_rethrow recycles MicrosoftCXXABI's rethrow. Contributing-author: Jordan Saunders <jmsaunde@microsoft.com> Contributing-author: Shayne Hiet-Block <shaynehi@microsoft.com>
- Loading branch information
Showing
1 changed file
with
170 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
#include <stdlib.h> | ||
#include <stdio.h> | ||
#include <string.h> | ||
#include <vector> | ||
|
||
#include "objc/runtime.h" | ||
#include "visibility.h" | ||
|
||
#ifndef __has_builtin | ||
#define __has_builtin(x) 0 | ||
#endif | ||
|
||
#if !__has_builtin(__builtin_unreachable) | ||
#define __builtin_unreachable abort | ||
#endif | ||
|
||
struct _MSVC_TypeDescriptor | ||
{ | ||
const void* pVFTable; | ||
void* spare; | ||
char name[0]; | ||
}; | ||
|
||
struct _MSVC_CatchableType | ||
{ | ||
unsigned int flags; | ||
_MSVC_TypeDescriptor* type; | ||
int mdisp; | ||
int pdisp; | ||
int vdisp; | ||
int size; | ||
void* copyFunction; | ||
}; | ||
|
||
struct _MSVC_CatchableTypeArray | ||
{ | ||
int count; | ||
_MSVC_CatchableType* types[0]; | ||
}; | ||
|
||
struct _MSVC_ThrowInfo | ||
{ | ||
unsigned int attributes; | ||
void* pfnUnwind; | ||
void* pfnForwardCompat; | ||
_MSVC_CatchableTypeArray* pCatchableTypeArray; | ||
}; | ||
|
||
#if defined(_WIN64) | ||
extern "C" int __ImageBase; | ||
#define IMAGE_RELATIVE(ptr) ((decltype(ptr))(ptr ? ((uintptr_t)ptr - (uintptr_t)&__ImageBase) : (uintptr_t)nullptr)) | ||
#else | ||
#define IMAGE_RELATIVE(ptr) (ptr) | ||
#endif | ||
|
||
extern "C" void __stdcall _CxxThrowException(void*, _MSVC_ThrowInfo*); | ||
|
||
namespace | ||
{ | ||
|
||
static std::string mangleObjcObject() | ||
{ | ||
// This mangled name doesn't vary based on bitness. | ||
return ".PAUobjc_object@@"; | ||
} | ||
|
||
static std::string mangleStructNamed(const char* className) | ||
{ | ||
// 32-bit: | ||
// .PAUxxx@@ = ?? struct xxx * `RTTI Type Descriptor' | ||
// 64-bit: | ||
// .PEAUxxx@@ = ?? struct xxx * __ptr64 `RTTI Type Descriptor' | ||
return | ||
#if defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8 | ||
std::string(".PEAU") + | ||
#else | ||
std::string(".PAU") + | ||
#endif | ||
className + "@@"; | ||
} | ||
|
||
void fillCatchableType(_MSVC_CatchableType* exceptType) | ||
{ | ||
exceptType->flags = 1; | ||
exceptType->mdisp = 0; | ||
exceptType->pdisp = -1; | ||
exceptType->vdisp = 0; | ||
exceptType->size = sizeof(id); | ||
exceptType->copyFunction = nullptr; | ||
} | ||
|
||
} // <anonymous-namespace> | ||
|
||
PUBLIC extern "C" void objc_exception_throw(id object) | ||
{ | ||
// This is the base vtable for all RTTI entries | ||
static const void* typeid_vtable = *(void**)&typeid(void *); | ||
|
||
SEL rethrow_sel = sel_registerName("rethrow"); | ||
if ((nil != object) && | ||
(class_respondsToSelector(object_getClass(object), rethrow_sel))) | ||
{ | ||
IMP rethrow = objc_msg_lookup(object, rethrow_sel); | ||
rethrow(object, rethrow_sel); | ||
// Should not be reached! If it is, then the rethrow method actually | ||
// didn't, so we throw it normally. | ||
} | ||
|
||
SEL processException_sel = sel_registerName("_processException"); | ||
if ((nil != object) && | ||
(class_respondsToSelector(object_getClass(object), processException_sel))) | ||
{ | ||
IMP processException = objc_msg_lookup(object, processException_sel); | ||
processException(object, processException_sel); | ||
} | ||
|
||
// The 'id' base type will be taking up a spot in the list: | ||
size_t typeCount = 1; | ||
|
||
// Get count of all types in exception | ||
for (Class cls = object_getClass(object); cls != nil; cls = class_getSuperclass(cls), ++typeCount) | ||
; | ||
|
||
// Unfortunately we can't put this in a real function since the alloca has to be in this stack frame: | ||
#define CREATE_TYPE_DESCRIPTOR(desc, symName) \ | ||
desc = reinterpret_cast<_MSVC_TypeDescriptor*>(alloca(sizeof(_MSVC_TypeDescriptor) + symName.size() + 1 /* null terminator */)); \ | ||
desc->pVFTable = typeid_vtable; \ | ||
desc->spare = nullptr; \ | ||
strcpy_s(desc->name, symName.size() + 1, symName.c_str()); | ||
|
||
auto exceptTypes = | ||
(_MSVC_CatchableTypeArray*)_alloca(sizeof(_MSVC_CatchableTypeArray) + sizeof(_MSVC_CatchableType*) * typeCount); | ||
exceptTypes->count = typeCount; | ||
|
||
// Add exception type and all base types to throw information | ||
size_t curTypeIndex = 0; | ||
for (Class cls = object_getClass(object); cls != nil; cls = class_getSuperclass(cls)) | ||
{ | ||
auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType)); | ||
fillCatchableType(exceptType); | ||
|
||
auto mangledName = mangleStructNamed(class_getName(cls)); | ||
CREATE_TYPE_DESCRIPTOR(exceptType->type, mangledName); | ||
exceptType->type = IMAGE_RELATIVE(exceptType->type); | ||
exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType); | ||
} | ||
|
||
// Add id (struct objc_object*) | ||
auto exceptType = (_MSVC_CatchableType*)_alloca(sizeof(_MSVC_CatchableType)); | ||
fillCatchableType(exceptType); | ||
auto idName = mangleObjcObject(); | ||
CREATE_TYPE_DESCRIPTOR(exceptType->type, idName); | ||
exceptType->type = IMAGE_RELATIVE(exceptType->type); | ||
exceptTypes->types[curTypeIndex++] = IMAGE_RELATIVE(exceptType); | ||
|
||
_MSVC_ThrowInfo ti = { | ||
0, // attributes | ||
NULL, // pfnUnwind | ||
NULL, // pfnForwardCompat | ||
IMAGE_RELATIVE(exceptTypes) // pCatchableTypeArray | ||
}; | ||
_CxxThrowException(&object, &ti); | ||
__builtin_unreachable(); | ||
} | ||
|
||
PUBLIC extern "C" void objc_exception_rethrow(void* exc) | ||
{ | ||
_CxxThrowException(nullptr, nullptr); | ||
__builtin_unreachable(); | ||
} |