instructions.js (33604B)
1 // Tests for Wasm exception proposal instructions. 2 3 // Test try blocks with no handlers. 4 assertEq( 5 wasmEvalText( 6 `(module 7 (func (export "f") (result i32) 8 try (result i32) (i32.const 0) end))` 9 ).exports.f(), 10 0 11 ); 12 13 assertEq( 14 wasmEvalText( 15 `(module 16 (func (export "f") (result i32) 17 try (result i32) (i32.const 0) (br 0) (i32.const 1) end))` 18 ).exports.f(), 19 0 20 ); 21 22 assertEq( 23 wasmEvalText( 24 `(module 25 (type (func)) 26 (tag $exn (type 0)) 27 (func (export "f") (result i32) 28 try (result i32) 29 try (result i32) 30 (throw $exn) 31 (i32.const 1) 32 end 33 drop 34 (i32.const 2) 35 catch $exn 36 (i32.const 0) 37 end))` 38 ).exports.f(), 39 0 40 ); 41 42 assertEq( 43 wasmEvalText( 44 `(module 45 (type (func)) 46 (tag $exn (type 0)) 47 (func (export "f") (result i32) 48 try (result i32) 49 try (result i32) 50 try 51 try 52 (throw $exn) 53 end 54 end 55 (i32.const 1) 56 end 57 drop 58 (i32.const 2) 59 catch $exn 60 (i32.const 0) 61 end))` 62 ).exports.f(), 63 0 64 ); 65 66 assertEq( 67 wasmEvalText( 68 `(module 69 (type (func)) 70 (tag $exn (type 0)) 71 (func (export "f") (result i32) 72 try (result i32) 73 try (result i32) 74 try 75 try 76 (throw $exn) 77 end 78 catch_all 79 rethrow 0 80 end 81 (i32.const 1) 82 end 83 drop 84 (i32.const 2) 85 catch $exn 86 (i32.const 0) 87 end))` 88 ).exports.f(), 89 0 90 ); 91 92 // Test trivial try-catch with empty bodies. 93 assertEq( 94 wasmEvalText( 95 `(module 96 (type (func)) 97 (tag $exn (type 0)) 98 (func (export "f") (result i32) 99 try nop catch $exn end 100 (i32.const 0)))` 101 ).exports.f(), 102 0 103 ); 104 105 assertEq( 106 wasmEvalText( 107 `(module 108 (func (export "f") (result i32) 109 try nop catch_all end 110 (i32.const 0)))` 111 ).exports.f(), 112 0 113 ); 114 115 // Test try block with no throws 116 assertEq( 117 wasmEvalText( 118 `(module 119 (type (func)) 120 (tag $exn (type 0)) 121 (func (export "f") (result i32) 122 try (result i32) 123 (i32.const 0) 124 catch $exn 125 (i32.const 1) 126 end))` 127 ).exports.f(), 128 0 129 ); 130 131 // Ensure catch block is really not run when no throw occurs. 132 assertEq( 133 wasmEvalText( 134 `(module 135 (type (func)) 136 (tag $exn (type 0)) 137 (func (export "f") (result i32) (local i32) 138 try 139 (local.set 0 (i32.const 42)) 140 catch $exn 141 (local.set 0 (i32.const 99)) 142 end 143 (local.get 0)))` 144 ).exports.f(), 145 42 146 ); 147 148 // Simple uses of throw. 149 assertEq( 150 wasmEvalText( 151 `(module 152 (type (func (param i32))) 153 (tag $exn (type 0)) 154 (func (export "f") (result i32) 155 try (result i32) 156 (i32.const 42) 157 (throw $exn) 158 catch $exn 159 drop 160 (i32.const 1) 161 end))` 162 ).exports.f(), 163 1 164 ); 165 166 assertEq( 167 wasmEvalText( 168 `(module 169 (type (func (param i32))) 170 (tag $exn (type 0)) 171 (func $foo (param i32) (result i32) 172 (local.get 0) (throw $exn)) 173 (func (export "f") (result i32) 174 try (result i32) 175 (i32.const 42) 176 (call $foo) 177 catch $exn 178 drop 179 (i32.const 1) 180 end))` 181 ).exports.f(), 182 1 183 ); 184 185 // Simple uses of throw of some Wasm vectortype values (Simd128). 186 if (wasmSimdEnabled()) { 187 assertEq( 188 wasmEvalText( 189 `(module 190 (type (func (param v128 v128 v128 v128))) 191 (tag $exn (type 0)) 192 (func (export "f") (result i32) 193 try (result i32) 194 (v128.const i32x4 42 41 40 39) 195 (v128.const f32x4 4.2 4.1 0.40 3.9) 196 (v128.const i64x2 42 41) 197 (v128.const f64x2 4.2 4.1) 198 (throw $exn) 199 catch $exn 200 drop drop drop 201 (i64x2.all_true) 202 end))` 203 ).exports.f(), 204 1 205 ); 206 207 assertEq( 208 wasmEvalText( 209 `(module 210 (type (func (param v128 v128 v128 v128))) 211 (tag $exn (type 0)) 212 (func $foo (param v128 v128 v128 v128) (result i32) 213 (throw $exn (local.get 0) 214 (local.get 1) 215 (local.get 2) 216 (local.get 3))) 217 (func (export "f") (result i32) 218 try (result i32) 219 (v128.const i32x4 42 41 40 39) 220 (v128.const f32x4 4.2 4.1 0.40 3.9) 221 (v128.const i64x2 42 41) 222 (v128.const f64x2 4.2 4.1) 223 (call $foo) 224 catch $exn 225 drop drop drop 226 (i64x2.all_true) 227 end))` 228 ).exports.f(), 229 1 230 ); 231 232 { 233 let imports = 234 wasmEvalText( 235 `(module 236 (tag $exn (export "exn") (param v128)) 237 (func (export "throws") (param v128) (result v128) 238 (throw $exn (local.get 0)) 239 (v128.const i32x4 9 10 11 12)))`).exports; 240 241 let mod = 242 `(module 243 (import "m" "exn" (tag $exn (param v128))) 244 (import "m" "throws" (func $throws (param v128) (result v128))) 245 (func (export "f") (result i32) (local v128) 246 (v128.const i32x4 1 2 3 4) 247 (local.tee 0) 248 try (param v128) (result v128) 249 (call $throws) 250 catch $exn 251 catch_all (v128.const i32x4 5 6 7 8) 252 end 253 (local.get 0) 254 (i32x4.eq) 255 (i32x4.all_true)))`; 256 257 assertEq(wasmEvalText(mod, { m : imports }).exports.f(), 1); 258 } 259 } 260 261 // Further nested call frames should be ok. 262 assertEq( 263 wasmEvalText( 264 `(module 265 (type (func (param i32))) 266 (tag $exn (type 0)) 267 (func $foo (param i32) (result i32) 268 (local.get 0) (call $bar)) 269 (func $bar (param i32) (result i32) 270 (local.get 0) (call $quux)) 271 (func $quux (param i32) (result i32) 272 (local.get 0) (throw $exn)) 273 (func (export "f") (result i32) 274 try (result i32) 275 (i32.const 42) 276 (call $foo) 277 catch $exn 278 drop 279 (i32.const 1) 280 end))` 281 ).exports.f(), 282 1 283 ); 284 285 // Basic throwing from loop. 286 287 assertEq( 288 wasmEvalText( 289 `(module 290 (tag $exn) 291 ;; For the purpose of this test, the params below should be increasing. 292 (func (export "f") (param $depth_to_throw_exn i32) 293 (param $maximum_loop_iterations i32) 294 (result i32) 295 (local $loop_counter i32) 296 ;; The loop is counting down. 297 (local.get $maximum_loop_iterations) 298 (local.set $loop_counter) 299 (block $catch 300 (loop $loop 301 (if (i32.eqz (local.get $loop_counter)) 302 (then 303 (return (i32.const 440))) 304 (else 305 try 306 (if (i32.eq (local.get $depth_to_throw_exn) 307 (local.get $loop_counter)) 308 (then 309 (throw $exn)) 310 (else 311 (local.set $loop_counter 312 (i32.sub (local.get $loop_counter) 313 (i32.const 1))))) 314 catch $exn (br $catch) 315 catch_all 316 end)) 317 (br $loop)) 318 (return (i32.const 10001))) 319 (i32.const 10000)))` 320 ).exports.f(2, 4), 321 10000 322 ); 323 324 // Ensure conditional throw works. 325 let conditional = wasmEvalText( 326 `(module 327 (type (func (param))) 328 (tag $exn (type 0)) 329 (func (export "f") (param i32) (result i32) 330 try (result i32) 331 (local.get 0) 332 if (result i32) 333 (throw $exn) 334 else 335 (i32.const 42) 336 end 337 catch $exn 338 (i32.const 99) 339 end))` 340 ).exports.f; 341 342 assertEq(conditional(0), 42); 343 assertEq(conditional(1), 99); 344 345 // Ensure multiple & nested try-catch blocks work. 346 assertEq( 347 wasmEvalText( 348 `(module 349 (type (func (param))) 350 (tag $exn (type 0)) 351 (func $foo (throw $exn)) 352 (func (export "f") (result i32) (local i32) 353 try 354 nop 355 catch $exn 356 (local.set 0 (i32.const 99)) 357 end 358 try 359 (call $foo) 360 catch $exn 361 (local.set 0 (i32.const 42)) 362 end 363 (local.get 0)))` 364 ).exports.f(), 365 42 366 ); 367 368 assertEq( 369 wasmEvalText( 370 `(module 371 (type (func (param))) 372 (tag $exn (type 0)) 373 (func (export "f") (result i32) (local i32) 374 try 375 try 376 try 377 (throw $exn) 378 catch $exn 379 (local.set 0 (i32.const 42)) 380 end 381 catch $exn 382 (local.set 0 (i32.const 97)) 383 end 384 catch $exn 385 (local.set 0 (i32.const 98)) 386 end 387 (local.get 0)))` 388 ).exports.f(), 389 42 390 ); 391 392 assertEq( 393 wasmEvalText( 394 `(module 395 (tag $exn) 396 (func (export "f") (result i32) 397 try 398 throw $exn 399 catch $exn 400 try 401 (throw $exn) 402 catch $exn 403 end 404 end 405 (i32.const 27)))` 406 ).exports.f(), 407 27 408 ); 409 410 { 411 let nested_throw_in_block_and_in_catch = 412 wasmEvalText( 413 `(module 414 (tag $exn) 415 (func $throw 416 (throw $exn)) 417 (func (export "f") (param $arg i32) (result i32) 418 (block (result i32) 419 try (result i32) 420 (call $throw) 421 (unreachable) 422 catch $exn 423 (if (result i32) 424 (local.get $arg) 425 (then 426 try (result i32) 427 (call $throw) 428 (unreachable) 429 catch $exn 430 (i32.const 27) 431 end) 432 (else 433 (i32.const 11)) 434 ) 435 end 436 ) 437 ))` 438 ).exports.f; 439 440 assertEq(nested_throw_in_block_and_in_catch(1), 27); 441 assertEq(nested_throw_in_block_and_in_catch(0), 11); 442 } 443 444 assertEq( 445 wasmEvalText( 446 `(module 447 (tag $thrownExn) 448 (tag $notThrownExn) 449 (func (export "f") (result i32) 450 try (result i32) 451 try (result i32) 452 (throw $thrownExn) 453 catch $notThrownExn 454 (i32.const 19) 455 catch $thrownExn 456 (i32.const 20) 457 catch_all 458 (i32.const 21) 459 end 460 end))` 461 ).exports.f(), 462 20 463 ); 464 465 // Test that uncaught exceptions get propagated. 466 assertEq( 467 wasmEvalText( 468 `(module 469 (tag $thrownExn) 470 (tag $notThrownExn) 471 (func (export "f") (result i32) (local i32) 472 try 473 try 474 try 475 (throw $thrownExn) 476 catch $notThrownExn 477 (local.set 0 478 (i32.or (local.get 0) 479 (i32.const 1))) 480 end 481 catch $notThrownExn 482 (local.set 0 483 (i32.or (local.get 0) 484 (i32.const 2))) 485 end 486 catch $thrownExn 487 (local.set 0 488 (i32.or (local.get 0) 489 (i32.const 4))) 490 end 491 (local.get 0)))` 492 ).exports.f(), 493 4 494 ); 495 496 // Test tag dispatch for catches. 497 assertEq( 498 wasmEvalText( 499 `(module 500 (type (func (param))) 501 (tag $exn1 (type 0)) 502 (tag $exn2 (type 0)) 503 (tag $exn3 (type 0)) 504 (func (export "f") (result i32) 505 try (result i32) 506 throw $exn1 507 catch $exn1 508 i32.const 1 509 catch $exn2 510 i32.const 2 511 catch $exn3 512 i32.const 3 513 end))` 514 ).exports.f(), 515 1 516 ); 517 518 assertEq( 519 wasmEvalText( 520 `(module 521 (type (func (param))) 522 (tag $exn1 (type 0)) 523 (tag $exn2 (type 0)) 524 (tag $exn3 (type 0)) 525 (func (export "f") (result i32) 526 try (result i32) 527 throw $exn2 528 catch $exn1 529 i32.const 1 530 catch $exn2 531 i32.const 2 532 catch $exn3 533 i32.const 3 534 end))` 535 ).exports.f(), 536 2 537 ); 538 539 assertEq( 540 wasmEvalText( 541 `(module 542 (type (func (param))) 543 (tag $exn1 (type 0)) 544 (tag $exn2 (type 0)) 545 (tag $exn3 (type 0)) 546 (func (export "f") (result i32) 547 try (result i32) 548 throw $exn3 549 catch $exn1 550 i32.const 1 551 catch $exn2 552 i32.const 2 553 catch $exn3 554 i32.const 3 555 end))` 556 ).exports.f(), 557 3 558 ); 559 560 assertEq( 561 wasmEvalText( 562 `(module 563 (type (func (param))) 564 (tag $exn1 (type 0)) 565 (tag $exn2 (type 0)) 566 (tag $exn3 (type 0)) 567 (tag $exn4 (type 0)) 568 (func (export "f") (result i32) 569 try (result i32) 570 try (result i32) 571 throw $exn4 572 catch $exn1 573 i32.const 1 574 catch $exn2 575 i32.const 2 576 catch $exn3 577 i32.const 3 578 end 579 catch $exn4 580 i32.const 4 581 end))` 582 ).exports.f(), 583 4 584 ); 585 586 // Test usage of br before a throw. 587 assertEq( 588 wasmEvalText( 589 `(module 590 (type (func (param i32))) 591 (tag $exn (type 0)) 592 (func (export "f") (result i32) 593 try $l (result i32) 594 (i32.const 2) 595 (br $l) 596 (throw $exn) 597 catch $exn 598 drop 599 (i32.const 1) 600 end))` 601 ).exports.f(), 602 2 603 ); 604 605 assertEq( 606 wasmEvalText( 607 `(module 608 (type (func (param))) 609 (tag $exn (type 0)) 610 (func (export "f") (result i32) 611 try $l (result i32) 612 (throw $exn) 613 catch $exn 614 (i32.const 2) 615 (br $l) 616 rethrow 0 617 end))` 618 ).exports.f(), 619 2 620 ); 621 622 assertEq( 623 wasmEvalText( 624 `(module 625 (type (func (param))) 626 (tag $exn (type 0)) 627 (func (export "f") (result i32) 628 try $l (result i32) 629 (throw $exn) 630 catch_all 631 (i32.const 2) 632 (br $l) 633 rethrow 0 634 end))` 635 ).exports.f(), 636 2 637 ); 638 639 // Test br branching out of a catch block. 640 assertEq( 641 wasmEvalText( 642 `(module 643 (type (func (param i32))) 644 (tag $exn (type 0)) 645 (func (export "f") (result i32) 646 block $l (result i32) 647 block (result i32) 648 try (result i32) 649 (i32.const 42) 650 (throw $exn) 651 catch $exn 652 br $l 653 (i32.const 99) 654 end 655 end 656 end))` 657 ).exports.f(), 658 42 659 ); 660 661 // Test dead catch block. 662 assertEq( 663 wasmEvalText( 664 `(module 665 (type (func)) 666 (tag $exn (type 0)) 667 (func (export "f") (result i32) 668 i32.const 0 669 return 670 try nop catch $exn end))` 671 ).exports.f(), 672 0 673 ); 674 675 assertEq( 676 wasmEvalText( 677 `(module 678 (func (export "f") (result i32) 679 i32.const 0 680 return 681 try nop catch_all end))` 682 ).exports.f(), 683 0 684 ); 685 686 // Test catch with exception values pushed to stack. 687 assertEq( 688 wasmEvalText( 689 `(module 690 (type (func (param i32))) 691 (type (func (param i32))) 692 (type (func (param i64))) 693 (tag $exn (type 0)) 694 (tag $foo (type 1)) 695 (tag $bar (type 2)) 696 (func (export "f") (result i32) 697 try $l (result i32) 698 (i32.const 42) 699 (throw $exn) 700 catch $exn 701 catch_all 702 (i32.const 99) 703 end))` 704 ).exports.f(), 705 42 706 ); 707 708 // Throw an exception carrying more than one value. 709 assertEq( 710 wasmEvalText( 711 `(module 712 (type (func (param i32 i64 f32 f64))) 713 (tag $exn (type 0)) 714 (func (export "f") (result i32) 715 try $l (result i32 i64 f32 f64) 716 (i32.const 42) 717 (i64.const 84) 718 (f32.const 42.2) 719 (f64.const 84.4) 720 (throw $exn) 721 catch $exn 722 catch_all 723 (i32.const 99) 724 (i64.const 999) 725 (f32.const 99.9) 726 (f64.const 999.9) 727 end 728 drop drop drop))` 729 ).exports.f(), 730 42 731 ); 732 733 // This should also work inside nested frames. 734 assertEq( 735 wasmEvalText( 736 `(module 737 (type (func (param i32 i64 f32 f64))) 738 (tag $exn (type 0)) 739 (func $foo (param i32 i64 f32 f64) (result i32 i64 f32 f64) 740 (local.get 0) 741 (local.get 1) 742 (local.get 2) 743 (local.get 3) 744 (throw $exn)) 745 (func (export "f") (result i32) 746 try $l (result i32 i64 f32 f64) 747 (i32.const 42) 748 (i64.const 84) 749 (f32.const 42.2) 750 (f64.const 84.4) 751 (call $foo) 752 catch $exn 753 catch_all 754 (i32.const 99) 755 (i64.const 999) 756 (f32.const 99.9) 757 (f64.const 999.9) 758 end 759 drop drop drop))` 760 ).exports.f(), 761 42 762 ); 763 764 // Multiple tagged catch in succession. 765 assertEq( 766 wasmEvalText( 767 `(module 768 (type (func (param i32))) 769 (tag $exn1 (type 0)) 770 (tag $exn2 (type 0)) 771 (func (export "f") (result i32) 772 try (result i32) 773 (i32.const 42) 774 (throw $exn2) 775 catch $exn1 776 catch $exn2 777 catch_all 778 (i32.const 99) 779 end))` 780 ).exports.f(), 781 42 782 ); 783 784 assertEq( 785 wasmEvalText( 786 `(module 787 (tag $exn0) 788 (tag $exn1) 789 (tag $exn2) 790 (tag $exn3) 791 (tag $exn4) 792 (tag $exn5) 793 (func (export "f") (result i32) 794 try (result i32) 795 (throw $exn4) 796 catch $exn5 (i32.const 5) 797 catch $exn2 (i32.const 2) 798 catch $exn4 (i32.const 4) ;; Caught here. 799 catch $exn4 (i32.const 44) 800 end 801 ))` 802 ).exports.f(), 803 4 804 ); 805 806 // Try catch with block parameters. 807 assertEq( 808 wasmEvalText( 809 `(module 810 (type (func)) 811 (tag $exn (type 0)) 812 (func (export "f") (result i32) 813 (i32.const 42) 814 try (param i32) (result i32) 815 nop 816 catch $exn 817 (i32.const 99) 818 end))` 819 ).exports.f(), 820 42 821 ); 822 823 assertEq( 824 wasmEvalText( 825 `(module 826 (type (func (param i32))) 827 (tag $exn (type 0)) 828 (func (export "f") (result i32) 829 (i32.const 42) 830 try $l (param i32) (result i32) 831 (throw $exn) 832 catch $exn 833 catch_all 834 (i32.const 99) 835 end))` 836 ).exports.f(), 837 42 838 ); 839 840 // Test the catch_all case. 841 assertEq( 842 wasmEvalText( 843 `(module 844 (type (func (param i32))) 845 (tag $exn1 (type 0)) 846 (tag $exn2 (type 0)) 847 (func (export "f") (result i32) 848 try $l (result i32) 849 (i32.const 42) 850 (throw $exn2) 851 catch $exn1 852 catch_all 853 (i32.const 99) 854 end))` 855 ).exports.f(), 856 99 857 ); 858 859 assertEq( 860 wasmEvalText( 861 `(module 862 (tag $exn (param i32)) 863 (func (export "f") (result i32) 864 try (result i32) 865 try (result i32) 866 (i32.const 42) 867 (throw $exn) 868 catch_all 869 (i32.const 99) 870 end 871 catch $exn 872 end))` 873 ).exports.f(), 874 99 875 ); 876 877 // Test foreign exception catch. 878 assertEq( 879 wasmEvalText( 880 `(module 881 (type (func)) 882 (import "m" "foreign" (func $foreign)) 883 (tag $exn (type 0)) 884 (func (export "f") (result i32) (local i32) 885 try $l 886 (call $foreign) 887 catch $exn 888 catch_all 889 (local.set 0 (i32.const 42)) 890 end 891 (local.get 0)))`, 892 { 893 m: { 894 foreign() { 895 throw 5; 896 }, 897 }, 898 } 899 ).exports.f(), 900 42 901 ); 902 903 // Exception handlers should not catch traps. 904 assertErrorMessage( 905 () => 906 wasmEvalText( 907 `(module 908 (type (func)) 909 (tag $exn (type 0)) 910 (func (export "f") (result i32) (local i32) 911 try $l 912 unreachable 913 catch $exn 914 (local.set 0 (i32.const 98)) 915 catch_all 916 (local.set 0 (i32.const 99)) 917 end 918 (local.get 0)))` 919 ).exports.f(), 920 WebAssembly.RuntimeError, 921 "unreachable executed" 922 ); 923 924 // Ensure that a RuntimeError created by the user is not filtered out 925 // as a trap emitted by the runtime (i.e., the filtering predicate is not 926 // observable from JS). 927 assertEq( 928 wasmEvalText( 929 `(module 930 (import "m" "foreign" (func $foreign)) 931 (func (export "f") (result i32) 932 try (result i32) 933 (call $foreign) 934 (i32.const 99) 935 catch_all 936 (i32.const 42) 937 end))`, 938 { 939 m: { 940 foreign() { 941 throw new WebAssembly.RuntimeError(); 942 }, 943 }, 944 } 945 ).exports.f(), 946 42 947 ); 948 949 // Test uncatchable JS exceptions (OOM & stack overflow). 950 { 951 let f = wasmEvalText( 952 `(module 953 (import "m" "foreign" (func $foreign)) 954 (func (export "f") (result) 955 try 956 (call $foreign) 957 catch_all 958 end))`, 959 { 960 m: { 961 foreign() { 962 throwOutOfMemory(); 963 }, 964 }, 965 } 966 ).exports.f; 967 968 var thrownVal; 969 try { 970 f(); 971 } catch (exn) { 972 thrownVal = exn; 973 } 974 975 assertEq(thrownVal, "out of memory"); 976 } 977 978 assertErrorMessage( 979 () => 980 wasmEvalText( 981 `(module 982 (import "m" "foreign" (func $foreign)) 983 (func (export "f") 984 try 985 (call $foreign) 986 catch_all 987 end))`, 988 { 989 m: { 990 foreign: function foreign() { 991 foreign(); 992 }, 993 }, 994 } 995 ).exports.f(), 996 Error, 997 "too much recursion" 998 ); 999 1000 // Test all implemented instructions in a single module. 1001 { 1002 let divFunctypeInline = 1003 `(param $numerator i32) (param $denominator i32) (result i32)`; 1004 1005 let safediv = wasmEvalText( 1006 `(module 1007 (tag $divexn (param i32 i32)) 1008 (tag $notThrownExn (param i32)) 1009 (func $throwingdiv ${divFunctypeInline} 1010 (local.get $numerator) 1011 (local.get $denominator) 1012 (if (param i32 i32) (result i32) 1013 (i32.eqz (local.get $denominator)) 1014 (then 1015 try (param i32 i32) 1016 (throw $divexn) 1017 delegate 0 1018 (i32.const 9)) 1019 (else 1020 i32.div_u))) 1021 (func $safediv (export "safediv") ${divFunctypeInline} 1022 (local.get $numerator) 1023 (local.get $denominator) 1024 try (param i32 i32) (result i32) 1025 (call $throwingdiv) 1026 catch $notThrownExn 1027 catch $divexn 1028 i32.add 1029 catch_all 1030 (i32.const 44) 1031 end 1032 ))` 1033 ).exports.safediv; 1034 1035 assertEq(safediv(6, 3), 2); 1036 assertEq(safediv(6, 0), 6); 1037 } 1038 1039 // Test simple rethrow. 1040 assertEq( 1041 wasmEvalText( 1042 `(module 1043 (type (func)) 1044 (tag $exn (type 0)) 1045 (func (export "f") (result i32) 1046 try (result i32) 1047 try 1048 throw $exn 1049 catch $exn 1050 rethrow 0 1051 end 1052 i32.const 1 1053 catch $exn 1054 i32.const 27 1055 end))` 1056 ).exports.f(), 1057 27 1058 ); 1059 1060 assertEq( 1061 wasmEvalText( 1062 `(module 1063 (type (func)) 1064 (tag $exn (type 0)) 1065 (func (export "f") (result i32) 1066 try (result i32) 1067 try 1068 throw $exn 1069 catch_all 1070 rethrow 0 1071 end 1072 i32.const 1 1073 catch $exn 1074 i32.const 27 1075 end))` 1076 ).exports.f(), 1077 27 1078 ); 1079 1080 // Test rethrows in nested blocks. 1081 assertEq( 1082 wasmEvalText( 1083 `(module 1084 (type (func)) 1085 (tag $exn (type 0)) 1086 (func (export "f") (result i32) 1087 try (result i32) 1088 try 1089 throw $exn 1090 catch $exn 1091 block 1092 rethrow 1 1093 end 1094 end 1095 i32.const 1 1096 catch $exn 1097 i32.const 27 1098 end))` 1099 ).exports.f(), 1100 27 1101 ); 1102 1103 assertEq( 1104 wasmEvalText( 1105 `(module 1106 (type (func)) 1107 (tag $exn (type 0)) 1108 (func (export "f") (result i32) 1109 try (result i32) 1110 try 1111 throw $exn 1112 catch_all 1113 block 1114 rethrow 1 1115 end 1116 end 1117 i32.const 1 1118 catch $exn 1119 i32.const 27 1120 end))` 1121 ).exports.f(), 1122 27 1123 ); 1124 1125 assertEq( 1126 wasmEvalText( 1127 `(module 1128 (type (func)) 1129 (tag $exn1 (type 0)) 1130 (tag $exn2 (type 0)) 1131 (func (export "f") (result i32) 1132 try (result i32) 1133 try 1134 throw $exn1 1135 catch $exn1 1136 try 1137 throw $exn2 1138 catch $exn2 1139 rethrow 1 1140 end 1141 end 1142 i32.const 0 1143 catch $exn1 1144 i32.const 1 1145 catch $exn2 1146 i32.const 2 1147 end))` 1148 ).exports.f(), 1149 1 1150 ); 1151 1152 assertEq( 1153 wasmEvalText( 1154 `(module 1155 (type (func)) 1156 (tag $exn1 (type 0)) 1157 (tag $exn2 (type 0)) 1158 (func (export "f") (result i32) 1159 try (result i32) 1160 try 1161 throw $exn1 1162 catch $exn1 1163 try 1164 throw $exn2 1165 catch_all 1166 rethrow 1 1167 end 1168 end 1169 i32.const 0 1170 catch $exn1 1171 i32.const 1 1172 catch $exn2 1173 i32.const 2 1174 end))` 1175 ).exports.f(), 1176 1 1177 ); 1178 1179 // Test that rethrow makes the rest of the block dead code. 1180 assertEq( 1181 wasmEvalText( 1182 `(module 1183 (tag (param i32)) 1184 (func (export "f") (result i32) 1185 try (result i32) 1186 (i32.const 1) 1187 catch 0 1188 (rethrow 0) 1189 (i32.const 2) 1190 end 1191 ))` 1192 ).exports.f(), 1193 1 1194 ); 1195 1196 assertEq( 1197 wasmEvalText( 1198 `(module 1199 (tag (param i32)) 1200 (func (export "f") (result i32) 1201 try (result i32) 1202 try 1203 (i32.const 13) 1204 (throw 0) 1205 catch 0 1206 (rethrow 0) 1207 end 1208 (unreachable) 1209 catch 0 1210 end 1211 ))` 1212 ).exports.f(), 1213 13 1214 ); 1215 1216 assertEq( 1217 wasmEvalText( 1218 `(module 1219 (tag) 1220 (func (export "f") (result i32) 1221 try (result i32) 1222 try 1223 (throw 0) 1224 catch 0 1225 (i32.const 4) 1226 (rethrow 0) 1227 end 1228 (unreachable) 1229 catch 0 1230 (i32.const 13) 1231 end 1232 ))` 1233 ).exports.f(), 1234 13 1235 ); 1236 1237 assertEq( 1238 wasmEvalText( 1239 `(module 1240 (tag (param i32)) 1241 (func (export "f") (result i32) 1242 try (result i32) 1243 try 1244 (i32.const 13) 1245 (throw 0) 1246 catch 0 1247 (i32.const 111) 1248 (rethrow 0) 1249 end 1250 (i32.const 222) 1251 catch 0 1252 end 1253 ))` 1254 ).exports.f(), 1255 13 1256 ); 1257 1258 // Test try-delegate blocks. 1259 1260 // Dead delegate to caller 1261 assertEq( 1262 wasmEvalText( 1263 `(module 1264 (tag $exn (param)) 1265 (func (export "f") (result i32) 1266 i32.const 1 1267 br 0 1268 try 1269 throw $exn 1270 delegate 0))` 1271 ).exports.f(), 1272 1 1273 ); 1274 1275 // Nested try-delegate. 1276 assertEq( 1277 wasmEvalText( 1278 `(module 1279 (tag $exn (param)) 1280 (func (export "f") (result i32) 1281 try (result i32) 1282 try 1283 try 1284 throw $exn 1285 delegate 0 1286 end 1287 i32.const 0 1288 catch $exn 1289 i32.const 1 1290 end))` 1291 ).exports.f(), 1292 1 1293 ); 1294 1295 // Non-throwing and breaking try-delegate. 1296 assertEq( 1297 wasmEvalText( 1298 `(module 1299 (tag $exn (param)) 1300 (func (export "f") (result i32) 1301 try (result i32) 1302 i32.const 1 1303 br 0 1304 delegate 0))` 1305 ).exports.f(), 1306 1 1307 ); 1308 1309 assertEq( 1310 wasmEvalText( 1311 `(module 1312 (tag $exn (param)) 1313 (func (export "f") (result i32) 1314 try (result i32) 1315 i32.const 1 1316 return 1317 delegate 0))` 1318 ).exports.f(), 1319 1 1320 ); 1321 1322 // More nested try-delegate. 1323 assertEq( 1324 wasmEvalText( 1325 `(module 1326 (type (func (param i32))) 1327 (tag $exn (type 0)) 1328 (func (export "f") (result i32) 1329 try (result i32) 1330 try 1331 i32.const 42 1332 throw $exn 1333 delegate 0 1334 i32.const 0 1335 catch $exn 1336 i32.const 1 1337 i32.add 1338 end))` 1339 ).exports.f(), 1340 43 1341 ); 1342 1343 assertEq( 1344 wasmEvalText( 1345 `(module 1346 (type (func (param i32))) 1347 (tag $exn (type 0)) 1348 (func (export "f") (result i32) 1349 try (result i32) 1350 try (result i32) 1351 try 1352 i32.const 42 1353 throw $exn 1354 delegate 1 1355 i32.const 0 1356 catch $exn 1357 i32.const 1 1358 i32.add 1359 end 1360 catch $exn 1361 i32.const 2 1362 i32.add 1363 end))` 1364 ).exports.f(), 1365 44 1366 ); 1367 1368 assertEq( 1369 wasmEvalText( 1370 `(module 1371 (type (func (param i32))) 1372 (tag $exn (type 0)) 1373 (func (export "f") (result i32) 1374 try (result i32) 1375 try (result i32) 1376 try (result i32) 1377 try 1378 i32.const 42 1379 throw $exn 1380 delegate 1 1381 i32.const 0 1382 catch $exn 1383 i32.const 1 1384 i32.add 1385 end 1386 delegate 0 1387 catch $exn 1388 i32.const 2 1389 i32.add 1390 end))` 1391 ).exports.f(), 1392 44 1393 ); 1394 1395 assertEq( 1396 wasmEvalText( 1397 `(module 1398 (tag $exn (param)) 1399 (func $g (param i32) (result i32) (i32.const 42)) 1400 (func (export "f") (result i32) 1401 try (result i32) 1402 try $t 1403 block (result i32) 1404 (i32.const 4) 1405 (call $g) 1406 try 1407 throw $exn 1408 delegate $t 1409 end 1410 drop 1411 end 1412 i32.const 0 1413 catch_all 1414 i32.const 1 1415 end))` 1416 ).exports.f(), 1417 1 1418 ); 1419 1420 // Test delegation to function body and blocks. 1421 1422 // Non-throwing. 1423 assertEq( 1424 wasmEvalText( 1425 `(module 1426 (tag $exn (param)) 1427 (func (export "f") (result i32) 1428 try (result i32) 1429 i32.const 1 1430 delegate 0))` 1431 ).exports.f(), 1432 1 1433 ); 1434 1435 // Block target. 1436 assertEq( 1437 wasmEvalText( 1438 `(module 1439 (tag $exn (param i32)) 1440 (func (export "f") (result i32) 1441 try (result i32) 1442 block 1443 try 1444 i32.const 1 1445 throw $exn 1446 delegate 0 1447 end 1448 i32.const 0 1449 catch $exn 1450 end))` 1451 ).exports.f(), 1452 1 1453 ); 1454 1455 // Catch target. 1456 assertEq( 1457 wasmEvalText( 1458 `(module 1459 (tag $exn (param)) 1460 (func (export "f") (result i32) 1461 try (result i32) 1462 try 1463 throw $exn 1464 catch $exn 1465 try 1466 throw $exn 1467 delegate 0 1468 end 1469 i32.const 0 1470 catch_all 1471 i32.const 1 1472 end))` 1473 ).exports.f(), 1474 1 1475 ); 1476 1477 // Target function body. 1478 assertEq( 1479 wasmEvalText( 1480 `(module 1481 (type (func (param i32))) 1482 (tag $exn (type 0)) 1483 (func (export "f") (result i32) 1484 try (result i32) 1485 call $g 1486 catch $exn 1487 end) 1488 (func $g (result i32) 1489 try (result i32) 1490 try 1491 i32.const 42 1492 throw $exn 1493 delegate 1 1494 i32.const 0 1495 catch $exn 1496 i32.const 1 1497 i32.add 1498 end))` 1499 ).exports.f(), 1500 42 1501 ); 1502 1503 // Try-delegate from inside a loop. 1504 assertEq( 1505 wasmEvalText( 1506 `(module 1507 (tag $exn) 1508 ;; For the purpose of this test, the params below should be increasing. 1509 (func (export "f") (param $depth_to_throw_exn i32) 1510 (param $maximum_loop_iterations i32) 1511 (result i32) 1512 (local $loop_countdown i32) 1513 ;; Counts how many times the loop was started. 1514 (local $loop_verifier i32) 1515 ;; The loop is counting down. 1516 (local.get $maximum_loop_iterations) 1517 (local.set $loop_countdown) 1518 try $catch_exn (result i32) 1519 try 1520 (loop $loop 1521 ;; Counts how many times the loop was started. 1522 (local.set $loop_verifier 1523 (i32.add (i32.const 1) 1524 (local.get $loop_verifier))) 1525 (if (i32.eqz (local.get $loop_countdown)) 1526 (then (return (i32.const 440))) 1527 (else 1528 try $rethrow_label 1529 (if (i32.eq (local.get $depth_to_throw_exn) 1530 (local.get $loop_countdown)) 1531 (then (throw $exn)) 1532 (else 1533 (local.set $loop_countdown 1534 (i32.sub (local.get $loop_countdown) 1535 (i32.const 1))))) 1536 catch $exn try 1537 (rethrow $rethrow_label) 1538 delegate $catch_exn 1539 end 1540 )) 1541 (br $loop)) 1542 catch_all unreachable 1543 end 1544 (i32.const 2000) 1545 catch_all (i32.const 10000) 1546 end 1547 (i32.add (local.get $loop_verifier))))` 1548 ).exports.f(3, 5), 1549 10003 1550 );