diff --git a/CMakeLists.txt b/CMakeLists.txt index e2468ed3..a9934d51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,6 @@ 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 @@ -107,12 +106,6 @@ set(libobjc_CXX_SRCS selector_table.cc ) -# Windows does not use DWARF EH, except when using the GNU ABI (MinGW) -if (WIN32 AND NOT MINGW) - list(APPEND libobjc_CXX_SRCS eh_win32_msvc.cc) -elseif (NOT MINGW) - list(APPEND libobjc_C_SRCS eh_personality.c) -endif () find_package(tsl-robin-map) @@ -141,6 +134,7 @@ option(LEGACY_COMPAT "Enable legacy compatibility features" OFF) option(DEBUG_ARC_COMPAT "Log warnings for classes that don't hit ARC fast paths" OFF) option(ENABLE_OBJCXX "Enable support for Objective-C++" ON) +option(ENABLE_EXCEPTIONS "Enable exceptions" ON) option(TESTS "Enable building the tests") option(EMBEDDED_BLOCKS_RUNTIME "Include an embedded blocks runtime, rather than relying on libBlocksRuntime to supply it" ON) option(STRICT_APPLE_COMPATIBILITY "Use strict Apple compatibility, always defining BOOL as signed char" OFF) @@ -161,6 +155,17 @@ if (EMBEDDED_BLOCKS_RUNTIME) list(APPEND libobjc_C_SRCS block_to_imp.c) endif() +if (ENABLE_EXCEPTIONS) + # Windows does not use DWARF EH, except when using the GNU ABI (MinGW) + if (WIN32 AND NOT MINGW) + list(APPEND libobjc_CXX_SRCS eh_win32_msvc.cc) + elseif (NOT MINGW) + list(APPEND libobjc_C_SRCS eh_personality.c) + endif () + + list(APPEND libobjc_HDRS objc/objc-exception.h) +endif() + if (OLDABI_COMPAT) list(APPEND libobjc_C_SRCS legacy.c abi_version.c statics_loader.c) add_definitions(-DOLDABI_COMPAT=1) @@ -220,23 +225,25 @@ endif() -if (WIN32 AND NOT MINGW) - message(STATUS "Using MSVC-compatible exception model") -elseif (MINGW) - message(STATUS "Using MinGW-compatible exception model") - list(APPEND libobjc_CXX_SRCS objcxx_eh.cc objcxx_eh_mingw.cc) -else () - set(EH_PERSONALITY_FLAGS "") - if (CMAKE_CXX_COMPILER_TARGET) - list(APPEND EH_PERSONALITY_FLAGS "${CMAKE_CXX_COMPILE_OPTIONS_TARGET}${CMAKE_CXX_COMPILER_TARGET}") +if (ENABLE_EXCEPTIONS) + if (WIN32 AND NOT MINGW) + message(STATUS "Using MSVC-compatible exception model") + elseif (MINGW) + message(STATUS "Using MinGW-compatible exception model") + list(APPEND libobjc_CXX_SRCS objcxx_eh.cc objcxx_eh_mingw.cc) + else () + set(EH_PERSONALITY_FLAGS "") + if (CMAKE_CXX_COMPILER_TARGET) + list(APPEND EH_PERSONALITY_FLAGS "${CMAKE_CXX_COMPILE_OPTIONS_TARGET}${CMAKE_CXX_COMPILER_TARGET}") + endif () + add_custom_command(OUTPUT eh_trampoline.S + COMMAND ${CMAKE_CXX_COMPILER} ARGS ${EH_PERSONALITY_FLAGS} -fPIC -S "${CMAKE_SOURCE_DIR}/eh_trampoline.cc" -o - -fexceptions -fno-inline | sed "s/__gxx_personality_v0/test_eh_personality/g" > "${CMAKE_BINARY_DIR}/eh_trampoline.S" + MAIN_DEPENDENCY eh_trampoline.cc) + list(APPEND libobjc_ASM_SRCS eh_trampoline.S) + list(APPEND libobjc_CXX_SRCS objcxx_eh.cc) + # Find libm for linking, as some versions of libc++ don't link against it + find_library(M_LIBRARY m) endif () - add_custom_command(OUTPUT eh_trampoline.S - COMMAND ${CMAKE_CXX_COMPILER} ARGS ${EH_PERSONALITY_FLAGS} -fPIC -S "${CMAKE_SOURCE_DIR}/eh_trampoline.cc" -o - -fexceptions -fno-inline | sed "s/__gxx_personality_v0/test_eh_personality/g" > "${CMAKE_BINARY_DIR}/eh_trampoline.S" - MAIN_DEPENDENCY eh_trampoline.cc) - list(APPEND libobjc_ASM_SRCS eh_trampoline.S) - list(APPEND libobjc_CXX_SRCS objcxx_eh.cc) - # Find libm for linking, as some versions of libc++ don't link against it - find_library(M_LIBRARY m) endif () if (EMBEDDED_BLOCKS_RUNTIME) @@ -256,7 +263,9 @@ else () endif () add_library(objc SHARED ${libobjc_C_SRCS} ${libobjc_ASM_SRCS} ${libobjc_OBJC_SRCS} ${libobjc_OBJCXX_SRCS} ${libobjc_ASM_OBJS}) -target_compile_options(objc PRIVATE "$<$,$>:-Wno-gnu-folding-constant;-Wno-deprecated-objc-isa-usage;-Wno-objc-root-class;-fobjc-runtime=gnustep-2.0>$<$:-Xclang;-fexceptions;-Wno-gnu-folding-constant>") + +#target_compile_options(objc PRIVATE "$<$,$>:-Wno-gnu-folding-constant;-Wno-deprecated-objc-isa-usage;-Wno-objc-root-class;-fobjc-runtime=gnustep-2.0>$<$:-Xclang;-fexceptions;-Wno-gnu-folding-constant>") +target_compile_options(objc PRIVATE "$<$,$>:-Wno-gnu-folding-constant;-fno-exceptions;-fno-objc-exceptions;-Wno-deprecated-objc-isa-usage;-Wno-objc-root-class;-fobjc-runtime=gnustep-2.0>$<$:-Xclang;-Wno-gnu-folding-constant>") list(APPEND libobjc_CXX_SRCS ${libobjcxx_CXX_SRCS}) target_sources(objc PRIVATE ${libobjc_CXX_SRCS}) @@ -466,13 +475,15 @@ if (TESTS) add_subdirectory(Test) endif (TESTS) -CHECK_CXX_SOURCE_COMPILES(" - #include - extern \"C\" { - __attribute__((weak)) - void *__cxa_allocate_exception(size_t thrown_size) noexcept; - } - #include - int main() { return 0; }" CXA_ALLOCATE_EXCEPTION_NOEXCEPT_COMPILES) - -add_compile_definitions($,CXA_ALLOCATE_EXCEPTION_SPECIFIER=noexcept,CXA_ALLOCATE_EXCEPTION_SPECIFIER>) +if (ENABLE_EXCEPTIONS) + CHECK_CXX_SOURCE_COMPILES(" + #include + extern \"C\" { + __attribute__((weak)) + void *__cxa_allocate_exception(size_t thrown_size) noexcept; + } + #include + int main() { return 0; }" CXA_ALLOCATE_EXCEPTION_NOEXCEPT_COMPILES) + + add_compile_definitions($,CXA_ALLOCATE_EXCEPTION_SPECIFIER=noexcept,CXA_ALLOCATE_EXCEPTION_SPECIFIER>) +endif() diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 3aa27b73..7a0f8a26 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -1,4 +1,3 @@ - # Clear the LD_LIBRARY_PATH if GNUstep set it so that we don't accidentally use # the installed version @@ -19,13 +18,11 @@ set(TESTS BlockTest_arc.m ConstantString.m Category.m - ExceptionTest.m FastARC.m FastARCPool.m FastRefCount.m Forward.m ManyManySelectors.m - NestedExceptions.m PropertyAttributeTest.m ProtocolExtendedProperties.m PropertyIntrospectionTest.m @@ -43,16 +40,36 @@ set(TESTS IVarSuperclassOverlap.m objc_msgSend.m msgInterpose.m - NilException.m MethodArguments.m zeroSizedIVar.m exchange.m hash_table_delete.c hash_test.c setSuperclass.m - UnexpectedException.m ) +if (ENABLE_EXCEPTIONS) + set(ADDITIONAL_CFLAGS "-fobjc-exceptions") + + list(APPEND TESTS + ExceptionTest.m + NestedExceptions.m + NilException.m + UnexpectedException.m + ) + + # Don't run the tests that are specific to Itanium-style exceptions on + # Windows. + if (NOT WIN32) + list(APPEND TESTS + BoxedForeignException.m + ForeignException.m + ) + endif() +else() + set(ADDITIONAL_CFLAGS "-fno-objc-exceptions") +endif() + if (EMBEDDED_BLOCKS_RUNTIME) list(APPEND TESTS BlockImpTest.m) endif() @@ -73,13 +90,6 @@ if (WIN32) objc_msgSend_WoA64.mm ) endif() -else () - # Don't run the tests that are specific to Itanium-style exceptions on - # Windows. - list(APPEND TESTS - BoxedForeignException.m - ForeignException.m - ) endif () if (ENABLE_ALL_OBJC_ARC_TESTS) @@ -102,14 +112,14 @@ remove_definitions(-D__OBJC_RUNTIME_INTERNAL__=1) add_library(test_runtime_legacy OBJECT Test.m) set_target_properties(test_runtime_legacy PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR};${PROJECT_BINARY_DIR}/objc/" - COMPILE_FLAGS "-Xclang -fblocks -fobjc-runtime=gnustep-1.7" + COMPILE_FLAGS "-Xclang -fblocks -fobjc-runtime=gnustep-1.7 ${ADDITIONAL_CFLAGS}" LINKER_LANGUAGE C ) add_library(test_runtime OBJECT Test.m) set_target_properties(test_runtime PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR};${PROJECT_BINARY_DIR}/objc/" - COMPILE_FLAGS "-Xclang -fblocks -fobjc-runtime=gnustep-2.0" + COMPILE_FLAGS "-Xclang -fblocks -fobjc-runtime=gnustep-2.0 ${ADDITIONAL_CFLAGS}" LINKER_LANGUAGE C ) @@ -142,16 +152,16 @@ function(addtest_flags TEST_NAME FLAGS TEST_SOURCE) endfunction(addtest_flags) function(addtest_variants TEST TEST_SOURCE LEGACY) - addtest_flags(${TEST} "-O0 -fobjc-runtime=gnustep-2.2 -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}") + addtest_flags(${TEST} "-O0 -fobjc-runtime=gnustep-2.2 ${ADDITIONAL_CFLAGS} -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}") target_sources(${TEST} PRIVATE $) - addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.2 -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}") + addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.2 ${ADDITIONAL_CFLAGS} -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}") target_sources("${TEST}_optimised" PRIVATE $) # -fobjc-arc is not supported on platforms using the legacy runtime if (${LEGACY} AND ${OLDABI_COMPAT} AND NOT ${TEST} MATCHES ".*_arc") - addtest_flags("${TEST}_legacy" "-O0 -fobjc-runtime=gnustep-1.7 -UNDEBUG" "${TEST_SOURCE}") + addtest_flags("${TEST}_legacy" "-O0 -fobjc-runtime=gnustep-1.7 ${ADDITIONAL_CFLAGS} -UNDEBUG" "${TEST_SOURCE}") target_sources("${TEST}_legacy" PRIVATE $) - addtest_flags("${TEST}_legacy_optimised" "-O3 -fobjc-runtime=gnustep-1.7 -UNDEBUG" "${TEST_SOURCE}") + addtest_flags("${TEST}_legacy_optimised" "-O3 -fobjc-runtime=gnustep-1.7 ${ADDITIONAL_CFLAGS} -UNDEBUG" "${TEST_SOURCE}") target_sources("${TEST}_legacy_optimised" PRIVATE $) endif() endfunction(addtest_variants) @@ -166,18 +176,20 @@ foreach(TEST_SOURCE ${NEW_TESTS}) addtest_variants(${TEST} ${TEST_SOURCE} false) endforeach() -# Tests that are more than a single file. -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) - if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0.0) +if (ENABLE_EXCEPTIONS) + # Tests that are more than a single file. + 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) + if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) + if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0.0) + addtest_variants(ObjCXXEHInterop_arc "ObjCXXEHInterop_arc.mm;ObjCXXEHInterop_arc.m" true) + endif() + else() addtest_variants(ObjCXXEHInterop_arc "ObjCXXEHInterop_arc.mm;ObjCXXEHInterop_arc.m" true) endif() - else() - addtest_variants(ObjCXXEHInterop_arc "ObjCXXEHInterop_arc.mm;ObjCXXEHInterop_arc.m" true) endif() endif() diff --git a/Test/RuntimeTest.m b/Test/RuntimeTest.m index 1a49609e..199d98f5 100644 --- a/Test/RuntimeTest.m +++ b/Test/RuntimeTest.m @@ -78,7 +78,9 @@ - (int) manyTypes; - (void) synchronizedCode; + (void) synchronizedCode; + (id) shared; +#if __has_feature(objc_exceptions) - (BOOL) basicThrowAndCatchException; +#endif @end @interface Bar : Foo @@ -117,6 +119,8 @@ + (id) shared @synchronized(self) { } return nil; } + +#if __has_feature(objc_exceptions) - (void) throwException { @throw exceptionObj; @@ -138,6 +142,7 @@ - (BOOL) basicThrowAndCatchException } return NO; } +#endif @end @implementation Bar @@ -282,6 +287,7 @@ void testSynchronized() printf("testSynchronized() ran\n"); } +#if __has_feature(objc_exceptions) void testExceptions() { Foo *foo = [Foo new]; @@ -290,6 +296,7 @@ void testExceptions() printf("testExceptions() ran\n"); } +#endif void testRegisterAlias() { @@ -336,7 +343,9 @@ int main (int argc, const char * argv[]) printf("Instance of NSObject: %p\n", class_createInstance([NSObject class], 0)); testSynchronized(); +#if __has_feature(objc_exceptions) testExceptions(); +#endif testRegisterAlias(); return exitStatus; diff --git a/Test/objc_msgSend.m b/Test/objc_msgSend.m index 9fd76b73..fe5dc9d4 100644 --- a/Test/objc_msgSend.m +++ b/Test/objc_msgSend.m @@ -92,7 +92,9 @@ + (void)printf: (const char*)str, ... + (void)initialize { [self printf: "Format %s %d %f%c", "string", 42, 42.0, '\n']; +#if __has_feature(objc_exceptions) @throw self; +#endif } + nothing { return 0; } @end @@ -180,6 +182,8 @@ int main(void) __objc_msg_forward2 = forward; __objc_msg_forward3 = forward_slot; TestCls = objc_getClass("MsgTest"); + +#if __has_feature(objc_exceptions) int exceptionThrown = 0; @try { objc_msgSend(TestCls, @selector(foo)); @@ -189,6 +193,8 @@ int main(void) exceptionThrown = 1; } assert(exceptionThrown && "An exception was thrown"); +#endif + assert((id)0x42 == objc_msgSend(TestCls, @selector(foo))); objc_msgSend(TestCls, @selector(nothing)); objc_msgSend(TestCls, @selector(missing)); diff --git a/associate.m b/associate.m index 2b987f99..07e67b4e 100644 --- a/associate.m +++ b/associate.m @@ -168,6 +168,7 @@ static void setReference(struct reference_list *list, lock = lock_for_pointer(r); lock_spinlock(lock); } +#if __has_feature(objc_exceptions) @try { if (OBJC_ASSOCIATION_ASSIGN != r->policy) @@ -180,6 +181,14 @@ static void setReference(struct reference_list *list, r->policy = policy; r->object = obj; } +#else + if (OBJC_ASSOCIATION_ASSIGN != r->policy) + { + objc_release(r->object); + } + r->policy = policy; + r->object = obj; +#endif // __has_feature(objc_exceptions) if (needLock) { unlock_spinlock(lock);