|
|
|
@ -1,16 +1,60 @@
|
|
|
|
typedef struct objc_object* id;
|
|
|
|
typedef struct objc_object* id;
|
|
|
|
|
|
|
|
#include <atomic>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "dwarf_eh.h"
|
|
|
|
#include "dwarf_eh.h"
|
|
|
|
#include "objcxx_eh.h"
|
|
|
|
#include "objcxx_eh.h"
|
|
|
|
#include <atomic>
|
|
|
|
#include "visibility.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "objc/runtime.h"
|
|
|
|
#include "objc/runtime.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Helper function that has a custom personality function.
|
|
|
|
|
|
|
|
* This calls `cxx_throw` and has a destructor that must be run. We intercept
|
|
|
|
|
|
|
|
* the personality function calls and inspect the in-flight C++ exception.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
int eh_trampoline();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t cxx_exception_class;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern "C" void *__cxa_allocate_exception(size_t) noexcept;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Our own definitions of C++ ABI functions and types. These are provided
|
|
|
|
|
|
|
|
* because this file must not include cxxabi.h. We need to handle subtly
|
|
|
|
|
|
|
|
* different variations of the ABI and including one specific implementation
|
|
|
|
|
|
|
|
* would make that very difficult.
|
|
|
|
|
|
|
|
*/
|
|
|
|
namespace __cxxabiv1
|
|
|
|
namespace __cxxabiv1
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Type info for classes. Forward declared because the GNU ABI provides a
|
|
|
|
|
|
|
|
* method on all type_info objects that the dynamic the dynamic cast header
|
|
|
|
|
|
|
|
* needs.
|
|
|
|
|
|
|
|
*/
|
|
|
|
struct __class_type_info;
|
|
|
|
struct __class_type_info;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The C++ in-flight exception object. We will derive the offset of fields
|
|
|
|
|
|
|
|
* in this, so we do not ever actually see a concrete definition of it.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct __cxa_exception;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The public ABI structure for current exception state.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct __cxa_eh_globals
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The current exception that has been caught.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
__cxa_exception *caughtExceptions;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The number of uncaught exceptions still in flight.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
unsigned int uncaughtExceptions;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Retrieve the above structure.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C" __cxa_eh_globals *__cxa_get_globals();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
namespace std
|
|
|
|
namespace std
|
|
|
|
@ -18,13 +62,6 @@ namespace std
|
|
|
|
struct type_info;
|
|
|
|
struct type_info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
|
|
|
|
void __cxa_throw(void *thrown_exception, std::type_info *tinfo,
|
|
|
|
|
|
|
|
void (*dest)(void *));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
|
|
|
|
void *__cxa_current_primary_exception();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using namespace __cxxabiv1;
|
|
|
|
using namespace __cxxabiv1;
|
|
|
|
|
|
|
|
|
|
|
|
// Define some C++ ABI types here, rather than including them. This prevents
|
|
|
|
// Define some C++ ABI types here, rather than including them. This prevents
|
|
|
|
@ -69,10 +106,103 @@ namespace std
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
namespace
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Helper needed by the unwind helper headers.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
inline _Unwind_Reason_Code continueUnwinding(struct _Unwind_Exception *ex,
|
|
|
|
|
|
|
|
struct _Unwind_Context *context)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
#if defined(__arm__) && !defined(__ARM_DWARF_EH__)
|
|
|
|
|
|
|
|
if (__gnu_unwind_frame(ex, context) != _URC_OK) { return _URC_FAILURE; }
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return _URC_CONTINUE_UNWIND;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Flag indicating that we've already inspected a C++ exception and found all
|
|
|
|
|
|
|
|
* of the offsets.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
std::atomic<bool> done_setup;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The offset of the C++ type_info object in a thrown exception from the unwind
|
|
|
|
|
|
|
|
* header in a `__cxa_exception`.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
std::atomic<ptrdiff_t> type_info_offset;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The offset of the reference count in a
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
std::atomic<ptrdiff_t> refcount_offset;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The size of the `_Unwind_Exception` (including padding) in a
|
|
|
|
|
|
|
|
* `__cxa_exception`.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
std::atomic<size_t> exception_struct_size;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Helper function to find a particular value scanning backwards in a
|
|
|
|
|
|
|
|
* structure.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
|
|
|
ptrdiff_t find_backwards(void *addr, T val)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
T *ptr = reinterpret_cast<T*>(addr);
|
|
|
|
|
|
|
|
for (ptrdiff_t disp = -1 ; (disp * sizeof(T) > -128) ; disp--)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (ptr[disp] == val)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return disp * sizeof(T);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Unable to find field in C++ exception structure\n");
|
|
|
|
|
|
|
|
abort();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Helper function to find a particular value scanning forwards in a
|
|
|
|
|
|
|
|
* structure.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
|
|
|
ptrdiff_t find_forwards(void *addr, T val)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
T *ptr = reinterpret_cast<T*>(addr);
|
|
|
|
|
|
|
|
for (ptrdiff_t disp = 0 ; (disp * sizeof(T) < 256) ; disp++)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (ptr[disp] == val)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return disp * sizeof(T);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(stderr, "Unable to find field in C++ exception structure\n");
|
|
|
|
|
|
|
|
abort();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
|
|
|
|
T *pointer_add(void *ptr, ptrdiff_t offset)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return reinterpret_cast<T*>(reinterpret_cast<char*>(ptr) + offset);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Exception cleanup function for C++ exceptions that wrap Objective-C
|
|
|
|
|
|
|
|
* exceptions.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
void exception_cleanup(_Unwind_Reason_Code reason,
|
|
|
|
|
|
|
|
struct _Unwind_Exception *ex)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// __cxa_exception takes a pointer to the end of the __cxa_exception
|
|
|
|
|
|
|
|
// structure, and so we find that by adding the size of the generic
|
|
|
|
|
|
|
|
// exception structure + padding to the pointer to the generic exception
|
|
|
|
|
|
|
|
// structure field of the enclosing structure.
|
|
|
|
|
|
|
|
auto *cxxEx = pointer_add<__cxa_exception>(ex, exception_struct_size);
|
|
|
|
|
|
|
|
__cxa_free_exception(cxxEx);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static std::atomic<ptrdiff_t> exception_object_offset;
|
|
|
|
}
|
|
|
|
static std::atomic<ptrdiff_t> exception_type_offset;
|
|
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL isKindOfClass(Class thrown, Class type)
|
|
|
|
static BOOL isKindOfClass(Class thrown, Class type)
|
|
|
|
@ -189,58 +319,28 @@ gnustep::libobjc::__objc_id_type_info __objc_id_type_info;
|
|
|
|
|
|
|
|
|
|
|
|
struct _Unwind_Exception *objc_init_cxx_exception(id obj)
|
|
|
|
struct _Unwind_Exception *objc_init_cxx_exception(id obj)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
void *cxxexception = nullptr;
|
|
|
|
id *newEx = static_cast<id*>(__cxa_allocate_exception(sizeof(id)));
|
|
|
|
try
|
|
|
|
*newEx = obj;
|
|
|
|
{
|
|
|
|
_Unwind_Exception *ex = pointer_add<_Unwind_Exception>(newEx, -exception_struct_size);
|
|
|
|
id *exception_object = static_cast<id*>(__cxa_allocate_exception(sizeof(id)));
|
|
|
|
*pointer_add<std::type_info*>(ex, type_info_offset) = &__objc_id_type_info;
|
|
|
|
*exception_object = obj;
|
|
|
|
ex->exception_class = cxx_exception_class;
|
|
|
|
__cxa_throw(exception_object, &__objc_id_type_info, nullptr);
|
|
|
|
ex->exception_cleanup = exception_cleanup;
|
|
|
|
}
|
|
|
|
__cxa_get_globals()->uncaughtExceptions++;
|
|
|
|
catch (...)
|
|
|
|
return ex;
|
|
|
|
{
|
|
|
|
|
|
|
|
cxxexception = __cxa_current_primary_exception();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(cxxexception);
|
|
|
|
|
|
|
|
uint64_t *ehcls = reinterpret_cast<uint64_t*>(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<const char*>(cxxexception) - reinterpret_cast<const char*>(ehcls);
|
|
|
|
|
|
|
|
assert((exception_object_offset == 0) || (exception_object_offset == displacement));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exception_object_offset = displacement;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::type_info **ehtype = reinterpret_cast<std::type_info**>(ehcls);
|
|
|
|
|
|
|
|
ehtype--;
|
|
|
|
|
|
|
|
count = 1;
|
|
|
|
|
|
|
|
while (*ehtype != &__objc_id_type_info)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
ehtype--;
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
assert((count < 32) && "Exception structure appears to be corrupt");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
displacement = reinterpret_cast<const char*>(ehtype) - reinterpret_cast<const char*>(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)
|
|
|
|
void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
ptrdiff_t type_offset = exception_type_offset;
|
|
|
|
ptrdiff_t type_offset = type_info_offset;
|
|
|
|
if (type_offset == 0)
|
|
|
|
if (type_offset == 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
*isValid = 0;
|
|
|
|
*isValid = 0;
|
|
|
|
return nullptr;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const std::type_info *thrownType =
|
|
|
|
const std::type_info *thrownType =
|
|
|
|
*reinterpret_cast<const std::type_info**>(reinterpret_cast<char*>(thrown_exception) + type_offset);
|
|
|
|
*pointer_add<const std::type_info*>(thrown_exception, type_offset);
|
|
|
|
|
|
|
|
|
|
|
|
if (!dynamic_cast<const gnustep::libobjc::__objc_id_type_info*>(thrownType) &&
|
|
|
|
if (!dynamic_cast<const gnustep::libobjc::__objc_id_type_info*>(thrownType) &&
|
|
|
|
!dynamic_cast<const gnustep::libobjc::__objc_class_type_info*>(thrownType))
|
|
|
|
!dynamic_cast<const gnustep::libobjc::__objc_class_type_info*>(thrownType))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -248,18 +348,113 @@ void* objc_object_for_cxx_exception(void *thrown_exception, int *isValid)
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*isValid = 1;
|
|
|
|
*isValid = 1;
|
|
|
|
return *reinterpret_cast<id*>(reinterpret_cast<char*>(thrown_exception) + exception_object_offset);
|
|
|
|
return *pointer_add<id>(thrown_exception, exception_struct_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
} // extern "C"
|
|
|
|
void print_type_info(void *thrown_exception)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* C++ structure that is thrown through a frame with the `test_eh_personality`
|
|
|
|
|
|
|
|
* personality function. This contains a well-known value that we can search
|
|
|
|
|
|
|
|
* for after the unwind header.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct
|
|
|
|
|
|
|
|
PRIVATE
|
|
|
|
|
|
|
|
MagicValueHolder
|
|
|
|
{
|
|
|
|
{
|
|
|
|
__cxa_exception *ex = (__cxa_exception*) ((char*)thrown_exception -
|
|
|
|
/**
|
|
|
|
offsetof(struct __cxa_exception, unwindHeader));
|
|
|
|
* The constant that we will search for to identify this object.
|
|
|
|
fprintf(stderr, "Type info: %s\n", ex->exceptionType->name());
|
|
|
|
*/
|
|
|
|
fprintf(stderr, "offset is: %d\n", offsetof(struct __cxa_exception, unwindHeader));
|
|
|
|
static constexpr uint32_t magic = 0x01020304;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The single field in this structure.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
uint32_t magic_value;
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Constructor. Initialises the field with the magic constant.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
MagicValueHolder() { magic_value = magic; }
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Function that simply throws an instance of `MagicValueHolder`.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
PRIVATE void cxx_throw()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
MagicValueHolder x;
|
|
|
|
|
|
|
|
throw x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // extern "C"
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Personality function that wraps the C++ personality and inspects the C++
|
|
|
|
|
|
|
|
* exception structure on the way past. This should be used only for the
|
|
|
|
|
|
|
|
* `eh_trampoline` function.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
|
|
|
|
PRIVATE
|
|
|
|
|
|
|
|
BEGIN_PERSONALITY_FUNCTION(test_eh_personality)
|
|
|
|
|
|
|
|
// Don't bother with a mutex here. It doesn't matter if two threads set
|
|
|
|
|
|
|
|
// these values at the same time.
|
|
|
|
|
|
|
|
if (!done_setup)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
uint64_t cls = __builtin_bswap64(exceptionClass);
|
|
|
|
|
|
|
|
type_info_offset = find_backwards(exceptionObject, &typeid(MagicValueHolder));
|
|
|
|
|
|
|
|
#ifdef __LP64__
|
|
|
|
|
|
|
|
// On 64-bit platforms, the refcount is added to the front of the
|
|
|
|
|
|
|
|
// structure.
|
|
|
|
|
|
|
|
ptrdiff_t refcount_backwards_offset = type_info_offset - sizeof(uintptr_t);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
// On 32-bit platforms, this should be immediately before the
|
|
|
|
|
|
|
|
// _Unwind_Exception in some spare padding, but libsupc++ puts it in
|
|
|
|
|
|
|
|
// the same place as for 64-bit. Try the one that's definitely in the
|
|
|
|
|
|
|
|
// object first and then fall back to the other...
|
|
|
|
|
|
|
|
ptrdiff_t refcount_backwards_offset = -sizeof(uint32_t);
|
|
|
|
|
|
|
|
auto read_offset = [](void *obj, ptrdiff_t offset)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
char *addr = reinterpret_cast<char*>(obj) + offset;
|
|
|
|
|
|
|
|
uintptr_t v = *reinterpret_cast<uintptr_t*>(addr);
|
|
|
|
|
|
|
|
return v;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
if (read_offset(exceptionObject, refcount_backwards_offset) != 1)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
refcount_backwards_offset = type_info_offset - sizeof(uintptr_t);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (read_offset(exceptionObject, refcount_backwards_offset) != 1)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fprintf(stderr, "Unable to find refcount field\n");
|
|
|
|
|
|
|
|
abort();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
exception_struct_size = find_forwards(exceptionObject, MagicValueHolder::magic);
|
|
|
|
|
|
|
|
cxx_exception_class = exceptionClass;
|
|
|
|
|
|
|
|
done_setup = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return CALL_PERSONALITY_FUNCTION(__gxx_personality_v0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Probe the C++ exception handling implementation. This throws a C++
|
|
|
|
|
|
|
|
* exception through a function that uses `test_eh_personality` as its
|
|
|
|
|
|
|
|
* personality function, allowing us to inspect a C++ exception that is in a
|
|
|
|
|
|
|
|
* known state.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
extern "C" void test_cxx_eh_implementation()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (done_setup)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool caught = false;
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
eh_trampoline();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
catch(MagicValueHolder)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
caught = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(caught);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|