1/* 2need exception-safe ARC for exception deallocation tests 3need F/CF for testonthread() in GC mode 4TEST_CFLAGS -fobjc-arc-exceptions -framework Foundation 5 6llvm-gcc unavoidably warns about our deliberately out-of-order handlers 7 8TEST_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 .* 16OR 17END 18*/ 19 20#include "test.h" 21#include "testroot.i" 22#include <objc/runtime.h> 23#include <objc/objc-exception.h> 24 25static volatile int state = 0; 26static 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 65void 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 78ALT_HANDLER(1) 79ALT_HANDLER(2) 80ALT_HANDLER(3) 81ALT_HANDLER(4) 82ALT_HANDLER(5) 83ALT_HANDLER(6) 84ALT_HANDLER(7) 85 86 87static void throwWithAltHandler(void) __attribute__((noinline)); 88static 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 105static void throwWithAltHandlerAndRethrow(void) __attribute__((noinline)); 106static 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> 128void 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 142int 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 889