2 need exception-safe ARC for exception deallocation tests
3 need F/CF for testonthread() in GC mode
4 TEST_CFLAGS -fobjc-arc-exceptions -framework Foundation
6 llvm-gcc unavoidably warns about our deliberately out-of-order handlers
9 .*exc.m: In function .*
10 .*exc.m:\d+: warning: exception of type .* will be caught
11 .*exc.m:\d+: warning: by earlier handler for .*
12 .*exc.m:\d+: warning: exception of type .* will be caught
13 .*exc.m:\d+: warning: by earlier handler for .*
14 .*exc.m:\d+: warning: exception of type .* will be caught
15 .*exc.m:\d+: warning: by earlier handler for .*
22 #include <objc/runtime.h>
23 #include <objc/objc-exception.h>
25 static volatile int state = 0;
26 static volatile int dealloced = 0;
29 #if defined(USE_FOUNDATION)
31 #include <Foundation/Foundation.h>
33 @interface Super : NSException @end
35 +(id)exception { return AUTORELEASE([[self alloc] initWithName:@"Super" reason:@"reason" userInfo:nil]); }
36 -(void)check { state++; }
37 +(void)check { testassert(!"caught class object, not instance"); }
38 -(void)dealloc { dealloced++; SUPER_DEALLOC(); }
39 -(void)finalize { dealloced++; [super finalize]; }
42 #define FILENAME "nsexc.m"
46 @interface Super : TestRoot @end
48 +(id)exception { return AUTORELEASE([self new]); }
49 -(void)check { state++; }
50 +(void)check { testassert(!"caught class object, not instance"); }
51 -(void)dealloc { dealloced++; SUPER_DEALLOC(); }
52 -(void)finalize { dealloced++; [super finalize]; }
55 #define FILENAME "exc.m"
59 @interface Sub : Super @end
64 #if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE
65 void altHandlerFail(id unused __unused, void *context __unused)
67 fail("altHandlerFail called");
70 #define ALT_HANDLER(n) \
71 void altHandler##n(id unused __unused, void *context) \
73 testassert(context == (void*)&altHandler##n); \
74 testassert(state == n); \
87 static void throwWithAltHandler(void) __attribute__((noinline));
88 static void throwWithAltHandler(void)
92 uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
93 // state++ inside alt handler
94 @throw [Super exception];
96 objc_removeExceptionHandler(token);
105 static void throwWithAltHandlerAndRethrow(void) __attribute__((noinline));
106 static void throwWithAltHandlerAndRethrow(void)
110 uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
111 // state++ inside alt handler
112 @throw [Super exception];
114 objc_removeExceptionHandler(token);
117 testassert(state == 4);
126 #if __cplusplus && __OBJC2__
136 testonthread(^{ PUSH_POOL { code } POP_POOL; }); \
144 testprintf("try-catch-finally, exception caught exactly\n");
153 @throw [Super exception];
158 [e check]; // state++
169 testassert(state == 6);
170 testassert(dealloced == 1);
173 testprintf("try-finally, no exception thrown\n");
192 testassert(state == 4);
193 testassert(dealloced == 0);
196 testprintf("try-finally, with exception\n");
205 @throw [Super exception];
215 [e check]; // state++
218 testassert(state == 5);
219 testassert(dealloced == 1);
223 testprintf("try-finally, with autorelease pool pop during unwind\n");
224 // Popping an autorelease pool during unwind used to deallocate the
225 // exception object, but now we retain them while in flight.
227 // This use-after-free is undetected without MallocScribble or guardmalloc.
228 if (!getenv("MallocScribble") &&
229 (!getenv("DYLD_INSERT_LIBRARIES") ||
230 !strstr(getenv("DYLD_INSERT_LIBRARIES"), "libgmalloc")))
232 testwarn("MallocScribble not set");
239 void *pool2 = objc_autoreleasePoolPush();
243 @throw [Super exception];
248 objc_autoreleasePoolPop(pool2);
254 [e check]; // state++
257 testassert(state == 5);
258 testassert(dealloced == 1);
262 testprintf("try-catch-finally, no exception\n");
283 testassert(state == 4);
284 testassert(dealloced == 0);
287 testprintf("try-catch-finally, exception not caught\n");
296 @throw [Super exception];
309 [e check]; // state++
312 testassert(state == 5);
313 testassert(dealloced == 1);
316 testprintf("try-catch-finally, exception caught exactly, rethrown\n");
325 @throw [Super exception];
330 [e check]; // state++
341 [e check]; // state++
344 testassert(state == 7);
345 testassert(dealloced == 1);
348 testprintf("try-catch, no exception\n");
366 testassert(state == 3);
367 testassert(dealloced == 0);
370 testprintf("try-catch, exception not caught\n");
379 @throw [Super exception];
389 [e check]; // state++
392 testassert(state == 4);
393 testassert(dealloced == 1);
396 testprintf("try-catch, exception caught exactly\n");
405 @throw [Super exception];
410 [e check]; // state++
418 testassert(state == 5);
419 testassert(dealloced == 1);
422 testprintf("try-catch, exception caught exactly, rethrown\n");
431 @throw [Super exception];
436 [e check]; // state++
444 [e check]; // state++
447 testassert(state == 6);
448 testassert(dealloced == 1);
451 testprintf("try-catch, exception caught exactly, thrown again explicitly\n");
460 @throw [Super exception];
465 [e check]; // state++
473 [e check]; // state++
476 testassert(state == 6);
477 testassert(dealloced == 1);
480 testprintf("try-catch, default catch, rethrown\n");
489 @throw [Super exception];
501 [e check]; // state++
504 testassert(state == 5);
505 testassert(dealloced == 1);
508 testprintf("try-catch, default catch, rethrown and caught inside nested handler\n");
517 @throw [Super exception];
529 } @catch (Super *e) {
531 [e check]; // state++
546 testassert(state == 9);
547 testassert(dealloced == 1);
550 testprintf("try-catch, default catch, rethrown inside nested handler but not caught\n");
559 @throw [Super exception];
583 [e check]; // state++
586 testassert(state == 7);
587 testassert(dealloced == 1);
590 #if __cplusplus && __OBJC2__
591 testprintf("C++ try/catch, Objective-C exception superclass\n");
602 @throw [Super exception];
618 [e check]; // state++
625 [e check]; // state++;
628 testassert(state == 8);
629 testassert(dealloced == 1);
632 testprintf("C++ try/catch, Objective-C exception subclass\n");
643 @throw [Sub exception];
657 [e check]; // state++
666 [e check]; // state++;
669 testassert(state == 8);
670 testassert(dealloced == 1);
675 #if !__OBJC2__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
676 // alt handlers for modern Mac OS only
681 // run a lot to catch failed unregistration (runtime complains at 1000)
682 #define ALT_HANDLER_REPEAT 2000
684 testprintf("alt handler, no exception\n");
688 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
693 uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
695 objc_removeExceptionHandler(token);
704 testassert(state == 3);
707 testassert(dealloced == 0);
710 testprintf("alt handler, exception thrown through\n");
714 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
720 uintptr_t token = objc_addExceptionHandler(altHandler2, (void*)altHandler2);
721 // state++ inside alt handler
722 @throw [Super exception];
724 objc_removeExceptionHandler(token);
732 testassert(state == 3);
734 [e check]; // state++
736 testassert(state == 5);
739 testassert(dealloced == ALT_HANDLER_REPEAT);
742 testprintf("alt handler, nested\n");
746 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
752 // same-level handlers called in FIFO order (not stack-like)
753 uintptr_t token = objc_addExceptionHandler(altHandler4, (void*)altHandler4);
754 // state++ inside alt handler
755 uintptr_t token2 = objc_addExceptionHandler(altHandler5, (void*)altHandler5);
756 // state++ inside alt handler
757 throwWithAltHandler(); // state += 2 inside
759 objc_removeExceptionHandler(token);
760 objc_removeExceptionHandler(token2);
763 testassert(state == 6);
765 [e check]; // state++;
772 testassert(state == 9);
775 testassert(dealloced == ALT_HANDLER_REPEAT);
778 testprintf("alt handler, nested, rethrows in between\n");
782 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
788 // same-level handlers called in FIFO order (not stack-like)
789 uintptr_t token = objc_addExceptionHandler(altHandler5, (void*)altHandler5);
790 // state++ inside alt handler
791 uintptr_t token2 = objc_addExceptionHandler(altHandler6, (void*)altHandler6);
792 // state++ inside alt handler
793 throwWithAltHandlerAndRethrow(); // state += 3 inside
795 objc_removeExceptionHandler(token);
796 objc_removeExceptionHandler(token2);
799 testassert(state == 7);
806 testassert(state == 8);
808 [e check]; // state++
810 testassert(state == 10);
813 testassert(dealloced == ALT_HANDLER_REPEAT);
816 testprintf("alt handler, exception thrown and caught inside\n");
820 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
824 uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
827 @throw [Super exception];
832 [e check]; // state++
835 objc_removeExceptionHandler(token);
840 testassert(state == 5);
843 testassert(dealloced == ALT_HANDLER_REPEAT);
846 #if defined(USE_FOUNDATION)
847 testprintf("alt handler, rdar://10055775\n");
851 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
854 uintptr_t token = objc_addExceptionHandler(altHandler1, (void*)altHandler1);
856 id x = [NSArray array];
860 // state++ inside alt handler
861 [Super raise:@"foo" format:@"bar"];
863 objc_removeExceptionHandler(token);
866 testassert(state == 3);
868 testassert(state == 3);
871 testassert(dealloced == ALT_HANDLER_REPEAT);
873 // defined(USE_FOUNDATION)
880 #if __cplusplus && __OBJC2__
881 std::set_terminate(terminator);
883 fail("should not have returned from objc_terminate()");