Skip to content

Commit

Permalink
tests: Test that we can throw and catch the same exception multiple t…
Browse files Browse the repository at this point in the history
…imes sequentially. (#188)

This originally came up as an issue with libc++abi support (#152), but is not specific to that ABI.

* Use the C++ runtime to check for uncaught C++ exceptions.

As discussed in #152, use the function defined in the Itanium C++ ABI to
check whether the thrown exception is the current caught C++ exception
and needs rethrowing via `__cxa_rethrow()`.

Co-authored-by: Niels Grewe <grewe@ocean-insights.com>
Co-authored-by: David Chisnall <gnustep@theravensnest.org>
  • Loading branch information
3 people authored Dec 24, 2021
1 parent 14619f2 commit a357cb9
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 8 deletions.
1 change: 1 addition & 0 deletions Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ addtest_variants("CXXExceptions" "CXXException.m;CXXException.cc" true)
addtest_variants("ForwardDeclareProtocolAccess" "ForwardDeclareProtocolAccess.m;ForwardDeclareProtocol.m" true)
if (ENABLE_OBJCXX)
addtest_variants(ObjCXXEHInterop "ObjCXXEHInterop.mm;ObjCXXEHInterop.m" true)
addtest_variants(ObjCXXEHInteropTwice "ObjCXXEHInteropTwice.mm" true)
# This test is failing on Win32, but not for any obvious reason. Disable
# it for now to keep CI happy.
if (WIN32)
Expand Down
22 changes: 22 additions & 0 deletions Test/ObjCXXEHInteropTwice.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#import "Test.h"

#import "stdio.h"


void excerciseExceptionCXX(Test *e) {
@try {
printf("Raising Test\n");
@throw e;
} @catch (Test *localException) {
printf("Caught\n");
}
}

int main(void)
{
Test *e = [Test new];
excerciseExceptionCXX(e);
excerciseExceptionCXX(e);
[e release];
}

30 changes: 22 additions & 8 deletions eh_personality.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,32 @@
#endif

void test_cxx_eh_implementation();
/**
* The Itanium C++ public structure for in-flight exception status.
*/
struct __cxa_eh_globals
{
/**
* The head exception object. By convention, this is actually the end of
* the `__cxa_exception` structure and points to the address of the thrown
* object. This is either an `id*` or a pointer to a C++ type that we're
* not going to look at.
*/
struct __cxa_exception *caughtExceptions;
/**
* The number of in-flight exceptions thrown.
*/
unsigned int uncaughtExceptions;
};


// Weak references to C++ runtime functions. We don't bother testing that
// these are 0 before calling them, because if they are not resolved then we
// should not be in a code path that involves a C++ exception.
__attribute__((weak)) void *__cxa_begin_catch(void *e);
__attribute__((weak)) void __cxa_end_catch(void);
__attribute__((weak)) void __cxa_rethrow(void);
__attribute__((weak)) struct __cxa_eh_globals *__cxa_get_globals(void);


/**
Expand Down Expand Up @@ -93,7 +112,6 @@ enum exception_type
struct thread_data
{
enum exception_type current_exception_type;
id lastThrownObject;
BOOL cxxCaughtException;
struct objc_exception *caughtExceptions;
};
Expand Down Expand Up @@ -202,12 +220,9 @@ void objc_exception_throw(id object)
// cases.
if (td->cxxCaughtException)
{
// For catchalls, we may result in our being passed the pointer to the
// object, not the object.
if ((object == td->lastThrownObject) ||
((object != nil) &&
!isSmallObject(object) &&
(*(id*)object == td->lastThrownObject)))
struct __cxa_eh_globals *globals = __cxa_get_globals();
if ((globals->caughtExceptions != NULL) &&
(*(id*)globals->caughtExceptions == object))
{
__cxa_rethrow();
}
Expand All @@ -233,7 +248,6 @@ void objc_exception_throw(id object)

ex->object = object;

td->lastThrownObject = object;
td->cxxCaughtException = NO;

_Unwind_Reason_Code err = _Unwind_RaiseException(&ex->unwindHeader);
Expand Down

0 comments on commit a357cb9

Please sign in to comment.