diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c9cb98..23943cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,7 @@ set(libobjc_HDRS objc/objc-arc.h objc/objc-auto.h objc/objc-class.h + objc/objc-exception.h objc/objc-runtime.h objc/objc-visibility.h objc/objc.h diff --git a/Test/UnexpectedException.m b/Test/UnexpectedException.m index 0f48069..c2e1b03 100644 --- a/Test/UnexpectedException.m +++ b/Test/UnexpectedException.m @@ -34,7 +34,7 @@ int main(void) SetUnhandledExceptionFilter(&_UnhandledExceptionFilter); #endif - _objc_unexpected_exception = _UncaughtExceptionHandler; + objc_setUncaughtExceptionHandler(_UncaughtExceptionHandler); @throw exceptionObj; assert(0 && "should not be reached!"); diff --git a/eh_personality.c b/eh_personality.c index 53704de..1910f06 100644 --- a/eh_personality.c +++ b/eh_personality.c @@ -4,6 +4,7 @@ #include "dwarf_eh.h" #include "objc/runtime.h" #include "objc/hooks.h" +#include "objc/objc-exception.h" #include "class.h" #include "objcxx_eh.h" @@ -760,3 +761,8 @@ void objc_exception_rethrow(struct _Unwind_Exception *e) _Unwind_Resume_or_Rethrow(e); abort(); } + +objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler) +{ + return __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST); +} diff --git a/eh_win32_msvc.cc b/eh_win32_msvc.cc index 319408c..997dce5 100644 --- a/eh_win32_msvc.cc +++ b/eh_win32_msvc.cc @@ -4,7 +4,7 @@ #include #include "objc/runtime.h" -#include "objc/hooks.h" +#include "objc/objc-exception.h" #include "visibility.h" #include @@ -57,6 +57,7 @@ struct _MSVC_ThrowInfo }; static LPTOP_LEVEL_EXCEPTION_FILTER originalUnhandledExceptionFilter = nullptr; +void (*_objc_unexpected_exception)(id exception); LONG WINAPI _objc_unhandled_exception_filter(struct _EXCEPTION_POINTERS* exceptionInfo); #if defined(_WIN64) @@ -197,17 +198,6 @@ OBJC_PUBLIC extern "C" void objc_exception_throw(id object) exception.ExceptionInformation[2] = reinterpret_cast(&ti); exception.ExceptionInformation[3] = reinterpret_cast(&x); - // Set unhandled exception filter to support _objc_unexpected_exception hook. - // Unfortunately there doesn't seem to be a better place to call this, as - // installing it at construction time means that it will get overwritten by - // the handler installed by the VC runtime. This way it works, but as a side - // effect throwing exceptions will overwrite any previously installed handler, - // so we save it and call it as part of our handler. - LPTOP_LEVEL_EXCEPTION_FILTER previousExceptionFilter = SetUnhandledExceptionFilter(&_objc_unhandled_exception_filter); - if (previousExceptionFilter != &_objc_unhandled_exception_filter) { - originalUnhandledExceptionFilter = previousExceptionFilter; - } - #ifdef _WIN64 RtlRaiseException(&exception); #else @@ -282,3 +272,16 @@ LONG WINAPI _objc_unhandled_exception_filter(struct _EXCEPTION_POINTERS* excepti // Since this is the last one, it is not likely to find any more. return EXCEPTION_CONTINUE_SEARCH; } + +OBJC_PUBLIC extern "C" objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler) +{ + objc_uncaught_exception_handler previousHandler = __atomic_exchange_n(&_objc_unexpected_exception, handler, __ATOMIC_SEQ_CST); + + // set unhandled exception filter to support hook + LPTOP_LEVEL_EXCEPTION_FILTER previousExceptionFilter = SetUnhandledExceptionFilter(&_objc_unhandled_exception_filter); + if (previousExceptionFilter != &_objc_unhandled_exception_filter) { + originalUnhandledExceptionFilter = previousExceptionFilter; + } + + return previousHandler; +} diff --git a/objc/hooks.h b/objc/hooks.h index b550ccb..21491b1 100644 --- a/objc/hooks.h +++ b/objc/hooks.h @@ -46,12 +46,17 @@ OBJC_PUBLIC extern struct objc_slot *(*__objc_msg_forward3)(id, SEL) OBJC_DEPREC * handles the forwarding. */ OBJC_PUBLIC extern IMP (*__objc_msg_forward2)(id, SEL); + +#ifndef _WIN32 /** * Hook defined for handling unhandled exceptions. If the unwind library * reaches the end of the stack without finding a handler then this hook is * called. + * Deprecated. Use objc_setUncaughtExceptionHandler() instead. */ OBJC_HOOK void (*_objc_unexpected_exception)(id exception); +#endif + /** * Hook defined to return the class to be used for boxing a foreign exception * type. The class must implement: diff --git a/objc/objc-exception.h b/objc/objc-exception.h new file mode 100644 index 0000000..828e0e6 --- /dev/null +++ b/objc/objc-exception.h @@ -0,0 +1,33 @@ +#if defined(__clang__) && !defined(__OBJC_RUNTIME_INTERNAL__) +#pragma clang system_header +#endif +#include "objc-visibility.h" + +#ifndef __OBJC_EXCEPTION_INCLUDED__ +#define __OBJC_EXCEPTION_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*objc_uncaught_exception_handler)(id exception); + +/** + * Throw a runtime exception. Inserted by the compiler in place of @throw. + */ +OBJC_PUBLIC +void objc_exception_throw(id object); + +/** + * Installs handler for uncaught Objective-C exceptions. If the unwind library + * reaches the end of the stack without finding a handler then the handler is + * called. Returns the previous handler. + */ +OBJC_PUBLIC +objc_uncaught_exception_handler objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler handler); + +#ifdef __cplusplus +} +#endif + +#endif // __OBJC_EXCEPTION_INCLUDED__