Index: include/exception =================================================================== --- include/exception +++ include/exception @@ -134,8 +134,6 @@ _LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT; _LIBCPP_NORETURN _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr); -#ifndef _LIBCPP_ABI_MICROSOFT - class _LIBCPP_TYPE_VIS exception_ptr { void* __ptr_; @@ -158,10 +156,19 @@ bool operator!=(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT {return !(__x == __y);} + friend _LIBCPP_FUNC_VIS void swap(exception_ptr& __x, exception_ptr& __y) _NOEXCEPT + { + void* __tmp = __x.__ptr_; + __x.__ptr_ = __y.__ptr_; + __y.__ptr_ = __tmp; + } + friend _LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT; friend _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr); }; +#ifndef _LIBCPP_ABI_MICROSOFT + template exception_ptr make_exception_ptr(_Ep __e) _NOEXCEPT @@ -183,39 +190,7 @@ #else // _LIBCPP_ABI_MICROSOFT -class _LIBCPP_TYPE_VIS exception_ptr -{ -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-private-field" -#endif - void* __ptr1_; - void* __ptr2_; -#if defined(__clang__) -#pragma clang diagnostic pop -#endif -public: - exception_ptr() _NOEXCEPT; - exception_ptr(nullptr_t) _NOEXCEPT; - exception_ptr(const exception_ptr& __other) _NOEXCEPT; - exception_ptr& operator=(const exception_ptr& __other) _NOEXCEPT; - exception_ptr& operator=(nullptr_t) _NOEXCEPT; - ~exception_ptr() _NOEXCEPT; - _LIBCPP_EXPLICIT operator bool() const _NOEXCEPT; -}; - -_LIBCPP_FUNC_VIS -bool operator==(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT; - -inline _LIBCPP_INLINE_VISIBILITY -bool operator!=(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT - {return !(__x == __y);} - -_LIBCPP_FUNC_VIS void swap(exception_ptr&, exception_ptr&) _NOEXCEPT; - _LIBCPP_FUNC_VIS exception_ptr __copy_exception_ptr(void *__except, const void* __ptr); -_LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT; -_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr p); // This is a built-in template function which automagically extracts the required // information. @@ -229,6 +204,7 @@ } #endif // _LIBCPP_ABI_MICROSOFT + // nested_exception class _LIBCPP_EXCEPTION_ABI nested_exception Index: src/support/runtime/exception_pointer_msvc.ipp =================================================================== --- src/support/runtime/exception_pointer_msvc.ipp +++ src/support/runtime/exception_pointer_msvc.ipp @@ -8,84 +8,237 @@ // //===----------------------------------------------------------------------===// -#include -#include +#include +#include +#include +#include +#include // For RtlPcToFileHeader function -#if !defined(_CRTIMP2_PURE) -#define _CRTIMP2_PURE __declspec(dllimport) +struct EHCatchableType { + uint32_t properties; + int32_t type_info; + uint32_t non_virtual_adjustment; + uint32_t offset_to_virtual_base_ptr; + uint32_t virtual_base_table_index; + uint32_t size; + int32_t copy_function; +}; + +struct EHCatchableTypeArray { + uint32_t catchable_types; + // It is variable size but we only need the first element of this array + int32_t array_of_catchable_types[1]; +}; + +struct EHThrowInfo { + uint32_t attributes; + int32_t unwind; + int32_t forward_compat; + int32_t catchable_type_array; +}; + +struct EHParameters { + uint32_t magic_number; + void* exception_object; + EHThrowInfo* throw_info; +#ifdef _M_AMD64 + uintptr_t throw_image_base; #endif +}; -#if !defined(__CLRCALL_PURE_OR_CDECL) -#define __CLRCALL_PURE_OR_CDECL __cdecl +struct EHExceptionRecord { + uint32_t exception_code; + uint32_t exception_flags; + void* exception_record; + void* exception_address; + uint32_t number_of_parameters; + EHParameters parameters; +}; + +// defined in vcruntime.dll +extern "C" EHExceptionRecord** __current_exception(); + +// This is internal compiler definition for MSVC but not for clang. +// We use our own EHThrowInfo because _ThrowInfo doesn't match actual +// compiler-generated structures in 64-bit mode. +#ifdef __clang__ +struct _ThrowInfo; +// defined in vcruntime.dll +_LIBCPP_NORETURN extern "C" void __stdcall _CxxThrowException( + void* __exc, _ThrowInfo* __throw_info); #endif -_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCreate(void*); -_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrDestroy(void*); -_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCopy(void*, - const void*); -_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL -__ExceptionPtrAssign(void*, const void*); -_CRTIMP2_PURE bool __CLRCALL_PURE_OR_CDECL -__ExceptionPtrCompare(const void*, const void*); -_CRTIMP2_PURE bool __CLRCALL_PURE_OR_CDECL -__ExceptionPtrToBool(const void*); -_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrSwap(void*, void*); -_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL -__ExceptionPtrCurrentException(void*); -[[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL -__ExceptionPtrRethrow(const void*); -_CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL -__ExceptionPtrCopyException(void*, const void*, const void*); +namespace { +struct ExceptionPtr { + void* exception_object; + const EHThrowInfo* throw_info; + std::atomic counter; +#ifdef _M_AMD64 + PVOID image_base; +#endif + template + T convert(int32_t offset) { +#ifdef _M_AMD64 + uintptr_t value = reinterpret_cast(image_base) + + static_cast(offset); +#else + uintptr_t value = static_cast(offset); +#endif + T res; + static_assert( + sizeof(value) == sizeof(res), + "Can only convert to pointers or pointers to member functions"); + memcpy(&res, &value, sizeof(value)); + return res; + } -namespace std { + void copy(void* dst, const void* src, const EHCatchableType* exc_type) { + struct Temp {}; + constexpr uint32_t virtual_base = 4; + if (exc_type->copy_function == 0) { + memcpy(dst, src, exc_type->size); + } else if (exc_type->properties & virtual_base) { + auto copy_constructor = + convert(exc_type->copy_function); + ((Temp*)dst->*copy_constructor)(src, 1); + } else { + auto copy_constructor = + convert(exc_type->copy_function); + ((Temp*)dst->*copy_constructor)(src); + } + } -exception_ptr::exception_ptr() _NOEXCEPT { __ExceptionPtrCreate(this); } -exception_ptr::exception_ptr(nullptr_t) _NOEXCEPT { __ExceptionPtrCreate(this); } + EHCatchableType* exception_type() { + return convert( + convert(throw_info->catchable_type_array) + ->array_of_catchable_types[0]); + } -exception_ptr::exception_ptr(const exception_ptr& __other) _NOEXCEPT { - __ExceptionPtrCopy(this, &__other); -} -exception_ptr& exception_ptr::operator=(const exception_ptr& __other) _NOEXCEPT { - __ExceptionPtrAssign(this, &__other); - return *this; -} + ExceptionPtr(const void* exception_object_, const EHThrowInfo* throw_info_) + : exception_object(nullptr), throw_info(throw_info_), counter(1) { +#ifdef _M_AMD64 + RtlPcToFileHeader( + reinterpret_cast(const_cast(throw_info)), + &image_base); +#endif + EHCatchableType* exc_type = exception_type(); + this->exception_object = malloc(exc_type->size); + if (this->exception_object == nullptr) { + throw std::bad_alloc(); + } + copy(exception_object, exception_object_, exc_type); + } -exception_ptr& exception_ptr::operator=(nullptr_t) _NOEXCEPT { - exception_ptr dummy; - __ExceptionPtrAssign(this, &dummy); - return *this; -} + ~ExceptionPtr() { + if (throw_info->unwind && exception_object) { + struct Temp {}; + auto destructor = convert(throw_info->unwind); + ((Temp*)exception_object->*destructor)(); + } + free(exception_object); + } -exception_ptr::~exception_ptr() _NOEXCEPT { __ExceptionPtrDestroy(this); } + static ExceptionPtr bad_alloc; + static ExceptionPtr bad_exception; +}; -exception_ptr::operator bool() const _NOEXCEPT { - return __ExceptionPtrToBool(this); +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Waddress-of-temporary" +#endif + +ExceptionPtr ExceptionPtr::bad_alloc( + &std::bad_alloc(), + reinterpret_cast(__GetExceptionInfo(std::bad_alloc()))); + +ExceptionPtr +ExceptionPtr::bad_exception(&std::bad_exception(), + reinterpret_cast( + __GetExceptionInfo(std::bad_exception()))); + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +} // namespace + +namespace std { + +exception_ptr::exception_ptr(const exception_ptr& __other) _NOEXCEPT + : __ptr_(__other.__ptr_) { + if (__ptr_) { + reinterpret_cast(__ptr_)->counter.fetch_add(1); + } } -bool operator==(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT { - return __ExceptionPtrCompare(&__x, &__y); +exception_ptr& exception_ptr:: +operator=(const exception_ptr& __other) _NOEXCEPT { + auto before = __ptr_; + __ptr_ = __other.__ptr_; + if (__ptr_) { + reinterpret_cast(__ptr_)->counter.fetch_add(1); + } + if (before) { + if (reinterpret_cast(before)->counter.fetch_sub(1) == 1) { + delete reinterpret_cast(before); + } + } + return *this; } - -void swap(exception_ptr& lhs, exception_ptr& rhs) _NOEXCEPT { - __ExceptionPtrSwap(&rhs, &lhs); +exception_ptr::~exception_ptr() _NOEXCEPT { + if (__ptr_) { + if (reinterpret_cast(__ptr_)->counter.fetch_sub(1) == 1) { + delete reinterpret_cast(__ptr_); + } + } } -exception_ptr __copy_exception_ptr(void* __except, const void* __ptr) { - exception_ptr __ret = nullptr; - if (__ptr) - __ExceptionPtrCopyException(&__ret, __except, __ptr); - return __ret; +exception_ptr __copy_exception_ptr(void* exception_object, + const void* throw_info) { + ExceptionPtr* ptr; + try { + ptr = new ExceptionPtr(exception_object, + reinterpret_cast(throw_info)); + } catch (const std::bad_alloc&) { + ptr = &ExceptionPtr::bad_alloc; + ptr->counter.fetch_add(1); + } catch (...) { + ptr = &ExceptionPtr::bad_exception; + ptr->counter.fetch_add(1); + } + exception_ptr res; + memcpy(&res, &ptr, sizeof(ptr)); + return res; } exception_ptr current_exception() _NOEXCEPT { - exception_ptr __ret; - __ExceptionPtrCurrentException(&__ret); - return __ret; + EHExceptionRecord** record = __current_exception(); + if (*record && !std::uncaught_exception()) { + return __copy_exception_ptr((*record)->parameters.exception_object, + (*record)->parameters.throw_info); + } + return exception_ptr(); } _LIBCPP_NORETURN -void rethrow_exception(exception_ptr p) { __ExceptionPtrRethrow(&p); } +void rethrow_exception(exception_ptr p) { + if (!p) { + throw std::bad_exception(); + } + ExceptionPtr* exc_ptr = reinterpret_cast(p.__ptr_); + EHCatchableType* exc_type = exc_ptr->exception_type(); + // _CxxThrowException doesn't call free on exception object so we must + // allocate it on the stack. + void* dst = _alloca(exc_type->size); + exc_ptr->copy(dst, exc_ptr->exception_object, exc_type); + auto throw_info = reinterpret_cast<_ThrowInfo*>( + const_cast(exc_ptr->throw_info)); + // For some reason clang doesn't call p destructor during unwinding. + // So we must clear it ourselves. + p = nullptr; + _CxxThrowException(dst, throw_info); +} nested_exception::nested_exception() _NOEXCEPT : __ptr_(current_exception()) {}