]> git.saurik.com Git - apple/objc4.git/blob - test/exc.m
4f25b75b7dd0c3293c922f4ece8858452acec05c
[apple/objc4.git] / test / exc.m
1 /*
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
5
6 llvm-gcc unavoidably warns about our deliberately out-of-order handlers
7
8 TEST_BUILD_OUTPUT
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 .*
16 OR
17 END
18 */
19
20 #include "test.h"
21 #include "testroot.i"
22 #include <objc/runtime.h>
23 #include <objc/objc-exception.h>
24
25 static volatile int state = 0;
26 static volatile int dealloced = 0;
27 #define BAD 1000000
28
29 #if defined(USE_FOUNDATION)
30
31 #include <Foundation/Foundation.h>
32
33 @interface Super : NSException @end
34 @implementation Super
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]; }
40 @end
41
42 #define FILENAME "nsexc.m"
43
44 #else
45
46 @interface Super : TestRoot @end
47 @implementation Super
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]; }
53 @end
54
55 #define FILENAME "exc.m"
56
57 #endif
58
59 @interface Sub : Super @end
60 @implementation Sub
61 @end
62
63
64 #if __OBJC2__ && !TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE
65 void altHandlerFail(id unused __unused, void *context __unused)
66 {
67 fail("altHandlerFail called");
68 }
69
70 #define ALT_HANDLER(n) \
71 void altHandler##n(id unused __unused, void *context) \
72 { \
73 testassert(context == (void*)&altHandler##n); \
74 testassert(state == n); \
75 state++; \
76 }
77
78 ALT_HANDLER(1)
79 ALT_HANDLER(2)
80 ALT_HANDLER(3)
81 ALT_HANDLER(4)
82 ALT_HANDLER(5)
83 ALT_HANDLER(6)
84 ALT_HANDLER(7)
85
86
87 static void throwWithAltHandler(void) __attribute__((noinline));
88 static void throwWithAltHandler(void)
89 {
90 @try {
91 state++;
92 uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
93 // state++ inside alt handler
94 @throw [Super exception];
95 state = BAD;
96 objc_removeExceptionHandler(token);
97 }
98 @catch (Sub *e) {
99 state = BAD;
100 }
101 state = BAD;
102 }
103
104
105 static void throwWithAltHandlerAndRethrow(void) __attribute__((noinline));
106 static void throwWithAltHandlerAndRethrow(void)
107 {
108 @try {
109 state++;
110 uintptr_t token = objc_addExceptionHandler(altHandler3, (void*)altHandler3);
111 // state++ inside alt handler
112 @throw [Super exception];
113 state = BAD;
114 objc_removeExceptionHandler(token);
115 }
116 @catch (...) {
117 testassert(state == 4);
118 state++;
119 @throw;
120 }
121 state = BAD;
122 }
123
124 #endif
125
126 #if __cplusplus && __OBJC2__
127 #include <exception>
128 void terminator() {
129 succeed(FILENAME);
130 }
131 #endif
132
133
134 #define TEST(code) \
135 do { \
136 testonthread(^{ PUSH_POOL { code } POP_POOL; }); \
137 testcollect(); \
138 } while (0)
139
140
141
142 int main()
143 {
144 testprintf("try-catch-finally, exception caught exactly\n");
145
146 TEST({
147 state = 0;
148 dealloced = 0;
149 @try {
150 state++;
151 @try {
152 state++;
153 @throw [Super exception];
154 state = BAD;
155 }
156 @catch (Super *e) {
157 state++;
158 [e check]; // state++
159 }
160 @finally {
161 state++;
162 }
163 state++;
164 }
165 @catch (...) {
166 state = BAD;
167 }
168 });
169 testassert(state == 6);
170 testassert(dealloced == 1);
171
172
173 testprintf("try-finally, no exception thrown\n");
174
175 TEST({
176 state = 0;
177 dealloced = 0;
178 @try {
179 state++;
180 @try {
181 state++;
182 }
183 @finally {
184 state++;
185 }
186 state++;
187 }
188 @catch (...) {
189 state = BAD;
190 }
191 });
192 testassert(state == 4);
193 testassert(dealloced == 0);
194
195
196 testprintf("try-finally, with exception\n");
197
198 TEST({
199 state = 0;
200 dealloced = 0;
201 @try {
202 state++;
203 @try {
204 state++;
205 @throw [Super exception];
206 state = BAD;
207 }
208 @finally {
209 state++;
210 }
211 state = BAD;
212 }
213 @catch (id e) {
214 state++;
215 [e check]; // state++
216 }
217 });
218 testassert(state == 5);
219 testassert(dealloced == 1);
220
221
222 #if __OBJC2__
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.
226
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")))
231 {
232 testwarn("MallocScribble not set");
233 }
234
235 TEST({
236 state = 0;
237 dealloced = 0;
238 @try {
239 void *pool2 = objc_autoreleasePoolPush();
240 state++;
241 @try {
242 state++;
243 @throw [Super exception];
244 state = BAD;
245 }
246 @finally {
247 state++;
248 objc_autoreleasePoolPop(pool2);
249 }
250 state = BAD;
251 }
252 @catch (id e) {
253 state++;
254 [e check]; // state++
255 }
256 });
257 testassert(state == 5);
258 testassert(dealloced == 1);
259 #endif
260
261
262 testprintf("try-catch-finally, no exception\n");
263
264 TEST({
265 state = 0;
266 dealloced = 0;
267 @try {
268 state++;
269 @try {
270 state++;
271 }
272 @catch (...) {
273 state = BAD;
274 }
275 @finally {
276 state++;
277 }
278 state++;
279 } @catch (...) {
280 state = BAD;
281 }
282 });
283 testassert(state == 4);
284 testassert(dealloced == 0);
285
286
287 testprintf("try-catch-finally, exception not caught\n");
288
289 TEST({
290 state = 0;
291 dealloced = 0;
292 @try {
293 state++;
294 @try {
295 state++;
296 @throw [Super exception];
297 state = BAD;
298 }
299 @catch (Sub *e) {
300 state = BAD;
301 }
302 @finally {
303 state++;
304 }
305 state = BAD;
306 }
307 @catch (id e) {
308 state++;
309 [e check]; // state++
310 }
311 });
312 testassert(state == 5);
313 testassert(dealloced == 1);
314
315
316 testprintf("try-catch-finally, exception caught exactly, rethrown\n");
317
318 TEST({
319 state = 0;
320 dealloced = 0;
321 @try {
322 state++;
323 @try {
324 state++;
325 @throw [Super exception];
326 state = BAD;
327 }
328 @catch (Super *e) {
329 state++;
330 [e check]; // state++
331 @throw;
332 state = BAD;
333 }
334 @finally {
335 state++;
336 }
337 state = BAD;
338 }
339 @catch (id e) {
340 state++;
341 [e check]; // state++
342 }
343 });
344 testassert(state == 7);
345 testassert(dealloced == 1);
346
347
348 testprintf("try-catch, no exception\n");
349
350 TEST({
351 state = 0;
352 dealloced = 0;
353 @try {
354 state++;
355 @try {
356 state++;
357 }
358 @catch (...) {
359 state = BAD;
360 }
361 state++;
362 } @catch (...) {
363 state = BAD;
364 }
365 });
366 testassert(state == 3);
367 testassert(dealloced == 0);
368
369
370 testprintf("try-catch, exception not caught\n");
371
372 TEST({
373 state = 0;
374 dealloced = 0;
375 @try {
376 state++;
377 @try {
378 state++;
379 @throw [Super exception];
380 state = BAD;
381 }
382 @catch (Sub *e) {
383 state = BAD;
384 }
385 state = BAD;
386 }
387 @catch (id e) {
388 state++;
389 [e check]; // state++
390 }
391 });
392 testassert(state == 4);
393 testassert(dealloced == 1);
394
395
396 testprintf("try-catch, exception caught exactly\n");
397
398 TEST({
399 state = 0;
400 dealloced = 0;
401 @try {
402 state++;
403 @try {
404 state++;
405 @throw [Super exception];
406 state = BAD;
407 }
408 @catch (Super *e) {
409 state++;
410 [e check]; // state++
411 }
412 state++;
413 }
414 @catch (...) {
415 state = BAD;
416 }
417 });
418 testassert(state == 5);
419 testassert(dealloced == 1);
420
421
422 testprintf("try-catch, exception caught exactly, rethrown\n");
423
424 TEST({
425 state = 0;
426 dealloced = 0;
427 @try {
428 state++;
429 @try {
430 state++;
431 @throw [Super exception];
432 state = BAD;
433 }
434 @catch (Super *e) {
435 state++;
436 [e check]; // state++
437 @throw;
438 state = BAD;
439 }
440 state = BAD;
441 }
442 @catch (id e) {
443 state++;
444 [e check]; // state++
445 }
446 });
447 testassert(state == 6);
448 testassert(dealloced == 1);
449
450
451 testprintf("try-catch, exception caught exactly, thrown again explicitly\n");
452
453 TEST({
454 state = 0;
455 dealloced = 0;
456 @try {
457 state++;
458 @try {
459 state++;
460 @throw [Super exception];
461 state = BAD;
462 }
463 @catch (Super *e) {
464 state++;
465 [e check]; // state++
466 @throw e;
467 state = BAD;
468 }
469 state = BAD;
470 }
471 @catch (id e) {
472 state++;
473 [e check]; // state++
474 }
475 });
476 testassert(state == 6);
477 testassert(dealloced == 1);
478
479
480 testprintf("try-catch, default catch, rethrown\n");
481
482 TEST({
483 state = 0;
484 dealloced = 0;
485 @try {
486 state++;
487 @try {
488 state++;
489 @throw [Super exception];
490 state = BAD;
491 }
492 @catch (...) {
493 state++;
494 @throw;
495 state = BAD;
496 }
497 state = BAD;
498 }
499 @catch (id e) {
500 state++;
501 [e check]; // state++
502 }
503 });
504 testassert(state == 5);
505 testassert(dealloced == 1);
506
507
508 testprintf("try-catch, default catch, rethrown and caught inside nested handler\n");
509
510 TEST({
511 state = 0;
512 dealloced = 0;
513 @try {
514 state++;
515 @try {
516 state++;
517 @throw [Super exception];
518 state = BAD;
519 }
520 @catch (...) {
521 state++;
522
523 @try {
524 state++;
525 @throw;
526 state = BAD;
527 } @catch (Sub *e) {
528 state = BAD;
529 } @catch (Super *e) {
530 state++;
531 [e check]; // state++
532 } @catch (...) {
533 state = BAD;
534 } @finally {
535 state++;
536 }
537
538 state++;
539 }
540 state++;
541 }
542 @catch (...) {
543 state = BAD;
544 }
545 });
546 testassert(state == 9);
547 testassert(dealloced == 1);
548
549
550 testprintf("try-catch, default catch, rethrown inside nested handler but not caught\n");
551
552 TEST({
553 state = 0;
554 dealloced = 0;
555 @try {
556 state++;
557 @try {
558 state++;
559 @throw [Super exception];
560 state = BAD;
561 }
562 @catch (...) {
563 state++;
564
565 @try {
566 state++;
567 @throw;
568 state = BAD;
569 }
570 @catch (Sub *e) {
571 state = BAD;
572 }
573 @finally {
574 state++;
575 }
576
577 state = BAD;
578 }
579 state = BAD;
580 }
581 @catch (id e) {
582 state++;
583 [e check]; // state++
584 }
585 });
586 testassert(state == 7);
587 testassert(dealloced == 1);
588
589
590 #if __cplusplus && __OBJC2__
591 testprintf("C++ try/catch, Objective-C exception superclass\n");
592
593 TEST({
594 state = 0;
595 dealloced = 0;
596 try {
597 state++;
598 try {
599 state++;
600 try {
601 state++;
602 @throw [Super exception];
603 state = BAD;
604 } catch (...) {
605 state++;
606 throw;
607 state = BAD;
608 }
609 state = BAD;
610 } catch (void *e) {
611 state = BAD;
612 } catch (int e) {
613 state = BAD;
614 } catch (Sub *e) {
615 state = BAD;
616 } catch (Super *e) {
617 state++;
618 [e check]; // state++
619 throw;
620 } catch (...) {
621 state = BAD;
622 }
623 } catch (id e) {
624 state++;
625 [e check]; // state++;
626 }
627 });
628 testassert(state == 8);
629 testassert(dealloced == 1);
630
631
632 testprintf("C++ try/catch, Objective-C exception subclass\n");
633
634 TEST({
635 state = 0;
636 dealloced = 0;
637 try {
638 state++;
639 try {
640 state++;
641 try {
642 state++;
643 @throw [Sub exception];
644 state = BAD;
645 } catch (...) {
646 state++;
647 throw;
648 state = BAD;
649 }
650 state = BAD;
651 } catch (void *e) {
652 state = BAD;
653 } catch (int e) {
654 state = BAD;
655 } catch (Super *e) {
656 state++;
657 [e check]; // state++
658 throw;
659 } catch (Sub *e) {
660 state = BAD;
661 } catch (...) {
662 state = BAD;
663 }
664 } catch (id e) {
665 state++;
666 [e check]; // state++;
667 }
668 });
669 testassert(state == 8);
670 testassert(dealloced == 1);
671
672 #endif
673
674
675 #if !__OBJC2__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
676 // alt handlers for modern Mac OS only
677
678 #else
679 {
680 // alt handlers
681 // run a lot to catch failed unregistration (runtime complains at 1000)
682 #define ALT_HANDLER_REPEAT 2000
683
684 testprintf("alt handler, no exception\n");
685
686 TEST({
687 dealloced = 0;
688 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
689 state = 0;
690 @try {
691 state++;
692 @try {
693 uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
694 state++;
695 objc_removeExceptionHandler(token);
696 }
697 @catch (...) {
698 state = BAD;
699 }
700 state++;
701 } @catch (...) {
702 state = BAD;
703 }
704 testassert(state == 3);
705 }
706 });
707 testassert(dealloced == 0);
708
709
710 testprintf("alt handler, exception thrown through\n");
711
712 TEST({
713 dealloced = 0;
714 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
715 state = 0;
716 @try {
717 state++;
718 @try {
719 state++;
720 uintptr_t token = objc_addExceptionHandler(altHandler2, (void*)altHandler2);
721 // state++ inside alt handler
722 @throw [Super exception];
723 state = BAD;
724 objc_removeExceptionHandler(token);
725 }
726 @catch (Sub *e) {
727 state = BAD;
728 }
729 state = BAD;
730 }
731 @catch (id e) {
732 testassert(state == 3);
733 state++;
734 [e check]; // state++
735 }
736 testassert(state == 5);
737 }
738 });
739 testassert(dealloced == ALT_HANDLER_REPEAT);
740
741
742 testprintf("alt handler, nested\n");
743
744 TEST({
745 dealloced = 0;
746 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
747 state = 0;
748 @try {
749 state++;
750 @try {
751 state++;
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
758 state = BAD;
759 objc_removeExceptionHandler(token);
760 objc_removeExceptionHandler(token2);
761 }
762 @catch (id e) {
763 testassert(state == 6);
764 state++;
765 [e check]; // state++;
766 }
767 state++;
768 }
769 @catch (...) {
770 state = BAD;
771 }
772 testassert(state == 9);
773 }
774 });
775 testassert(dealloced == ALT_HANDLER_REPEAT);
776
777
778 testprintf("alt handler, nested, rethrows in between\n");
779
780 TEST({
781 dealloced = 0;
782 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
783 state = 0;
784 @try {
785 state++;
786 @try {
787 state++;
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
794 state = BAD;
795 objc_removeExceptionHandler(token);
796 objc_removeExceptionHandler(token2);
797 }
798 @catch (...) {
799 testassert(state == 7);
800 state++;
801 @throw;
802 }
803 state = BAD;
804 }
805 @catch (id e) {
806 testassert(state == 8);
807 state++;
808 [e check]; // state++
809 }
810 testassert(state == 10);
811 }
812 });
813 testassert(dealloced == ALT_HANDLER_REPEAT);
814
815
816 testprintf("alt handler, exception thrown and caught inside\n");
817
818 TEST({
819 dealloced = 0;
820 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
821 state = 0;
822 @try {
823 state++;
824 uintptr_t token = objc_addExceptionHandler(altHandlerFail, 0);
825 @try {
826 state++;
827 @throw [Super exception];
828 state = BAD;
829 }
830 @catch (Super *e) {
831 state++;
832 [e check]; // state++
833 }
834 state++;
835 objc_removeExceptionHandler(token);
836 }
837 @catch (...) {
838 state = BAD;
839 }
840 testassert(state == 5);
841 }
842 });
843 testassert(dealloced == ALT_HANDLER_REPEAT);
844
845
846 #if defined(USE_FOUNDATION)
847 testprintf("alt handler, rdar://10055775\n");
848
849 TEST({
850 dealloced = 0;
851 for (int i = 0; i < ALT_HANDLER_REPEAT; i++) {
852 state = 0;
853 @try {
854 uintptr_t token = objc_addExceptionHandler(altHandler1, (void*)altHandler1);
855 {
856 id x = [NSArray array];
857 x = [NSArray array];
858 }
859 state++;
860 // state++ inside alt handler
861 [Super raise:@"foo" format:@"bar"];
862 state = BAD;
863 objc_removeExceptionHandler(token);
864 } @catch (id e) {
865 state++;
866 testassert(state == 3);
867 }
868 testassert(state == 3);
869 }
870 });
871 testassert(dealloced == ALT_HANDLER_REPEAT);
872
873 // defined(USE_FOUNDATION)
874 #endif
875
876 }
877 // alt handlers
878 #endif
879
880 #if __cplusplus && __OBJC2__
881 std::set_terminate(terminator);
882 objc_terminate();
883 fail("should not have returned from objc_terminate()");
884 #else
885 succeed(FILENAME);
886 #endif
887 }
888