2 need exception-safe ARC for exception deallocation tests
3 TEST_CFLAGS -fobjc-arc-exceptions -framework Foundation
8 #include <objc/runtime.h>
9 #include <objc/objc-exception.h>
11 static volatile int state = 0;
12 static volatile int dealloced = 0;
15 #if defined(USE_FOUNDATION)
17 #include <Foundation/Foundation.h>
19 @interface Super : NSException @end
21 +(id)exception { return AUTORELEASE([[self alloc] initWithName:@"Super" reason:@"reason" userInfo:nil]); }
22 -(void)check { state++; }
23 +(void)check { testassert(!"caught class object, not instance"); }
24 -(void)dealloc { dealloced++; SUPER_DEALLOC(); }
27 #define FILENAME "nsexc.m"
31 @interface Super : TestRoot @end
33 +(id)exception { return AUTORELEASE([self new]); }
34 -(void)check { state++; }
35 +(void)check { testassert(!"caught class object, not instance"); }
36 -(void)dealloc { dealloced++; SUPER_DEALLOC(); }
39 #define FILENAME "exc.m"
43 @interface Sub : Super @end
49 void altHandlerFail(id unused __unused, void *context __unused)
51 fail("altHandlerFail called");
54 #define ALT_HANDLER(n) \
55 void altHandler##n(id unused __unused, void *context) \
57 testassert(context == (void*)&altHandler##n); \
58 testassert(state == n); \
71 static void throwWithAltHandler(void) __attribute__((noinline, used));
72 static void throwWithAltHandler(void)
76 uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
77 // state++ inside alt handler
78 @throw [Super exception];
80 objc_removeExceptionHandler(token);
89 static void throwWithAltHandlerAndRethrow(void) __attribute__((noinline, used));
90 static void throwWithAltHandlerAndRethrow(void)
94 uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
95 // state++ inside alt handler
96 @throw [Super exception];
98 objc_removeExceptionHandler(token);
101 testassert(state == 4);
120 testonthread(^{ PUSH_POOL { code } POP_POOL; }); \
128 testprintf("try-catch-finally, exception caught exactly\n");
137 @throw [Super exception];
142 [e check]; // state++
153 testassert(state == 6);
154 testassert(dealloced == 1);
157 testprintf("try-finally, no exception thrown\n");
176 testassert(state == 4);
177 testassert(dealloced == 0);
180 testprintf("try-finally, with exception\n");
189 @throw [Super exception];
199 [e check]; // state++
202 testassert(state == 5);
203 testassert(dealloced == 1);
206 testprintf("try-finally, with autorelease pool pop during unwind\n");
207 // Popping an autorelease pool during unwind used to deallocate the
208 // exception object, but now we retain them while in flight.
210 // This use-after-free is undetected without MallocScribble or guardmalloc.
211 if (!getenv("MallocScribble") &&
212 (!getenv("DYLD_INSERT_LIBRARIES") ||
213 !strstr(getenv("DYLD_INSERT_LIBRARIES"), "libgmalloc")))
215 testwarn("MallocScribble not set");
222 void *pool2 = objc_autoreleasePoolPush();
226 @throw [Super exception];
231 objc_autoreleasePoolPop(pool2);
237 [e check]; // state++
240 testassert(state == 5);
241 testassert(dealloced == 1);
244 testprintf("try-catch-finally, no exception\n");
265 testassert(state == 4);
266 testassert(dealloced == 0);
269 testprintf("try-catch-finally, exception not caught\n");
278 @throw [Super exception];
291 [e check]; // state++
294 testassert(state == 5);
295 testassert(dealloced == 1);
298 testprintf("try-catch-finally, exception caught exactly, rethrown\n");
307 @throw [Super exception];
312 [e check]; // state++
323 [e check]; // state++
326 testassert(state == 7);
327 testassert(dealloced == 1);
330 testprintf("try-catch, no exception\n");
348 testassert(state == 3);
349 testassert(dealloced == 0);
352 testprintf("try-catch, exception not caught\n");
361 @throw [Super exception];
371 [e check]; // state++
374 testassert(state == 4);
375 testassert(dealloced == 1);
378 testprintf("try-catch, exception caught exactly\n");
387 @throw [Super exception];
392 [e check]; // state++
400 testassert(state == 5);
401 testassert(dealloced == 1);
404 testprintf("try-catch, exception caught exactly, rethrown\n");
413 @throw [Super exception];
418 [e check]; // state++
426 [e check]; // state++
429 testassert(state == 6);
430 testassert(dealloced == 1);
433 testprintf("try-catch, exception caught exactly, thrown again explicitly\n");
442 @throw [Super exception];
447 [e check]; // state++
455 [e check]; // state++
458 testassert(state == 6);
459 testassert(dealloced == 1);
462 testprintf("try-catch, default catch, rethrown\n");
471 @throw [Super exception];
483 [e check]; // state++
486 testassert(state == 5);
487 testassert(dealloced == 1);
490 testprintf("try-catch, default catch, rethrown and caught inside nested handler\n");
499 @throw [Super exception];
511 } @catch (Super *e) {
513 [e check]; // state++
528 testassert(state == 9);
529 testassert(dealloced == 1);
532 testprintf("try-catch, default catch, rethrown inside nested handler but not caught\n");
541 @throw [Super exception];
565 [e check]; // state++
568 testassert(state == 7);
569 testassert(dealloced == 1);
573 testprintf("C++ try/catch, Objective-C exception superclass\n");
584 @throw [Super exception];
600 [e check]; // state++
607 [e check]; // state++;
610 testassert(state == 8);
611 testassert(dealloced == 1);
614 testprintf("C++ try/catch, Objective-C exception subclass\n");
625 @throw [Sub exception];
639 [e check]; // state++
648 [e check]; // state++;
651 testassert(state == 8);
652 testassert(dealloced == 1);
658 // alt handlers are for macOS only
662 // run a lot to catch failed unregistration (runtime complains at 1000)
663 #define ALT_HANDLER_REPEAT 2000
665 testprintf("alt handler, no exception\n");
669 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
674 uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
676 objc_removeExceptionHandler(token);
685 testassert(state == 3);
688 testassert(dealloced == 0);
691 testprintf("alt handler, exception thrown through\n");
695 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
701 uintptr_t token = objc_addExceptionHandler(altHandler2, (void*)altHandler2);
702 // state++ inside alt handler
703 @throw [Super exception];
705 objc_removeExceptionHandler(token);
713 testassert(state == 3);
715 [e check]; // state++
717 testassert(state == 5);
720 testassert(dealloced == ALT_HANDLER_REPEAT);
723 testprintf("alt handler, nested\n");
725 testwarn("fixme compiler no longer cooperative for local nested?");
726 // Nested alt handlers inside the same function require that each
727 // catch group have its own landing pad descriptor. The compiler is
728 // probably not doing that anymore. For now we assume that the
729 // combination of nested exception handlers and alt handlers is
730 // rare enough that nobody cares.
734 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
740 // same-level handlers called in FIFO order (not stack-like)
741 uintptr_t token = objc_addExceptionHandler(altHandler4, (void*)altHandler4);
742 // state++ inside alt handler
743 uintptr_t token2 = objc_addExceptionHandler(altHandler5, (void*)altHandler5);
744 // state++ inside alt handler
745 throwWithAltHandler(); // state += 2 inside
747 objc_removeExceptionHandler(token);
748 objc_removeExceptionHandler(token2);
751 testassert(state == 6);
753 [e check]; // state++;
760 testassert(state == 9);
763 testassert(dealloced == ALT_HANDLER_REPEAT);
766 testprintf("alt handler, nested, rethrows in between\n");
768 testwarn("fixme compiler no longer cooperative for local nested?");
773 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
779 // same-level handlers called in FIFO order (not stack-like)
780 uintptr_t token = objc_addExceptionHandler(altHandler5, (void*)altHandler5);
781 // state++ inside alt handler
782 uintptr_t token2 = objc_addExceptionHandler(altHandler6, (void*)altHandler6);
783 // state++ inside alt handler
784 throwWithAltHandlerAndRethrow(); // state += 3 inside
786 objc_removeExceptionHandler(token);
787 objc_removeExceptionHandler(token2);
790 testassert(state == 7);
797 testassert(state == 8);
799 [e check]; // state++
801 testassert(state == 10);
804 testassert(dealloced == ALT_HANDLER_REPEAT);
807 testprintf("alt handler, exception thrown and caught inside\n");
811 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
815 uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
818 @throw [Super exception];
823 [e check]; // state++
826 objc_removeExceptionHandler(token);
831 testassert(state == 5);
834 testassert(dealloced == ALT_HANDLER_REPEAT);
837 #if defined(USE_FOUNDATION)
838 testprintf("alt handler, rdar://10055775\n");
842 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
845 uintptr_t token = objc_addExceptionHandler(altHandler1, (void*)altHandler1);
847 id x = [NSArray array];
851 // state++ inside alt handler
852 [Super raise:@"foo" format:@"bar"];
854 objc_removeExceptionHandler(token);
857 testassert(state == 3);
859 testassert(state == 3);
862 testassert(dealloced == ALT_HANDLER_REPEAT);
864 // defined(USE_FOUNDATION)
872 std::set_terminate(terminator);
874 fail("should not have returned from objc_terminate()");