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