arrays.js (66591B)
1 // |jit-test| test-also=--gc-zeal=2 2 3 // Test array instructions on different valtypes 4 5 const GENERAL_TESTS = [ 6 [ 7 'i32', 8 0, 9 0xce, 10 ], [ 11 'i64', 12 0n, 13 0xabcdefn, 14 ], [ 15 'f32', 16 0, 17 13.5, 18 ], [ 19 'f64', 20 0, 21 13.5, 22 ], [ 23 'externref', 24 null, 25 'hello', 26 ], 27 ]; 28 29 for (let [valtype, def, nondef] of GENERAL_TESTS) { 30 let {create, createDefault, get, set, len} = wasmEvalText(`(module 31 (type $a (array (mut ${valtype}))) 32 33 (; new T[0] = { 1... } ;) 34 (func (export "create") (param i32 ${valtype}) (result eqref) 35 local.get 1 36 local.get 0 37 array.new $a 38 ) 39 40 (; new T[0] ;) 41 (func (export "createDefault") (param i32) (result eqref) 42 local.get 0 43 array.new_default $a 44 ) 45 46 (; 0[1] ;) 47 (func (export "get") (param eqref i32) (result ${valtype}) 48 local.get 0 49 ref.cast (ref null $a) 50 local.get 1 51 array.get $a 52 ) 53 54 (; 0[1] = 2 ;) 55 (func (export "set") (param eqref i32 ${valtype}) 56 local.get 0 57 ref.cast (ref null $a) 58 local.get 1 59 local.get 2 60 array.set $a 61 ) 62 63 (; len(a) ;) 64 (func (export "len") (param eqref) (result i32) 65 local.get 0 66 ref.cast (ref null $a) 67 array.len 68 ) 69 )`).exports; 70 71 function checkArray(array, length, init, setval) { 72 // Check length 73 assertEq(len(array), length); 74 assertEq(wasmGcArrayLength(array), length); 75 76 // Check init value 77 for (let i = 0; i < length; i++) { 78 assertEq(wasmGcReadField(array, i), init); 79 assertEq(get(array, i), init); 80 } 81 82 // Set every element to setval 83 for (let i = 0; i < length; i++) { 84 set(array, i, setval); 85 86 // Check there is no overwrite 87 for (let j = i + 1; j < length; j++) { 88 assertEq(wasmGcReadField(array, j), init); 89 assertEq(get(array, j), init); 90 } 91 } 92 93 // Check out of bounds conditions 94 for (let loc of [-1, length, length + 1]) { 95 assertErrorMessage(() => { 96 get(array, loc); 97 }, WebAssembly.RuntimeError, /out of bounds/); 98 assertErrorMessage(() => { 99 set(array, loc, setval); 100 }, WebAssembly.RuntimeError, /out of bounds/); 101 } 102 } 103 104 for (let arrayLength = 0; arrayLength <= 25; arrayLength++) { 105 checkArray(createDefault(arrayLength), arrayLength, def, nondef); 106 checkArray(create(arrayLength, nondef), arrayLength, nondef, def); 107 } 108 } 109 110 // Check packed array reads and writes 111 112 for (let [fieldtype, max] of [ 113 [ 114 'i8', 115 0xff, 116 ], [ 117 'i16', 118 0xffff, 119 ] 120 ]) { 121 let {create, getS, getU, set} = wasmEvalText(`(module 122 (type $a (array (mut ${fieldtype}))) 123 124 (; new T[0] = { 1... } ;) 125 (func (export "create") (param i32 i32) (result eqref) 126 local.get 1 127 local.get 0 128 array.new $a 129 ) 130 131 (; 0[1] ;) 132 (func (export "getS") (param eqref i32) (result i32) 133 local.get 0 134 ref.cast (ref null $a) 135 local.get 1 136 array.get_s $a 137 ) 138 139 (; 0[1] ;) 140 (func (export "getU") (param eqref i32) (result i32) 141 local.get 0 142 ref.cast (ref null $a) 143 local.get 1 144 array.get_u $a 145 ) 146 147 (; 0[1] = 2 ;) 148 (func (export "set") (param eqref i32 i32) 149 local.get 0 150 ref.cast (ref null $a) 151 local.get 1 152 local.get 2 153 array.set $a 154 ) 155 )`).exports; 156 157 // Check zero and sign extension 158 let a = create(1, 0); 159 set(a, 0, max); 160 assertEq(getS(a, 0), -1); 161 assertEq(getU(a, 0), max); 162 163 // JS-API defaults to sign extension 164 assertEq(wasmGcReadField(a, 0), getS(a, 0)); 165 166 // Check array.new truncates init value 167 assertEq(wasmGcReadField(create(1, max + 1), 0), 0); 168 169 // Check array.set truncates 170 let b = create(1, 0); 171 set(b, 0, max + 1); 172 assertEq(getU(b, 0), 0); 173 174 // Check no overwrite on set 175 let c = create(2, 0); 176 set(c, 0, max << 4); 177 assertEq(getU(c, 0), (max << 4) & max); 178 assertEq(getU(c, 1), 0); 179 } 180 181 // Check arrays must be mutable to mutate 182 183 assertErrorMessage(() => wasmEvalText(`(module 184 (type $a (array i32)) 185 (func 186 (array.set $a 187 (array.new $a 188 i32.const 0xff 189 i32.const 10) 190 i32.const 0 191 i32.const 0 192 ) 193 ) 194 ) 195 `), WebAssembly.CompileError, /array is not mutable/); 196 197 // Check operations trap on null 198 199 assertErrorMessage(() => { 200 wasmEvalText(`(module 201 (type $a (array (mut i32))) 202 (func 203 ref.null $a 204 i32.const 0 205 array.get $a 206 drop 207 ) 208 (start 0) 209 )`); 210 }, WebAssembly.RuntimeError, /null/); 211 212 assertErrorMessage(() => { 213 wasmEvalText(`(module 214 (type $a (array (mut i32))) 215 (func 216 ref.null $a 217 i32.const 0 218 i32.const 0 219 array.set $a 220 ) 221 (start 0) 222 )`); 223 }, WebAssembly.RuntimeError, /null/); 224 225 assertErrorMessage(() => { 226 wasmEvalText(`(module 227 (type $a (array (mut i32))) 228 (func 229 ref.null $a 230 array.len 231 drop 232 ) 233 (start 0) 234 )`); 235 }, WebAssembly.RuntimeError, /null/); 236 237 // Check an extension postfix is present iff the element is packed 238 239 for ([fieldtype, packed] of [ 240 ['i8', true], 241 ['i16', true], 242 ['i32', false], 243 ['i64', false], 244 ['f32', false], 245 ['f64', false], 246 ]) { 247 let extensionModule = `(module 248 (type $a (array ${fieldtype})) 249 (func 250 ref.null $a 251 i32.const 0 252 array.get_s $a 253 drop 254 ) 255 (func 256 ref.null $a 257 i32.const 0 258 array.get_u $a 259 drop 260 ) 261 )`; 262 let noExtensionModule = `(module 263 (type $a (array ${fieldtype})) 264 (func 265 ref.null $a 266 i32.const 0 267 array.get $a 268 drop 269 ) 270 )`; 271 272 if (packed) { 273 wasmValidateText(extensionModule); 274 wasmFailValidateText(noExtensionModule, /must specify signedness/); 275 } else { 276 wasmFailValidateText(extensionModule, /must not specify signedness/); 277 wasmValidateText(noExtensionModule); 278 } 279 } 280 281 ////////////////////////////////////////////////////////////////////////////// 282 // 283 // array.new_fixed 284 /* 285 validation: 286 array-type imm-operand needs to be "in range" 287 array-type imm-operand must refer to an array type 288 operands (on stack) must all match ("be compatible with") the array elem 289 type 290 number of operands (on stack) must not be less than the num-of-elems 291 imm-operand 292 zero elements doesn't fail compilation 293 reftypes elements doesn't fail compilation 294 trying to create a 1-billion-element array fails gracefully 295 run: 296 resulting 4-element array is as expected 297 resulting zero-element array is as expected 298 resulting 30-element array is as expected 299 */ 300 301 // validation: array-type imm-operand needs to be "in range" 302 assertErrorMessage(() => wasmEvalText(`(module 303 (type $a (array i8)) 304 (func (result eqref) 305 i32.const 66 306 i32.const 77 307 array.new_fixed 2 2 ;; type index 2 is the first invalid one 308 ) 309 ) 310 `), WebAssembly.CompileError, /type index out of range/); 311 312 // validation: array-type imm-operand must refer to an array type 313 assertErrorMessage(() => wasmEvalText(`(module 314 (type $a (func (param f64) (result f64))) 315 (func (result eqref) 316 i32.const 66 317 i32.const 77 318 array.new_fixed $a 2 319 ) 320 ) 321 `), WebAssembly.CompileError, /not an array type/); 322 323 // validation: operands (on stack) must all match ("be compatible with") 324 // the array elem type 325 assertErrorMessage(() => wasmEvalText(`(module 326 (type $a (array i32)) 327 (func (result eqref) 328 f32.const 66.6 329 f64.const 77.7 330 array.new_fixed $a 2 331 ) 332 ) 333 `), WebAssembly.CompileError, /expression has type f64 but expected i32/); 334 335 // validation: number of operands (on stack) must not be less than the 336 // num-of-elems imm-operand 337 assertNoWarning(() => wasmEvalText(`(module 338 (type $a (array f32)) 339 (func 340 f64.const 66.6 ;; we won't put this in the array 341 f32.const 77.7 342 f32.const 88.8 343 array.new_fixed $a 2 ;; use up 88.8 and 77.7 and replace with array 344 drop ;; dump the array 345 f64.const 99.9 346 f64.mul ;; check the 66.6 value is still on the stack 347 drop ;; now should be empty 348 ) 349 ) 350 `)); 351 // (more) 352 assertErrorMessage(() => wasmEvalText(`(module 353 (type $a (array i64)) 354 (func (param i64) (result eqref) 355 local.get 0 356 array.new_fixed $a 2 357 ) 358 ) 359 `), WebAssembly.CompileError, /popping value from empty stack/); 360 361 // validation: zero elements doesn't fail compilation 362 assertNoWarning(() => wasmEvalText(`(module 363 (type $a (array i32)) 364 (func (result eqref) 365 array.new_fixed $a 0 366 ) 367 ) 368 `)); 369 370 // validation: reftyped elements doesn't fail compilation 371 assertNoWarning(() => wasmEvalText(`(module 372 (type $a (array eqref)) 373 (func (param eqref) (result eqref) 374 local.get 0 375 array.new_fixed $a 1 376 ) 377 ) 378 `)); 379 380 // run: resulting 4-element array is as expected 381 { 382 let { newFixed } = wasmEvalText(`(module 383 (type $a (array i8)) 384 (func (export "newFixed") (result eqref) 385 i32.const 66 386 i32.const 77 387 i32.const 88 388 i32.const 99 389 array.new_fixed $a 4 390 ) 391 )`).exports; 392 let a = newFixed(); 393 assertEq(wasmGcArrayLength(a), 4); 394 assertEq(wasmGcReadField(a, 0), 66); 395 assertEq(wasmGcReadField(a, 1), 77); 396 assertEq(wasmGcReadField(a, 2), 88); 397 assertEq(wasmGcReadField(a, 3), 99); 398 } 399 400 // run: resulting zero-element array is as expected 401 { 402 let { newFixed } = wasmEvalText(`(module 403 (type $a (array i16)) 404 (func (export "newFixed") (result eqref) 405 array.new_fixed $a 0 406 ) 407 )`).exports; 408 let a = newFixed(); 409 assertEq(wasmGcArrayLength(a), 0); 410 } 411 412 // run: resulting 30-element array is as expected 413 { 414 let { newFixed } = wasmEvalText(`(module 415 (type $a (array i16)) 416 (func (export "newFixed") (result eqref) 417 i32.const 1 418 i32.const 2 419 i32.const 3 420 i32.const 4 421 i32.const 5 422 i32.const 6 423 i32.const 7 424 i32.const 8 425 i32.const 9 426 i32.const 10 427 i32.const 11 428 i32.const 12 429 i32.const 13 430 i32.const 14 431 i32.const 15 432 i32.const 16 433 i32.const 17 434 i32.const 18 435 i32.const 19 436 i32.const 20 437 i32.const 21 438 i32.const 22 439 i32.const 23 440 i32.const 24 441 i32.const 25 442 i32.const 26 443 i32.const 27 444 i32.const 28 445 i32.const 29 446 i32.const 30 447 array.new_fixed $a 30 448 ) 449 )`).exports; 450 let a = newFixed(); 451 assertEq(wasmGcArrayLength(a), 30); 452 for (i = 0; i < 30; i++) { 453 assertEq(wasmGcReadField(a, i), i + 1); 454 } 455 } 456 457 // run: resulting 10_000-element array is as expected 458 { 459 let initializers = ''; 460 for (let i = 0; i < 10_000; i++) { 461 initializers += `i32.const ${i + 1}\n`; 462 } 463 let { newFixed } = wasmEvalText(`(module 464 (type $a (array i16)) 465 (func (export "newFixed") (result eqref) 466 ${initializers} 467 array.new_fixed $a 10000 468 ) 469 )`).exports; 470 let a = newFixed(); 471 assertEq(wasmGcArrayLength(a), 10000); 472 for (i = 0; i < 10000; i++) { 473 assertEq(wasmGcReadField(a, i), i + 1); 474 } 475 } 476 477 ////////////////////////////////////////////////////////////////////////////// 478 // 479 // array.new_data 480 /* 481 validation: 482 array-type imm-operand needs to be "in range" 483 array-type imm-operand must refer to an array type 484 array-type imm-operand must refer to an array of numeric elements 485 segment index must be "in range" 486 run: 487 if segment is "already used" (active, or passive that has subsequently 488 been dropped), then only a zero length array can be created 489 range to copy would require OOB read on data segment 490 resulting array is as expected 491 */ 492 493 // validation: array-type imm-operand needs to be "in range" 494 assertErrorMessage(() => wasmEvalText(`(module 495 (type $a (array i8)) 496 (data $d "1337") 497 (func (export "newData") (result eqref) 498 (; offset=0 into data ;) i32.const 0 499 (; size=4 into data ;) i32.const 4 500 array.new_data 2 $d 501 ) 502 )`), WebAssembly.CompileError, /type index out of range/); 503 504 // validation: array-type imm-operand must refer to an array type 505 assertErrorMessage(() => wasmEvalText(`(module 506 (type $a (func (param f32) (result f32))) 507 (data $d "1337") 508 (func (export "newData") (result eqref) 509 (; offset=0 into data ;) i32.const 0 510 (; size=4 into data ;) i32.const 4 511 array.new_data $a $d 512 ) 513 )`), WebAssembly.CompileError, /not an array type/); 514 515 // validation: array-type imm-operand must refer to an array of numeric elements 516 assertErrorMessage(() => wasmEvalText(`(module 517 (type $a (array eqref)) 518 (data $d "1337") 519 (func (export "newData") (result eqref) 520 (; offset=0 into data ;) i32.const 0 521 (; size=4 into data ;) i32.const 4 522 array.new_data $a $d 523 ) 524 )`), WebAssembly.CompileError, 525 /element type must be i8\/i16\/i32\/i64\/f32\/f64\/v128/); 526 527 // validation: segment index must be "in range" 528 assertErrorMessage(() => wasmEvalText(`(module 529 (type $a (array i8)) 530 (data $d "1337") 531 (func (export "newData") (result eqref) 532 (; offset=0 into data ;) i32.const 0 533 (; size=4 into data ;) i32.const 4 534 array.new_data $a 1 ;; 1 is the lowest invalid dseg index 535 ) 536 )`), WebAssembly.CompileError, /segment index is out of range/); 537 538 // run: if segment is "already used" (active, or passive that has subsequently 539 // been dropped), then only a zero length array can be created #1 540 { 541 let { newData } = wasmEvalText(`(module 542 (memory 1) 543 (type $a (array i8)) 544 (data $d (offset (i32.const 0)) "1337") 545 (func (export "newData") (result eqref) 546 (; offset=0 into data ;) i32.const 0 547 (; size=4 into data ;) i32.const 4 548 array.new_data $a $d 549 ) 550 )`).exports; 551 assertErrorMessage(() => { 552 newData(); 553 },WebAssembly.RuntimeError, /index out of bounds/); 554 } 555 556 // run: if segment is "already used" (active, or passive that has subsequently 557 // been dropped), then only a zero length array can be created #2 558 { 559 let { newData } = wasmEvalText(`(module 560 (memory 1) 561 (type $a (array i8)) 562 (data $d (offset (i32.const 0)) "1337") 563 (func (export "newData") (result eqref) 564 (; offset=4 into data ;) i32.const 4 565 (; size=0 into data ;) i32.const 0 566 array.new_data $a $d 567 ) 568 )`).exports; 569 assertErrorMessage(() => { 570 newData(); 571 },WebAssembly.RuntimeError, /index out of bounds/); 572 } 573 574 // run: if segment is "already used" (active, or passive that has subsequently 575 // been dropped), then only a zero length array can be created #3 576 { 577 let { newData } = wasmEvalText(`(module 578 (memory 1) 579 (type $a (array i8)) 580 (data $d (offset (i32.const 0)) "1337") 581 (func (export "newData") (result eqref) 582 (; offset=0 into data ;) i32.const 0 583 (; size=0 into data ;) i32.const 0 584 array.new_data $a $d 585 ) 586 )`).exports; 587 let arr = newData(); 588 assertEq(wasmGcArrayLength(arr), 0); 589 } 590 591 // run: range to copy would require OOB read on data segment 592 { 593 let { newData } = wasmEvalText(`(module 594 (type $a (array i8)) 595 (data $d "1337") 596 (func (export "newData") (result eqref) 597 (; offset=0 into data ;) i32.const 1 598 (; size=4 into data ;) i32.const 4 599 array.new_data $a $d 600 ) 601 )`).exports; 602 assertErrorMessage(() => { 603 newData(); 604 },WebAssembly.RuntimeError, /index out of bounds/); 605 } 606 607 // run: zero-length copies are allowed 608 { 609 let { newData } = wasmEvalText(`(module 610 (type $a (array i8)) 611 (data $d "1337") 612 (func (export "newData") (result eqref) 613 (; offset=0 into data ;) i32.const 0 614 (; size=0 into data ;) i32.const 0 615 array.new_data $a $d 616 ) 617 )`).exports; 618 let arr = newData(); 619 assertEq(wasmGcArrayLength(arr), 0); 620 } 621 622 // run: a zero-length copy from the end is allowed 623 { 624 let { newData } = wasmEvalText(`(module 625 (type $a (array i8)) 626 (data $d "1337") 627 (func (export "newData") (result eqref) 628 (; offset=4 into data ;) i32.const 4 629 (; size=0 into data ;) i32.const 0 630 array.new_data $a $d 631 ) 632 )`).exports; 633 let arr = newData(); 634 assertEq(wasmGcArrayLength(arr), 0); 635 } 636 637 // run: even empty data segments are allowed 638 { 639 let { newData } = wasmEvalText(`(module 640 (type $a (array i8)) 641 (data $d "") 642 (func (export "newData") (result eqref) 643 (; offset=0 into data ;) i32.const 0 644 (; size=0 into data ;) i32.const 0 645 array.new_data $a $d 646 ) 647 )`).exports; 648 let arr = newData(); 649 assertEq(wasmGcArrayLength(arr), 0); 650 } 651 652 // run: resulting array is as expected 653 { 654 let { newData } = wasmEvalText(`(module 655 (type $a (array i8)) 656 (data $other "\\\\9") 657 (data $d "1337") 658 (func (export "newData") (result eqref) 659 (; offset=0 into data ;) i32.const 0 660 (; size=4 into data ;) i32.const 4 661 array.new_data $a $d 662 ) 663 )`).exports; 664 let arr = newData(); 665 assertEq(wasmGcArrayLength(arr), 4); 666 assertEq(wasmGcReadField(arr, 0), 48+1); 667 assertEq(wasmGcReadField(arr, 1), 48+3); 668 assertEq(wasmGcReadField(arr, 2), 48+3); 669 assertEq(wasmGcReadField(arr, 3), 48+7); 670 } 671 672 ////////////////////////////////////////////////////////////////////////////// 673 // 674 // array.new_elem 675 /* 676 validation: 677 array-type imm-operand needs to be "in range" 678 array-type imm-operand must refer to an array type 679 array-type imm-operand must refer to an array of ref typed elements 680 destination elem type must be a supertype of src elem type 681 segment index must be "in range" 682 run: 683 if segment is "already used" (active, or passive that has subsequently 684 been dropped), then only a zero length array can be created 685 range to copy would require OOB read on elem segment 686 resulting array is as expected 687 */ 688 689 // validation: array-type imm-operand needs to be "in range" 690 assertErrorMessage(() => wasmEvalText(`(module 691 (type $a (array funcref)) 692 (elem $e func $f1 $f2 $f3 $f4) 693 (func $f1 (export "f1")) 694 (func $f2 (export "f2")) 695 (func $f3 (export "f3")) 696 (func $f4 (export "f4")) 697 (func (export "newElem") (result eqref) 698 (; offset=0 into elem ;) i32.const 0 699 (; size=4 into elem ;) i32.const 4 700 array.new_elem 3 $e 701 ) 702 )`), WebAssembly.CompileError, /type index out of range/); 703 704 // validation: array-type imm-operand must refer to an array type 705 assertErrorMessage(() => wasmEvalText(`(module 706 (type $a (func (param i64) (result f64))) 707 (elem $e func $f1 $f2 $f3 $f4) 708 (func $f1 (export "f1")) 709 (func $f2 (export "f2")) 710 (func $f3 (export "f3")) 711 (func $f4 (export "f4")) 712 (func (export "newElem") (result eqref) 713 (; offset=0 into elem ;) i32.const 0 714 (; size=4 into elem ;) i32.const 4 715 array.new_elem $a $e 716 ) 717 )`), WebAssembly.CompileError, /not an array type/); 718 719 // validation: array-type imm-operand must refer to an array of ref typed 720 // elements 721 assertErrorMessage(() => wasmEvalText(`(module 722 (type $a (array f32)) 723 (elem $e func $f1 $f2 $f3 $f4) 724 (func $f1 (export "f1")) 725 (func $f2 (export "f2")) 726 (func $f3 (export "f3")) 727 (func $f4 (export "f4")) 728 (func (export "newElem") (result eqref) 729 (; offset=0 into elem ;) i32.const 0 730 (; size=4 into elem ;) i32.const 4 731 array.new_elem $a $e 732 ) 733 )`), WebAssembly.CompileError, /element type is not a reftype/); 734 735 // validation: destination elem type must be a supertype of src elem type 736 assertErrorMessage(() => wasmEvalText(`(module 737 (type $a (array eqref)) 738 (elem $e func $f1) 739 (func $f1 (export "f1")) 740 ;; The implied copy here is from elem-seg-of-funcrefs to 741 ;; array-of-eqrefs, which must fail, because funcref isn't 742 ;; a subtype of eqref. 743 (func (export "newElem") (result eqref) 744 (; offset=0 into elem ;) i32.const 0 745 (; size=0 into elem ;) i32.const 0 746 array.new_elem $a $e 747 ) 748 )`), WebAssembly.CompileError, /incompatible element types/); 749 750 // validation: segment index must be "in range" 751 assertErrorMessage(() => wasmEvalText(`(module 752 (type $a (array funcref)) 753 (elem $e func $f1 $f2 $f3 $f4) 754 (func $f1 (export "f1")) 755 (func $f2 (export "f2")) 756 (func $f3 (export "f3")) 757 (func $f4 (export "f4")) 758 (func (export "newElem") (result eqref) 759 (; offset=0 into elem ;) i32.const 0 760 (; size=4 into elem ;) i32.const 4 761 array.new_elem $a 1 ;; 1 is the lowest invalid eseg index 762 ) 763 )`), WebAssembly.CompileError, /segment index is out of range/); 764 765 // run: if segment is "already used" (active, or passive that has subsequently 766 // been dropped), then only a zero length array can be created #1 767 { 768 let { newElem } = wasmEvalText(`(module 769 (table 4 funcref) 770 (type $a (array funcref)) 771 (elem $e (offset (i32.const 0)) func $f1 $f2 $f3 $f4) 772 (func $f1 (export "f1")) 773 (func $f2 (export "f2")) 774 (func $f3 (export "f3")) 775 (func $f4 (export "f4")) 776 (func (export "newElem") (result eqref) 777 (; offset=0 into elem ;) i32.const 0 778 (; size=4 into elem ;) i32.const 4 779 array.new_elem $a $e 780 ) 781 )`).exports; 782 assertErrorMessage(() => { 783 newElem(); 784 }, WebAssembly.RuntimeError, /index out of bounds/); 785 } 786 787 // run: if segment is "already used" (active, or passive that has subsequently 788 // been dropped), then only a zero length array can be created #2 789 { 790 let { newElem } = wasmEvalText(`(module 791 (table 4 funcref) 792 (type $a (array funcref)) 793 (elem $e (offset (i32.const 0)) func $f1 $f2 $f3 $f4) 794 (func $f1 (export "f1")) 795 (func $f2 (export "f2")) 796 (func $f3 (export "f3")) 797 (func $f4 (export "f4")) 798 (func (export "newElem") (result eqref) 799 (; offset=4 into elem ;) i32.const 4 800 (; size=0 into elem ;) i32.const 0 801 array.new_elem $a $e 802 ) 803 )`).exports; 804 assertErrorMessage(() => { 805 newElem(); 806 }, WebAssembly.RuntimeError, /index out of bounds/); 807 } 808 809 // run: if segment is "already used" (active, or passive that has subsequently 810 // been dropped), then only a zero length array can be created #3 811 { 812 let { newElem } = wasmEvalText(`(module 813 (table 4 funcref) 814 (type $a (array funcref)) 815 (elem $e (offset (i32.const 0)) func $f1 $f2 $f3 $f4) 816 (func $f1 (export "f1")) 817 (func $f2 (export "f2")) 818 (func $f3 (export "f3")) 819 (func $f4 (export "f4")) 820 (func (export "newElem") (result eqref) 821 (; offset=0 into elem ;) i32.const 0 822 (; size=0 into elem ;) i32.const 0 823 array.new_elem $a $e 824 ) 825 )`).exports; 826 let arr = newElem(); 827 assertEq(wasmGcArrayLength(arr), 0); 828 } 829 830 // run: range to copy would require OOB read on elem segment 831 { 832 let { newElem } = wasmEvalText(`(module 833 (type $a (array funcref)) 834 (elem $e func $f1 $f2 $f3 $f4) 835 (func $f1 (export "f1")) 836 (func $f2 (export "f2")) 837 (func $f3 (export "f3")) 838 (func $f4 (export "f4")) 839 (func (export "newElem") (result eqref) 840 (; offset=0 into elem ;) i32.const 1 841 (; size=4 into elem ;) i32.const 4 842 array.new_elem $a $e 843 ) 844 )`).exports; 845 assertErrorMessage(() => { 846 newElem(); 847 },WebAssembly.RuntimeError, /index out of bounds/); 848 } 849 850 // run: zero-length copies are allowed 851 { 852 let { newElem, f1, f2, f3, f4 } = wasmEvalText(`(module 853 (type $a (array funcref)) 854 (elem $e func $f1 $f2 $f3 $f4) 855 (func $f1 (export "f1")) 856 (func $f2 (export "f2")) 857 (func $f3 (export "f3")) 858 (func $f4 (export "f4")) 859 (func (export "newElem") (result eqref) 860 (; offset=0 into elem ;) i32.const 0 861 (; size=0 into elem ;) i32.const 0 862 array.new_elem $a $e 863 ) 864 )`).exports; 865 let arr = newElem(); 866 assertEq(wasmGcArrayLength(arr), 0); 867 } 868 869 // run: a zero-length copy from the end is allowed 870 { 871 let { newElem, f1, f2, f3, f4 } = wasmEvalText(`(module 872 (type $a (array funcref)) 873 (elem $e func $f1 $f2 $f3 $f4) 874 (func $f1 (export "f1")) 875 (func $f2 (export "f2")) 876 (func $f3 (export "f3")) 877 (func $f4 (export "f4")) 878 (func (export "newElem") (result eqref) 879 (; offset=4 into elem ;) i32.const 4 880 (; size=0 into elem ;) i32.const 0 881 array.new_elem $a $e 882 ) 883 )`).exports; 884 let arr = newElem(); 885 assertEq(wasmGcArrayLength(arr), 0); 886 } 887 888 // run: even empty elem segments are allowed 889 { 890 let { newElem, f1, f2, f3, f4 } = wasmEvalText(`(module 891 (type $a (array funcref)) 892 (elem $e func) 893 (func (export "newElem") (result eqref) 894 (; offset=0 into elem ;) i32.const 0 895 (; size=0 into elem ;) i32.const 0 896 array.new_elem $a $e 897 ) 898 )`).exports; 899 let arr = newElem(); 900 assertEq(wasmGcArrayLength(arr), 0); 901 } 902 903 // run: resulting array is as expected 904 { 905 let { newElem, f1, f2, f3, f4 } = wasmEvalText(`(module 906 (type $a (array funcref)) 907 (elem $e func $f1 $f2 $f3 $f4) 908 (func $f1 (export "f1")) 909 (func $f2 (export "f2")) 910 (func $f3 (export "f3")) 911 (func $f4 (export "f4")) 912 (func (export "newElem") (result eqref) 913 (; offset=0 into elem ;) i32.const 0 914 (; size=4 into elem ;) i32.const 4 915 array.new_elem $a $e 916 ) 917 )`).exports; 918 let arr = newElem(); 919 assertEq(wasmGcArrayLength(arr), 4); 920 assertEq(wasmGcReadField(arr, 0), f1); 921 assertEq(wasmGcReadField(arr, 1), f2); 922 assertEq(wasmGcReadField(arr, 2), f3); 923 assertEq(wasmGcReadField(arr, 3), f4); 924 } 925 926 ////////////////////////////////////////////////////////////////////////////// 927 // 928 // array.init_data 929 /* 930 validation: 931 array-type imm-operand needs to be "in range" 932 array-type imm-operand must refer to an array type 933 array-type imm-operand must refer to an array of numeric elements 934 array-type imm-operand must be mutable 935 segment index must be "in range" 936 run: 937 array must not be null 938 if segment is "already used" (active, or passive that has subsequently 939 been dropped), then only a zero length array can be created 940 range to copy would require OOB read on data segment 941 range to copy would require OOB write on array 942 resulting array is as expected 943 */ 944 945 // validation: array-type imm-operand needs to be "in range" 946 assertErrorMessage(() => wasmEvalText(`(module 947 (type $a (array i8)) 948 (data $d "1337") 949 (func (export "initData") 950 (; array to init ;) (array.new_default $a (i32.const 4)) 951 (; offset=0 into array ;) i32.const 0 952 (; offset=0 into data ;) i32.const 0 953 (; size=4 elements ;) i32.const 4 954 array.init_data 2 $d 955 ) 956 )`), WebAssembly.CompileError, /type index out of range/); 957 958 // validation: array-type imm-operand must refer to an array type 959 assertErrorMessage(() => wasmEvalText(`(module 960 (type $a (func (param f32) (result f32))) 961 (data $d "1337") 962 (func (export "initData") 963 (; array to init ;) (array.new_default $a (i32.const 4)) 964 (; offset=0 into array ;) i32.const 0 965 (; offset=0 into data ;) i32.const 0 966 (; size=4 elements ;) i32.const 4 967 array.init_data $a $d 968 ) 969 )`), WebAssembly.CompileError, /not an array type/); 970 971 // validation: array-type imm-operand must refer to an array of numeric elements 972 assertErrorMessage(() => wasmEvalText(`(module 973 (type $a (array eqref)) 974 (data $d "1337") 975 (func (export "initData") 976 (; array to init ;) (array.new_default $a (i32.const 4)) 977 (; offset=0 into array ;) i32.const 0 978 (; offset=0 into data ;) i32.const 0 979 (; size=4 elements ;) i32.const 4 980 array.init_data $a $d 981 ) 982 )`), WebAssembly.CompileError, /element type must be i8\/i16\/i32\/i64\/f32\/f64\/v128/); 983 984 // validation: array-type imm-operand must be mutable 985 assertErrorMessage(() => wasmEvalText(`(module 986 (type $a (array i8)) 987 (data $d "1337") 988 (func (export "initData") 989 (; array to init ;) (array.new_default $a (i32.const 4)) 990 (; offset=0 into array ;) i32.const 0 991 (; offset=0 into data ;) i32.const 0 992 (; size=4 elements ;) i32.const 4 993 array.init_data $a $d 994 ) 995 )`), WebAssembly.CompileError, 996 /destination array is not mutable/); 997 998 // validation: segment index must be "in range" 999 assertErrorMessage(() => wasmEvalText(`(module 1000 (type $a (array (mut i8))) 1001 (data $d "1337") 1002 (func (export "initData") 1003 (; array to init ;) (array.new_default $a (i32.const 4)) 1004 (; offset=0 into array ;) i32.const 0 1005 (; offset=0 into data ;) i32.const 0 1006 (; size=4 elements ;) i32.const 4 1007 array.init_data $a 1 ;; 1 is the lowest invalid dseg index 1008 ) 1009 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1010 )`), WebAssembly.CompileError, /segment index is out of range/); 1011 1012 // run: array must not be null 1013 { 1014 let { initData } = wasmEvalText(`(module 1015 (type $a (array (mut i8))) 1016 (data $d "1337") 1017 (func (export "initData") 1018 (; array to init ;) (ref.null $a) 1019 (; offset=0 into array ;) i32.const 0 1020 (; offset=0 into data ;) i32.const 0 1021 (; size=4 elements ;) i32.const 4 1022 array.init_data $a $d 1023 ) 1024 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1025 )`).exports; 1026 assertErrorMessage(() => { 1027 initData(); 1028 }, WebAssembly.RuntimeError, /dereferencing null pointer/); 1029 } 1030 1031 // run: if segment is "already used" (active, or passive that has subsequently 1032 // been dropped), then only a zero length init can be performed #1 1033 { 1034 let { initData } = wasmEvalText(`(module 1035 (memory 1) 1036 (type $a (array (mut i8))) 1037 (data $d (offset (i32.const 0)) "1337") 1038 (func (export "initData") 1039 (; array to init ;) (array.new_default $a (i32.const 4)) 1040 (; offset=0 into array ;) i32.const 0 1041 (; offset=0 into data ;) i32.const 0 1042 (; size=4 elements ;) i32.const 4 1043 array.init_data $a $d 1044 ) 1045 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1046 )`).exports; 1047 assertErrorMessage(() => { 1048 initData(); 1049 }, WebAssembly.RuntimeError, /index out of bounds/); 1050 } 1051 1052 // run: if segment is "already used" (active, or passive that has subsequently 1053 // been dropped), then only a zero length init can be performed #2 1054 { 1055 let { initData } = wasmEvalText(`(module 1056 (memory 1) 1057 (type $a (array (mut i8))) 1058 (data $d (offset (i32.const 0)) "1337") 1059 (func (export "initData") 1060 (; array to init ;) (array.new_default $a (i32.const 4)) 1061 (; offset=0 into array ;) i32.const 0 1062 (; offset=4 into data ;) i32.const 4 1063 (; size=0 elements ;) i32.const 0 1064 array.init_data $a $d 1065 ) 1066 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1067 )`).exports; 1068 assertErrorMessage(() => { 1069 initData(); 1070 },WebAssembly.RuntimeError, /index out of bounds/); 1071 } 1072 1073 // run: if segment is "already used" (active, or passive that has subsequently 1074 // been dropped), then only a zero length init can be performed #3 1075 { 1076 let { initData } = wasmEvalText(`(module 1077 (memory 1) 1078 (type $a (array (mut i8))) 1079 (data $d (offset (i32.const 0)) "1337") 1080 (func (export "initData") 1081 (; array to init ;) (array.new_default $a (i32.const 4)) 1082 (; offset=0 into array ;) i32.const 0 1083 (; offset=0 into data ;) i32.const 0 1084 (; size=0 elements ;) i32.const 0 1085 array.init_data $a $d 1086 ) 1087 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1088 )`).exports; 1089 initData(); 1090 } 1091 1092 // run: if segment is "already used" (active, or passive that has subsequently 1093 // been dropped), then only a zero length init can be performed #4 1094 { 1095 let { initData } = wasmEvalText(`(module 1096 (type $a (array (mut i8))) 1097 (data $d "1337") 1098 (func (export "initData") 1099 data.drop $d 1100 1101 (; array to init ;) (array.new_default $a (i32.const 4)) 1102 (; offset=0 into array ;) i32.const 0 1103 (; offset=0 into data ;) i32.const 0 1104 (; size=4 elements ;) i32.const 4 1105 array.init_data $a $d 1106 ) 1107 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1108 )`).exports; 1109 assertErrorMessage(() => { 1110 initData(); 1111 },WebAssembly.RuntimeError, /index out of bounds/); 1112 } 1113 1114 // run: if segment is "already used" (active, or passive that has subsequently 1115 // been dropped), then only a zero length init can be performed #5 1116 { 1117 let { initData } = wasmEvalText(`(module 1118 (type $a (array (mut i8))) 1119 (data $d "1337") 1120 (func (export "initData") 1121 data.drop $d 1122 1123 (; array to init ;) (array.new_default $a (i32.const 4)) 1124 (; offset=0 into array ;) i32.const 0 1125 (; offset=0 into data ;) i32.const 0 1126 (; size=0 elements ;) i32.const 0 1127 array.init_data $a $d 1128 ) 1129 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1130 )`).exports; 1131 initData(); 1132 } 1133 1134 // run: if segment is "already used" (active, or passive that has subsequently 1135 // been dropped), then only a zero length init can be performed #6 1136 { 1137 let { initData } = wasmEvalText(`(module 1138 (memory 1) 1139 (type $a (array (mut i8))) 1140 (data $d "1337") 1141 (func (export "initData") 1142 data.drop $d 1143 1144 (; array to init ;) (array.new_default $a (i32.const 4)) 1145 (; offset=4 into array ;) i32.const 4 1146 (; offset=0 into data ;) i32.const 0 1147 (; size=0 elements ;) i32.const 0 1148 array.init_data $a $d 1149 ) 1150 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1151 )`).exports; 1152 initData(); 1153 } 1154 1155 // run: range to copy would require OOB read on data segment #1 1156 { 1157 let { initData } = wasmEvalText(`(module 1158 (type $a (array (mut i8))) 1159 (data $d "1337") 1160 (func (export "initData") 1161 (; array to init ;) (array.new_default $a (i32.const 4)) 1162 (; offset=0 into array ;) i32.const 0 1163 (; offset=1 into data ;) i32.const 1 1164 (; size=4 elements ;) i32.const 4 1165 array.init_data $a $d 1166 ) 1167 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1168 )`).exports; 1169 assertErrorMessage(() => { 1170 initData(); 1171 },WebAssembly.RuntimeError, /index out of bounds/); 1172 } 1173 1174 // run: range to copy would require OOB read on data segment #2 1175 { 1176 let { initData } = wasmEvalText(`(module 1177 (type $a (array (mut i16))) 1178 (data $d "1337") 1179 (func (export "initData") 1180 (; array to init ;) (array.new_default $a (i32.const 4)) 1181 (; offset=0 into array ;) i32.const 0 1182 (; offset=1 into data ;) i32.const 1 1183 (; size=2 elements ;) i32.const 2 ;; still 4 bytes 1184 array.init_data $a $d 1185 ) 1186 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1187 )`).exports; 1188 assertErrorMessage(() => { 1189 initData(); 1190 },WebAssembly.RuntimeError, /index out of bounds/); 1191 } 1192 1193 // run: range to copy would require OOB write on array #1 1194 { 1195 let { initData } = wasmEvalText(`(module 1196 (type $a (array (mut i8))) 1197 (data $d "1337") 1198 (func (export "initData") 1199 (; array to init ;) (array.new_default $a (i32.const 4)) 1200 (; offset=1 into array ;) i32.const 1 1201 (; offset=0 into data ;) i32.const 0 1202 (; size=4 elements ;) i32.const 4 1203 array.init_data $a $d 1204 ) 1205 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1206 )`).exports; 1207 assertErrorMessage(() => { 1208 initData(); 1209 },WebAssembly.RuntimeError, /index out of bounds/); 1210 } 1211 1212 // run: range to copy would require OOB write on array #2 1213 { 1214 let { initData } = wasmEvalText(`(module 1215 (type $a (array (mut i16))) 1216 (data $d "1337") 1217 (func (export "initData") 1218 (; array to init ;) (array.new_default $a (i32.const 2)) 1219 (; offset=1 into array ;) i32.const 1 1220 (; offset=0 into data ;) i32.const 0 1221 (; size=4 elements ;) i32.const 2 1222 array.init_data $a $d 1223 ) 1224 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1225 )`).exports; 1226 assertErrorMessage(() => { 1227 initData(); 1228 },WebAssembly.RuntimeError, /index out of bounds/); 1229 } 1230 1231 // run: zeroes everywhere 1232 { 1233 let { initData } = wasmEvalText(`(module 1234 (type $a (array (mut i8))) 1235 (data $d "") 1236 (func (export "initData") (result eqref) 1237 (local $arr (ref $a)) 1238 (local.set $arr (array.new_default $a (i32.const 0))) 1239 1240 (; array to init ;) local.get $arr 1241 (; offset=0 into array ;) i32.const 0 1242 (; offset=0 into data ;) i32.const 0 1243 (; size=0 elements ;) i32.const 0 1244 array.init_data $a $d 1245 1246 local.get $arr 1247 ) 1248 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1249 )`).exports; 1250 let arr = initData(); 1251 assertEq(wasmGcArrayLength(arr), 0); 1252 } 1253 1254 // run: resulting array is as expected 1255 { 1256 let { initData } = wasmEvalText(`(module 1257 (type $a (array (mut i8))) 1258 (data $other "\\\\9") 1259 (data $d "xX1337Xx") 1260 (func (export "initData") (result eqref) 1261 (local $arr (ref $a)) 1262 (local.set $arr (array.new_default $a (i32.const 6))) 1263 1264 (; array to init ;) local.get $arr 1265 (; offset=1 into array ;) i32.const 1 1266 (; offset=2 into data ;) i32.const 2 1267 (; size=4 elements ;) i32.const 4 1268 array.init_data $a $d 1269 1270 local.get $arr 1271 ) 1272 (func data.drop 0) ;; force write of data count section, see https://github.com/bytecodealliance/wasm-tools/pull/1194 1273 )`).exports; 1274 let arr = initData(); 1275 assertEq(wasmGcArrayLength(arr), 6); 1276 assertEq(wasmGcReadField(arr, 0), 0); 1277 assertEq(wasmGcReadField(arr, 1), 48+1); 1278 assertEq(wasmGcReadField(arr, 2), 48+3); 1279 assertEq(wasmGcReadField(arr, 3), 48+3); 1280 assertEq(wasmGcReadField(arr, 4), 48+7); 1281 assertEq(wasmGcReadField(arr, 5), 0); 1282 } 1283 1284 ////////////////////////////////////////////////////////////////////////////// 1285 // 1286 // array.init_elem 1287 /* 1288 validation: 1289 array-type imm-operand needs to be "in range" 1290 array-type imm-operand must refer to an array type 1291 array-type imm-operand must refer to an array of ref typed elements 1292 array-type imm-operand must be mutable 1293 destination elem type must be a supertype of src elem type 1294 segment index must be "in range" 1295 run: 1296 array must not be null 1297 if segment is "already used" (active, or passive that has subsequently 1298 been dropped), then only a zero length array can be created 1299 range to copy would require OOB read on element segment 1300 range to copy would require OOB write on array 1301 resulting array is as expected 1302 */ 1303 1304 // validation: array-type imm-operand needs to be "in range" 1305 assertErrorMessage(() => wasmEvalText(`(module 1306 (type $a (array (mut funcref))) 1307 (elem $e func $f1 $f2 $f3 $f4) 1308 (func $f1 (export "f1")) 1309 (func $f2 (export "f2")) 1310 (func $f3 (export "f3")) 1311 (func $f4 (export "f4")) 1312 (func (export "initElem") 1313 (; array to init ;) (array.new_default $a (i32.const 4)) 1314 (; offset=0 into array ;) i32.const 0 1315 (; offset=0 into elem ;) i32.const 0 1316 (; size=4 into elem ;) i32.const 4 1317 array.init_elem 4 $e 1318 ) 1319 )`), WebAssembly.CompileError, /type index out of range/); 1320 1321 // validation: array-type imm-operand must refer to an array type 1322 assertErrorMessage(() => wasmEvalText(`(module 1323 (type $a (func (param i64) (result f64))) 1324 (elem $e func $f1 $f2 $f3 $f4) 1325 (func $f1 (export "f1")) 1326 (func $f2 (export "f2")) 1327 (func $f3 (export "f3")) 1328 (func $f4 (export "f4")) 1329 (func (export "initElem") 1330 (; array to init ;) (array.new_default $a (i32.const 4)) 1331 (; offset=0 into array ;) i32.const 0 1332 (; offset=0 into elem ;) i32.const 0 1333 (; size=4 into elem ;) i32.const 4 1334 array.init_elem $a $e 1335 ) 1336 )`), WebAssembly.CompileError, /not an array type/); 1337 1338 // validation: array-type imm-operand must refer to an array of ref typed 1339 // elements 1340 assertErrorMessage(() => wasmEvalText(`(module 1341 (type $a (array (mut f32))) 1342 (elem $e func $f1 $f2 $f3 $f4) 1343 (func $f1 (export "f1")) 1344 (func $f2 (export "f2")) 1345 (func $f3 (export "f3")) 1346 (func $f4 (export "f4")) 1347 (func (export "initElem") 1348 (; array to init ;) (array.new_default $a (i32.const 4)) 1349 (; offset=0 into array ;) i32.const 0 1350 (; offset=0 into elem ;) i32.const 0 1351 (; size=4 into elem ;) i32.const 4 1352 array.init_elem $a $e 1353 ) 1354 )`), WebAssembly.CompileError, /element type is not a reftype/); 1355 1356 // validation: array-type imm-operand must be mutable 1357 assertErrorMessage(() => wasmEvalText(`(module 1358 (type $a (array funcref)) 1359 (elem $e func $f1 $f2 $f3 $f4) 1360 (func $f1 (export "f1")) 1361 (func $f2 (export "f2")) 1362 (func $f3 (export "f3")) 1363 (func $f4 (export "f4")) 1364 (func (export "initElem") 1365 (; array to init ;) (array.new_default $a (i32.const 4)) 1366 (; offset=0 into array ;) i32.const 0 1367 (; offset=0 into elem ;) i32.const 0 1368 (; size=4 into elem ;) i32.const 4 1369 array.init_elem $a $e 1370 ) 1371 )`), WebAssembly.CompileError, /destination array is not mutable/); 1372 1373 // validation: destination elem type must be a supertype of src elem type 1374 assertErrorMessage(() => wasmEvalText(`(module 1375 (type $a (array (mut eqref))) 1376 (elem $e func $f1) 1377 (func $f1 (export "f1")) 1378 ;; The copy here is from elem-seg-of-funcrefs to 1379 ;; array-of-eqrefs, which must fail, because funcref isn't 1380 ;; a subtype of eqref. 1381 (func (export "initElem") 1382 (; array to init ;) (array.new_default $a (i32.const 4)) 1383 (; offset=0 into array ;) i32.const 0 1384 (; offset=0 into elem ;) i32.const 0 1385 (; size=4 into elem ;) i32.const 4 1386 array.init_elem $a $e 1387 ) 1388 )`), WebAssembly.CompileError, /incompatible element types/); 1389 1390 // validation: segment index must be "in range" 1391 assertErrorMessage(() => wasmEvalText(`(module 1392 (type $a (array (mut funcref))) 1393 (elem $e func $f1 $f2 $f3 $f4) 1394 (func $f1 (export "f1")) 1395 (func $f2 (export "f2")) 1396 (func $f3 (export "f3")) 1397 (func $f4 (export "f4")) 1398 (func (export "initElem") 1399 (; array to init ;) (array.new_default $a (i32.const 4)) 1400 (; offset=0 into array ;) i32.const 0 1401 (; offset=0 into elem ;) i32.const 0 1402 (; size=4 into elem ;) i32.const 4 1403 array.init_elem $a 1 ;; 1 is the lowest invalid eseg index 1404 ) 1405 )`), WebAssembly.CompileError, /segment index is out of range/); 1406 1407 // run: array must not be null 1408 { 1409 let { initElem } = wasmEvalText(`(module 1410 (type $a (array (mut funcref))) 1411 (elem $e func $f1 $f2 $f3 $f4) 1412 (func $f1 (export "f1")) 1413 (func $f2 (export "f2")) 1414 (func $f3 (export "f3")) 1415 (func $f4 (export "f4")) 1416 (func (export "initElem") 1417 (; array to init ;) (ref.null $a) 1418 (; offset=0 into array ;) i32.const 0 1419 (; offset=0 into elem ;) i32.const 0 1420 (; size=4 into elem ;) i32.const 4 1421 array.init_elem $a $e 1422 ) 1423 )`).exports; 1424 assertErrorMessage(() => { 1425 initElem(); 1426 }, WebAssembly.RuntimeError, /dereferencing null pointer/); 1427 } 1428 1429 // run: if segment is "already used" (active, or passive that has subsequently 1430 // been dropped), then only a zero length array can be created #1 1431 { 1432 let { initElem } = wasmEvalText(`(module 1433 (table 4 funcref) 1434 (type $a (array (mut funcref))) 1435 (elem $e (offset (i32.const 0)) func $f1 $f2 $f3 $f4) 1436 (func $f1 (export "f1")) 1437 (func $f2 (export "f2")) 1438 (func $f3 (export "f3")) 1439 (func $f4 (export "f4")) 1440 (func (export "initElem") 1441 (; array to init ;) (array.new_default $a (i32.const 4)) 1442 (; offset=0 into array ;) i32.const 0 1443 (; offset=0 into elem ;) i32.const 0 1444 (; size=4 into elem ;) i32.const 4 1445 array.init_elem $a $e 1446 ) 1447 )`).exports; 1448 assertErrorMessage(() => { 1449 initElem(); 1450 }, WebAssembly.RuntimeError, /index out of bounds/); 1451 } 1452 1453 // run: if segment is "already used" (active, or passive that has subsequently 1454 // been dropped), then only a zero length array can be created #2 1455 { 1456 let { initElem } = wasmEvalText(`(module 1457 (table 4 funcref) 1458 (type $a (array (mut funcref))) 1459 (elem $e (offset (i32.const 0)) func $f1 $f2 $f3 $f4) 1460 (func $f1 (export "f1")) 1461 (func $f2 (export "f2")) 1462 (func $f3 (export "f3")) 1463 (func $f4 (export "f4")) 1464 (func (export "initElem") 1465 (; array to init ;) (array.new_default $a (i32.const 4)) 1466 (; offset=0 into array ;) i32.const 0 1467 (; offset=4 into elem ;) i32.const 4 1468 (; size=0 into elem ;) i32.const 0 1469 array.init_elem $a $e 1470 ) 1471 )`).exports; 1472 assertErrorMessage(() => { 1473 initElem(); 1474 }, WebAssembly.RuntimeError, /index out of bounds/); 1475 } 1476 1477 // run: if segment is "already used" (active, or passive that has subsequently 1478 // been dropped), then only a zero length array can be created #3 1479 { 1480 let { initElem } = wasmEvalText(`(module 1481 (table 4 funcref) 1482 (type $a (array (mut funcref))) 1483 (elem $e (offset (i32.const 0)) func $f1 $f2 $f3 $f4) 1484 (func $f1 (export "f1")) 1485 (func $f2 (export "f2")) 1486 (func $f3 (export "f3")) 1487 (func $f4 (export "f4")) 1488 (func (export "initElem") 1489 (; array to init ;) (array.new_default $a (i32.const 4)) 1490 (; offset=0 into array ;) i32.const 0 1491 (; offset=0 into elem ;) i32.const 0 1492 (; size=0 into elem ;) i32.const 0 1493 array.init_elem $a $e 1494 ) 1495 )`).exports; 1496 initElem(); 1497 } 1498 1499 // run: if segment is "already used" (active, or passive that has subsequently 1500 // been dropped), then only a zero length init can be performed #4 1501 { 1502 let { initElem } = wasmEvalText(`(module 1503 (type $a (array (mut funcref))) 1504 (elem $e func $f1 $f2 $f3 $f4) 1505 (func $f1 (export "f1")) 1506 (func $f2 (export "f2")) 1507 (func $f3 (export "f3")) 1508 (func $f4 (export "f4")) 1509 (func (export "initElem") 1510 elem.drop $e 1511 1512 (; array to init ;) (array.new_default $a (i32.const 4)) 1513 (; offset=0 into array ;) i32.const 0 1514 (; offset=0 into elem ;) i32.const 0 1515 (; size=4 into elem ;) i32.const 4 1516 array.init_elem $a $e 1517 ) 1518 )`).exports; 1519 assertErrorMessage(() => { 1520 initElem(); 1521 },WebAssembly.RuntimeError, /index out of bounds/); 1522 } 1523 1524 // run: if segment is "already used" (active, or passive that has subsequently 1525 // been dropped), then only a zero length init can be performed #5 1526 { 1527 let { initElem } = wasmEvalText(`(module 1528 (type $a (array (mut funcref))) 1529 (elem $e func $f1 $f2 $f3 $f4) 1530 (func $f1 (export "f1")) 1531 (func $f2 (export "f2")) 1532 (func $f3 (export "f3")) 1533 (func $f4 (export "f4")) 1534 (func (export "initElem") 1535 elem.drop $e 1536 1537 (; array to init ;) (array.new_default $a (i32.const 4)) 1538 (; offset=0 into array ;) i32.const 0 1539 (; offset=0 into elem ;) i32.const 0 1540 (; size=4 into elem ;) i32.const 0 1541 array.init_elem $a $e 1542 ) 1543 )`).exports; 1544 initElem(); 1545 } 1546 1547 // run: if segment is "already used" (active, or passive that has subsequently 1548 // been dropped), then only a zero length init can be performed #6 1549 { 1550 let { initElem } = wasmEvalText(`(module 1551 (type $a (array (mut funcref))) 1552 (elem $e func $f1 $f2 $f3 $f4) 1553 (func $f1 (export "f1")) 1554 (func $f2 (export "f2")) 1555 (func $f3 (export "f3")) 1556 (func $f4 (export "f4")) 1557 (func (export "initElem") 1558 elem.drop $e 1559 1560 (; array to init ;) (array.new_default $a (i32.const 4)) 1561 (; offset=0 into array ;) i32.const 4 1562 (; offset=0 into elem ;) i32.const 0 1563 (; size=4 into elem ;) i32.const 0 1564 array.init_elem $a $e 1565 ) 1566 )`).exports; 1567 initElem(); 1568 } 1569 1570 // run: range to copy would require OOB read on elem segment 1571 { 1572 let { initElem } = wasmEvalText(`(module 1573 (type $a (array (mut funcref))) 1574 (elem $e func $f1 $f2 $f3 $f4) 1575 (func $f1 (export "f1")) 1576 (func $f2 (export "f2")) 1577 (func $f3 (export "f3")) 1578 (func $f4 (export "f4")) 1579 (func (export "initElem") 1580 (; array to init ;) (array.new_default $a (i32.const 4)) 1581 (; offset=0 into array ;) i32.const 0 1582 (; offset=0 into elem ;) i32.const 1 1583 (; size=4 into elem ;) i32.const 4 1584 array.init_elem $a $e 1585 ) 1586 )`).exports; 1587 assertErrorMessage(() => { 1588 initElem(); 1589 },WebAssembly.RuntimeError, /index out of bounds/); 1590 } 1591 1592 // run: range to copy would require OOB write on array 1593 { 1594 let { initElem } = wasmEvalText(`(module 1595 (type $a (array (mut funcref))) 1596 (elem $e func $f1 $f2 $f3 $f4) 1597 (func $f1 (export "f1")) 1598 (func $f2 (export "f2")) 1599 (func $f3 (export "f3")) 1600 (func $f4 (export "f4")) 1601 (func (export "initElem") 1602 (; array to init ;) (array.new_default $a (i32.const 4)) 1603 (; offset=0 into array ;) i32.const 1 1604 (; offset=0 into elem ;) i32.const 0 1605 (; size=4 into elem ;) i32.const 4 1606 array.init_elem $a $e 1607 ) 1608 )`).exports; 1609 assertErrorMessage(() => { 1610 initElem(); 1611 },WebAssembly.RuntimeError, /index out of bounds/); 1612 } 1613 1614 // run: zeroes everywhere 1615 { 1616 let { initElem, f1, f2, f3, f4 } = wasmEvalText(`(module 1617 (type $a (array (mut funcref))) 1618 (elem $e func) 1619 (func (export "initElem") (result eqref) 1620 (local $arr (ref $a)) 1621 (local.set $arr (array.new_default $a (i32.const 0))) 1622 1623 (; array to init ;) local.get $arr 1624 (; offset=0 into array ;) i32.const 0 1625 (; offset=0 into elem ;) i32.const 0 1626 (; size=0 into elem ;) i32.const 0 1627 array.init_elem $a $e 1628 1629 local.get $arr 1630 ) 1631 )`).exports; 1632 let arr = initElem(); 1633 assertEq(wasmGcArrayLength(arr), 0); 1634 } 1635 1636 // run: resulting array is as expected 1637 { 1638 let { initElem, f1, f2, f3, f4 } = wasmEvalText(`(module 1639 (type $a (array (mut funcref))) 1640 (elem $e func $f5 $f5 $f1 $f2 $f3 $f4 $f5 $f5) 1641 (func $f1 (export "f1")) 1642 (func $f2 (export "f2")) 1643 (func $f3 (export "f3")) 1644 (func $f4 (export "f4")) 1645 (func $f5) 1646 (func (export "initElem") (result eqref) 1647 (local $arr (ref $a)) 1648 (local.set $arr (array.new_default $a (i32.const 6))) 1649 1650 (; array to init ;) local.get $arr 1651 (; offset=1 into array ;) i32.const 1 1652 (; offset=2 into elem ;) i32.const 2 1653 (; size=4 into elem ;) i32.const 4 1654 array.init_elem $a $e 1655 1656 local.get $arr 1657 ) 1658 )`).exports; 1659 let arr = initElem(); 1660 assertEq(wasmGcArrayLength(arr), 6); 1661 assertEq(wasmGcReadField(arr, 0), null); 1662 assertEq(wasmGcReadField(arr, 1), f1); 1663 assertEq(wasmGcReadField(arr, 2), f2); 1664 assertEq(wasmGcReadField(arr, 3), f3); 1665 assertEq(wasmGcReadField(arr, 4), f4); 1666 assertEq(wasmGcReadField(arr, 5), null); 1667 } 1668 1669 ////////////////////////////////////////////////////////////////////////////// 1670 // 1671 // array.copy 1672 /* 1673 validation: 1674 dest array must be mutable 1675 validation: src and dest arrays must have compatible element types 1676 run: 1677 check for OOB conditions on src/dest arrays for non-zero length copies 1678 check for OOB conditions on src/dest arrays for zero length copies 1679 check resulting arrays are as expected 1680 check that null src or dest array causes a trap 1681 */ 1682 1683 // validation: dest array must be mutable 1684 assertErrorMessage(() => wasmEvalText(`(module 1685 (type $a (array i32)) 1686 (func (param eqref) 1687 (array.copy $a $a (local.get 0) (i32.const 1) 1688 (local.get 0) (i32.const 2) (i32.const 3)) 1689 ) 1690 ) 1691 `), WebAssembly.CompileError, /array is not mutable/); 1692 1693 // validation: src and dest arrays must have compatible element types #1 1694 assertErrorMessage(() => wasmEvalText(`(module 1695 (type $a32 (array (mut i32))) 1696 (type $a8 (array i8)) 1697 (func (param eqref) 1698 (array.copy $a32 $a8 (local.get 0) (i32.const 1) 1699 (local.get 0) (i32.const 2) (i32.const 3)) 1700 ) 1701 ) 1702 `), WebAssembly.CompileError, /incompatible element types/); 1703 1704 // validation: src and dest arrays must have compatible element types #2 1705 assertErrorMessage(() => wasmEvalText(`(module 1706 (type $a64 (array (mut i64))) 1707 (type $aER (array eqref)) 1708 (func (param eqref) 1709 (array.copy $a64 $aER (local.get 0) (i32.const 1) 1710 (local.get 0) (i32.const 2) (i32.const 3)) 1711 ) 1712 ) 1713 `), WebAssembly.CompileError, /incompatible element types/); 1714 1715 // validation: src and dest arrays must have compatible element types #3 1716 // 1717 // We can only copy from a child (sub) reftype to a parent (super) reftype [or 1718 // to the same reftype.] Here, we have an array of eqref and an array of 1719 // arrays. An array is a child type of eqref, so it's invalid to try and copy 1720 // eqrefs into the array of arrays. 1721 assertErrorMessage(() => wasmEvalText(`(module 1722 (type $ty (array i32)) 1723 (type $child (array (mut (ref $ty)))) 1724 (type $parent (array (mut eqref))) 1725 (func (param (ref null $child) (ref null $parent)) 1726 ;; implied copy from parent to child -> not allowed 1727 (array.copy $child $parent (local.get 1) (i32.const 1) 1728 (local.get 0) (i32.const 2) (i32.const 3)) 1729 ) 1730 ) 1731 `), WebAssembly.CompileError, /incompatible element types/); 1732 1733 // run: check for OOB conditions on src/dest arrays for non-zero length copies 1734 // run: check for OOB conditions on src/dest arrays for zero length copies 1735 // run: check resulting arrays are as expected 1736 const ARRAY_COPY_TESTS = [ 1737 // Format is: 1738 // array element type 1739 // corresponding value type 1740 // source array 1741 // first expected result 1742 // second expected result 1743 // 1744 // Value-type cases 1745 [ 'i32', 'i32', 1746 [22, 33, 44, 55, 66, 77], 1747 [22, 55, 66, 77, 66, 77], 1748 [22, 33, 22, 33, 44, 77] 1749 ], 1750 [ 'i64', 'i64', 1751 [0x2022002220002n, 0x3033003330003n, 0x4044004440004n, 1752 0x5055005550005n, 0x6066006660006n, 0x7077007770007n], 1753 [0x2022002220002n, 0x5055005550005n, 0x6066006660006n, 1754 0x7077007770007n, 0x6066006660006n, 0x7077007770007n], 1755 [0x2022002220002n, 0x3033003330003n, 0x2022002220002n, 1756 0x3033003330003n, 0x4044004440004n, 0x7077007770007n] 1757 ], 1758 [ 'f32', 'f32', 1759 [22.0, 33.0, 44.0, 55.0, 66.0, 77.0], 1760 [22.0, 55.0, 66.0, 77.0, 66.0, 77.0], 1761 [22.0, 33.0, 22.0, 33.0, 44.0, 77.0] 1762 ], 1763 [ 'f64', 'f64', 1764 [22.0, 33.0, 44.0, 55.0, 66.0, 77.0], 1765 [22.0, 55.0, 66.0, 77.0, 66.0, 77.0], 1766 [22.0, 33.0, 22.0, 33.0, 44.0, 77.0] 1767 ], 1768 [ 'externref', 'externref', 1769 ['two', 'three', 'four', 'five', 'six', 'seven'], 1770 ['two', 'five', 'six', 'seven', 'six', 'seven'], 1771 ['two', 'three', 'two', 'three', 'four', 'seven'] 1772 ], 1773 // non-Value-type cases 1774 [ 'i8', 'i32', 1775 [22, 33, 44, 55, 66, 77], 1776 [22, 55, 66, 77, 66, 77], 1777 [22, 33, 22, 33, 44, 77] 1778 ], 1779 [ 'i16', 'i32', 1780 [22, 33, 44, 55, 66, 77], 1781 [22, 55, 66, 77, 66, 77], 1782 [22, 33, 22, 33, 44, 77] 1783 ] 1784 ]; 1785 1786 for (let [elemTy, valueTy, src, exp1, exp2] of ARRAY_COPY_TESTS) { 1787 let { arrayNew, arrayCopy } = wasmEvalText( 1788 `(module 1789 (type $arrTy (array (mut ${elemTy}))) 1790 (func (export "arrayNew") 1791 (param ${valueTy} ${valueTy} ${valueTy} 1792 ${valueTy} ${valueTy} ${valueTy}) 1793 (result eqref) 1794 local.get 0 1795 local.get 1 1796 local.get 2 1797 local.get 3 1798 local.get 4 1799 local.get 5 1800 array.new_fixed $arrTy 6 1801 ) 1802 (func (export "arrayCopy") 1803 (param eqref i32 eqref i32 i32) 1804 (array.copy $arrTy $arrTy 1805 (ref.cast (ref null $arrTy) local.get 0) (local.get 1) 1806 (ref.cast (ref null $arrTy) local.get 2) (local.get 3) (local.get 4) 1807 ) 1808 ) 1809 )` 1810 ).exports; 1811 1812 assertEq(src.length, 6); 1813 assertEq(exp1.length, 6); 1814 assertEq(exp2.length, 6); 1815 1816 function eqArrays(a1, a2) { 1817 function len(arr) { 1818 return Array.isArray(arr) ? arr.length : wasmGcArrayLength(arr); 1819 } 1820 function get(arr, i) { 1821 return Array.isArray(arr) ? arr[i] : wasmGcReadField(arr, i); 1822 } 1823 assertEq(len(a1), 6); 1824 assertEq(len(a2), 6); 1825 for (i = 0; i < 6; i++) { 1826 if (get(a1, i) !== get(a2, i)) 1827 return false; 1828 } 1829 return true; 1830 } 1831 function show(who, arr) { 1832 print(who + ": " + arr[0] + " " + arr[1] + " " + arr[2] + " " 1833 + arr[3] + " " + arr[4] + " " + arr[5] + " "); 1834 } 1835 1836 // Check that "normal" copying gives expected results. 1837 let srcTO; 1838 srcTO = arrayNew(src[0], src[1], src[2], src[3], src[4], src[5]); 1839 arrayCopy(srcTO, 1, srcTO, 3, 3); 1840 assertEq(eqArrays(srcTO, exp1), true); 1841 1842 srcTO = arrayNew(src[0], src[1], src[2], src[3], src[4], src[5]); 1843 arrayCopy(srcTO, 2, srcTO, 0, 3); 1844 assertEq(eqArrays(srcTO, exp2), true); 1845 1846 // Check out-of-bounds conditions 1847 let exp1TO = arrayNew(exp1[0], exp1[1], exp1[2], exp1[3], exp1[4], exp1[5]); 1848 let exp2TO = arrayNew(exp2[0], exp2[1], exp2[2], exp2[3], exp2[4], exp2[5]); 1849 1850 // dst overrun, wants to write [5, 6] 1851 assertErrorMessage(() => { 1852 arrayCopy(exp1TO, 5, exp2TO, 1, 2); 1853 },WebAssembly.RuntimeError, /index out of bounds/); 1854 1855 // dst overrun, wants to write [7, 8] 1856 assertErrorMessage(() => { 1857 arrayCopy(exp1TO, 7, exp2TO, 1, 2); 1858 },WebAssembly.RuntimeError, /index out of bounds/); 1859 1860 // dst zero-len overrun, wants to write no elements, but starting at 9 1861 assertErrorMessage(() => { 1862 arrayCopy(exp1TO, 9, exp2TO, 1, 0); 1863 },WebAssembly.RuntimeError, /index out of bounds/); 1864 1865 // src overrun, wants to read [5, 6] 1866 assertErrorMessage(() => { 1867 arrayCopy(exp1TO, 1, exp2TO, 5, 2); 1868 },WebAssembly.RuntimeError, /index out of bounds/); 1869 1870 // src overrun, wants to read [7, 8] 1871 assertErrorMessage(() => { 1872 arrayCopy(exp1TO, 1, exp2TO, 7, 2); 1873 },WebAssembly.RuntimeError, /index out of bounds/); 1874 1875 // src zero-len overrun, wants to read no elements, but starting at 9 1876 assertErrorMessage(() => { 1877 arrayCopy(exp1TO, 1, exp2TO, 9, 0); 1878 },WebAssembly.RuntimeError, /index out of bounds/); 1879 } 1880 1881 // run: check that null src or dest array causes a trap #1 1882 { 1883 let { shouldTrap } = wasmEvalText(`(module 1884 (type $a (array (mut f32))) 1885 (func (export "shouldTrap") 1886 ref.null $a 1887 i32.const 1 1888 (array.new_fixed $a 3 (f32.const 1.23) (f32.const 4.56) (f32.const 7.89)) 1889 i32.const 1 1890 i32.const 1 1891 array.copy $a $a 1892 ) 1893 )`).exports; 1894 assertErrorMessage(() => { 1895 shouldTrap(); 1896 }, WebAssembly.RuntimeError, /null/); 1897 } 1898 1899 // run: check that null src or dest array causes a trap #2 1900 { 1901 let { shouldTrap } = wasmEvalText(`(module 1902 (type $a (array (mut f32))) 1903 (func (export "shouldTrap") 1904 (array.new_fixed $a 3 (f32.const 1.23) (f32.const 4.56) (f32.const 7.89)) 1905 i32.const 1 1906 ref.null $a 1907 i32.const 1 1908 i32.const 1 1909 array.copy $a $a 1910 ) 1911 )`).exports; 1912 assertErrorMessage(() => { 1913 shouldTrap(); 1914 }, WebAssembly.RuntimeError, /null/); 1915 } 1916 1917 ////////////////////////////////////////////////////////////////////////////// 1918 // 1919 // array.fill 1920 /* 1921 validation: 1922 array must be mutable 1923 value must be compatible with array element type 1924 run: 1925 null array causes a trap 1926 OOB conditions on array for non-zero length copies 1927 OOB conditions on array for zero length copies 1928 resulting arrays are as expected (all types) 1929 */ 1930 1931 // validation: array must be mutable 1932 assertErrorMessage(() => wasmEvalText(`(module 1933 (type $a (array i32)) 1934 (func 1935 (array.new_default $a (i32.const 8)) 1936 i32.const 0 1937 i32.const 123 1938 i32.const 8 1939 array.fill $a 1940 ) 1941 ) 1942 `), WebAssembly.CompileError, /array is not mutable/); 1943 1944 // validation: value must be compatible with array element type #1 1945 assertErrorMessage(() => wasmEvalText(`(module 1946 (type $a (array (mut i32))) 1947 (func 1948 (array.new_default $a (i32.const 8)) 1949 i32.const 0 1950 i64.const 123 1951 i32.const 8 1952 array.fill $a 1953 ) 1954 ) 1955 `), WebAssembly.CompileError, /type mismatch/); 1956 1957 // validation: value must be compatible with array element type #2 1958 assertErrorMessage(() => wasmEvalText(`(module 1959 (type $a (array (mut eqref))) 1960 (func 1961 (array.new_default $a (i32.const 8)) 1962 i32.const 0 1963 ref.null any 1964 i32.const 8 1965 array.fill $a 1966 ) 1967 ) 1968 `), WebAssembly.CompileError, /type mismatch/); 1969 1970 // run: null array causes a trap 1971 { 1972 const { arrayFill } = wasmEvalText(`(module 1973 (type $a (array (mut i32))) 1974 (func (export "arrayFill") 1975 ref.null $a 1976 i32.const 0 1977 i32.const 123 1978 i32.const 8 1979 array.fill $a 1980 ) 1981 )`).exports; 1982 assertErrorMessage(() => { 1983 arrayFill(); 1984 }, WebAssembly.RuntimeError, /dereferencing null pointer/); 1985 } 1986 1987 // run: OOB conditions on array for non-zero length copies 1988 { 1989 const { arrayFill } = wasmEvalText(`(module 1990 (type $a (array (mut i32))) 1991 (func (export "arrayFill") 1992 (array.new_default $a (i32.const 8)) 1993 i32.const 1 1994 i32.const 123 1995 i32.const 8 1996 array.fill $a 1997 ) 1998 )`).exports; 1999 assertErrorMessage(() => { 2000 arrayFill(); 2001 }, WebAssembly.RuntimeError, /index out of bounds/); 2002 } 2003 2004 // run: OOB conditions on array for zero length copies 2005 { 2006 const { arrayFill } = wasmEvalText(`(module 2007 (type $a (array (mut i32))) 2008 (func (export "arrayFill") 2009 (array.new_default $a (i32.const 8)) 2010 i32.const 8 2011 i32.const 123 2012 i32.const 0 2013 array.fill $a 2014 ) 2015 )`).exports; 2016 arrayFill(); 2017 } 2018 2019 // run: arrays are as expected (all types) 2020 { 2021 const TESTS = [ 2022 { type: 'i8', val: 'i32.const 123', get: 'array.get_u', test: 'i32.eq' }, 2023 { type: 'i16', val: 'i32.const 123', get: 'array.get_u', test: 'i32.eq' }, 2024 { type: 'i32', val: 'i32.const 123', test: 'i32.eq' }, 2025 { type: 'i64', val: 'i64.const 123', test: 'i64.eq' }, 2026 { type: 'f32', val: 'f32.const 3.14', test: 'f32.eq' }, 2027 { type: 'f64', val: 'f64.const 3.14', test: 'f64.eq' }, 2028 { type: 'eqref', val: 'global.get 0', test: 'ref.eq' }, 2029 ]; 2030 if (wasmSimdEnabled()) { 2031 TESTS.push({ type: 'v128', val: 'v128.const i32x4 111 222 333 444', test: '(v128.xor) (i32.eq (v128.any_true) (i32.const 0))' }); 2032 } 2033 2034 for (const { type, val, get = 'array.get', test } of TESTS) { 2035 const { arrayFill, isDefault, isFilled } = wasmEvalText(`(module 2036 (type $a (array (mut ${type}))) 2037 (type $s (struct)) 2038 (global (ref $s) (struct.new_default $s)) 2039 (func (export "arrayFill") (result (ref $a)) 2040 (local $arr (ref $a)) 2041 (local.set $arr (array.new_default $a (i32.const 4))) 2042 2043 local.get $arr 2044 i32.const 1 2045 ${val} 2046 i32.const 2 2047 array.fill $a 2048 2049 local.get $arr 2050 ) 2051 (func (export "isDefault") (param (ref $a) i32) (result i32) 2052 (${get} $a (local.get 0) (local.get 1)) 2053 (${get} $a (array.new_default $a (i32.const 1)) (i32.const 0)) 2054 ${test} 2055 ) 2056 (func (export "isFilled") (param (ref $a) i32) (result i32) 2057 (${get} $a (local.get 0) (local.get 1)) 2058 ${val} 2059 ${test} 2060 ) 2061 )`).exports; 2062 const arr = arrayFill(); 2063 assertEq(isDefault(arr, 0), 1, `expected default value for ${type} but got filled`); 2064 assertEq(isFilled(arr, 1), 1, `expected filled value for ${type} but got default`); 2065 assertEq(isFilled(arr, 2), 1, `expected filled value for ${type} but got default`); 2066 assertEq(isDefault(arr, 3), 1, `expected default value for ${type} but got filled`); 2067 } 2068 } 2069 2070 // Test whether array data pointers are correctly tracked in stack maps. 2071 { 2072 const { newArray, test } = wasmEvalText(`(module 2073 (type $a (array i32)) 2074 (import "" "gc" (func $gc)) 2075 (func (export "newArray") (result (ref $a)) 2076 (array.new $a (i32.const 123) (i32.const 4)) 2077 ) 2078 (func (export "test") (param $arr (ref $a)) (result i32) 2079 (local i32) 2080 (local i32) 2081 2082 (array.get $a (local.get $arr) (i32.const 1)) 2083 local.set 1 2084 call $gc 2085 (array.get $a (local.get $arr) (i32.const 2)) 2086 local.set 2 2087 2088 (i32.add (local.get 1) (local.get 2)) 2089 ) 2090 )`, {"": {gc}}).exports; 2091 const arr = newArray(); 2092 assertEq(isNurseryAllocated(arr), true); 2093 const res = test(arr); 2094 assertEq(res, 246); 2095 } 2096 2097 // Test that zero-length arrays allocate correctly 2098 { 2099 const { testNew, testNewDefault, testNewFixed } = wasmEvalText(`(module 2100 (type $a (array f32)) 2101 2102 (func (export "testNew") (result eqref eqref eqref eqref) 2103 (array.new $a (f32.const 123) (i32.const 0)) 2104 (array.new $a (f32.const 123) (i32.const 0)) 2105 (array.new $a (f32.const 123) (i32.const 0)) 2106 (array.new $a (f32.const 123) (i32.const 0)) 2107 ) 2108 (func (export "testNewDefault") (result eqref eqref eqref eqref) 2109 (array.new_default $a (i32.const 0)) 2110 (array.new_default $a (i32.const 0)) 2111 (array.new_default $a (i32.const 0)) 2112 (array.new_default $a (i32.const 0)) 2113 ) 2114 (func (export "testNewFixed") (result eqref eqref eqref eqref) 2115 (array.new_fixed $a 0) 2116 (array.new_fixed $a 0) 2117 (array.new_fixed $a 0) 2118 (array.new_fixed $a 0) 2119 ) 2120 )`).exports; 2121 testNew(); 2122 testNewDefault(); 2123 testNewFixed(); 2124 }