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>main
parent
38efd04e33
commit
f352f62423
@ -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();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue