From a357cb999ce0723907dabb9bb4c51bcd4a1c8b9b Mon Sep 17 00:00:00 2001 From: Frederik Seiffert Date: Fri, 24 Dec 2021 18:41:35 +0100 Subject: [PATCH] tests: Test that we can throw and catch the same exception multiple times 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 Co-authored-by: David Chisnall --- Test/CMakeLists.txt | 1 + Test/ObjCXXEHInteropTwice.mm | 22 ++++++++++++++++++++++ eh_personality.c | 30 ++++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 Test/ObjCXXEHInteropTwice.mm diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index c9994b4..f0b58aa 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -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) diff --git a/Test/ObjCXXEHInteropTwice.mm b/Test/ObjCXXEHInteropTwice.mm new file mode 100644 index 0000000..11d542d --- /dev/null +++ b/Test/ObjCXXEHInteropTwice.mm @@ -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]; +} + diff --git a/eh_personality.c b/eh_personality.c index 65a65d2..53704de 100644 --- a/eh_personality.c +++ b/eh_personality.c @@ -21,6 +21,24 @@ #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 @@ -28,6 +46,7 @@ void test_cxx_eh_implementation(); __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); /** @@ -93,7 +112,6 @@ enum exception_type struct thread_data { enum exception_type current_exception_type; - id lastThrownObject; BOOL cxxCaughtException; struct objc_exception *caughtExceptions; }; @@ -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(); } @@ -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);