diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jit-test/tests/wasm/gc/ref-struct.js | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/wasm/gc/ref-struct.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/gc/ref-struct.js | 328 |
1 files changed, 328 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/gc/ref-struct.js b/js/src/jit-test/tests/wasm/gc/ref-struct.js new file mode 100644 index 0000000000..59376ac1e7 --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/ref-struct.js @@ -0,0 +1,328 @@ +// |jit-test| skip-if: !wasmGcEnabled() + +// We'll be running some binary-format tests shortly. + +load(libdir + "wasm-binary.js"); + +const v2vSigSection = sigSection([{args:[], ret:VoidCode}]); + +function checkInvalid(body, errorMessage) { + assertErrorMessage(() => new WebAssembly.Module( + moduleWithSections([v2vSigSection, + declSection([0]), + bodySection([body])])), + WebAssembly.CompileError, + errorMessage); +} + +// General test case for struct.new, struct.get, and struct.set: binary tree +// manipulation. + +{ + let bin = wasmTextToBinary( + `(module + (import "" "print_lp" (func $print_lp)) + (import "" "print_rp" (func $print_rp)) + (import "" "print_int" (func $print_int (param i32))) + + (type $wabbit (struct + (field $x (mut i32)) + (field $left (mut (ref null $wabbit))) + (field $right (mut (ref null $wabbit))))) + + (global $g (mut (ref null $wabbit)) (ref.null $wabbit)) + + (global $k (mut i32) (i32.const 0)) + + (func (export "init") (param $n i32) + (global.set $g (call $make (local.get $n)))) + + (func $make (param $n i32) (result (ref null $wabbit)) + (local $tmp i32) + (local.set $tmp (global.get $k)) + (global.set $k (i32.add (local.get $tmp) (i32.const 1))) + (if (result (ref null $wabbit)) (i32.le_s (local.get $n) (i32.const 2)) + (struct.new $wabbit (local.get $tmp) (ref.null $wabbit) (ref.null $wabbit)) + (block (result (ref null $wabbit)) + (struct.new $wabbit + (local.get $tmp) + (call $make (i32.sub (local.get $n) (i32.const 1))) + (call $make (i32.sub (local.get $n) (i32.const 2))))))) + + (func (export "accumulate") (result i32) + (call $accum (global.get $g))) + + (func $accum (param $w (ref null $wabbit)) (result i32) + (if (result i32) (ref.is_null (local.get $w)) + (i32.const 0) + (i32.add (struct.get $wabbit 0 (local.get $w)) + (i32.sub (call $accum (struct.get $wabbit 1 (local.get $w))) + (call $accum (struct.get $wabbit 2 (local.get $w))))))) + + (func (export "reverse") + (call $reverse (global.get $g))) + + (func $reverse (param $w (ref null $wabbit)) + (local $tmp (ref null $wabbit)) + (if (i32.eqz (ref.is_null (local.get $w))) + (block + (struct.set $wabbit 0 (local.get $w) (i32.mul (i32.const 2) (struct.get $wabbit 0 (local.get $w)))) + (local.set $tmp (struct.get $wabbit 1 (local.get $w))) + (struct.set $wabbit 1 (local.get $w) (struct.get $wabbit 2 (local.get $w))) + (struct.set $wabbit 2 (local.get $w) (local.get $tmp)) + (call $reverse (struct.get $wabbit 1 (local.get $w))) + (call $reverse (struct.get $wabbit 2 (local.get $w)))))) + + (func (export "print") + (call $pr (global.get $g))) + + (func $pr (param $w (ref null $wabbit)) + (if (i32.eqz (ref.is_null (local.get $w))) + (block + (call $print_lp) + (call $print_int (struct.get $wabbit 0 (local.get $w))) + (call $pr (struct.get $wabbit 1 (local.get $w))) + (call $pr (struct.get $wabbit 2 (local.get $w))) + (call $print_rp)))) + )`); + + let s = ""; + function pr_int(k) { s += k + " "; } + function pr_lp() { s += "(" }; + function pr_rp() { s += ")" } + + let mod = new WebAssembly.Module(bin); + let ins = new WebAssembly.Instance(mod, {"":{print_int:pr_int,print_lp:pr_lp,print_rp:pr_rp}}).exports; + + ins.init(6); + s = ""; ins.print(); assertEq(s, "(0 (1 (2 (3 (4 )(5 ))(6 ))(7 (8 )(9 )))(10 (11 (12 )(13 ))(14 )))"); + assertEq(ins.accumulate(), -13); + + ins.reverse(); + s = ""; ins.print(); assertEq(s, "(0 (20 (28 )(22 (26 )(24 )))(2 (14 (18 )(16 ))(4 (12 )(6 (10 )(8 )))))"); + assertEq(ins.accumulate(), 14); + + for (let i=10; i < 22; i++ ) { + ins.init(i); + ins.reverse(); + gc(); + ins.reverse(); + } +} + +// Sanity check for struct.set: we /can/ store a (ref null T) into a (ref null U) field +// with struct.set if T <: U; this should fall out of normal coercion but good +// to test. + +wasmEvalText( + `(module + (type $node (struct (field (mut (ref null $node))))) + (type $nix (sub $node (struct (field (mut (ref null $node))) (field i32)))) + (func $f (param $p (ref null $node)) (param $q (ref null $nix)) + (struct.set $node 0 (local.get $p) (local.get $q))))`); + +// ref.cast: if the pointer is null we trap + +assertErrorMessage(() => wasmEvalText( + `(module + (type $node (struct (field i32))) + (type $node2 (struct (field i32) (field f32))) + (func $f (param $p (ref null $node)) (result (ref null $node2)) + (ref.cast (ref $node2) (local.get $p))) + (func (export "test") (result eqref) + (call $f (ref.null $node))))`).exports.test(), + WebAssembly.RuntimeError, + /bad cast/, +); + +// ref.cast null: if the pointer is null we do not trap + +wasmEvalText( + `(module + (type $node (struct (field i32))) + (type $node2 (struct (field i32) (field f32))) + (func $f (param $p (ref null $node)) (result (ref null $node2)) + (ref.cast (ref null $node2) (local.get $p))) + (func (export "test") (result eqref) + (call $f (ref.null $node))))`).exports.test(); + +// ref.cast: if the downcast succeeds we get the original pointer + +assertEq(wasmEvalText( + `(module + (type $node (struct (field i32))) + (type $node2 (sub $node (struct (field i32) (field f32)))) + (func $f (param $p (ref null $node)) (result (ref null $node2)) + (ref.cast (ref null $node2) (local.get $p))) + (func (export "test") (result i32) + (local $n (ref null $node)) + (local.set $n (struct.new $node2 (i32.const 0) (f32.const 12))) + (ref.eq (call $f (local.get $n)) (local.get $n))))`).exports.test(), + 1); + +// And once more with mutable fields + +assertEq(wasmEvalText( + `(module + (type $node (struct (field (mut i32)))) + (type $node2 (sub $node (struct (field (mut i32)) (field f32)))) + (func $f (param $p (ref null $node)) (result (ref null $node2)) + (ref.cast (ref null $node2) (local.get $p))) + (func (export "test") (result i32) + (local $n (ref null $node)) + (local.set $n (struct.new $node2 (i32.const 0) (f32.const 12))) + (ref.eq (call $f (local.get $n)) (local.get $n))))`).exports.test(), + 1); + +// ref.cast: eqref -> struct when the eqref is the right struct; +// special case since eqref requires unboxing + +assertEq(wasmEvalText( + `(module + (type $node (struct (field i32))) + (func $f (param $p eqref) (result (ref null $node)) + (ref.cast (ref null $node) (local.get $p))) + (func (export "test") (result i32) + (local $n (ref null $node)) + (local.set $n (struct.new $node (i32.const 0))) + (ref.eq (call $f (local.get $n)) (local.get $n))))`).exports.test(), + 1); + +// Can default initialize a struct which zero initializes + +{ + let {makeA, makeB, makeC} = wasmEvalText(` + (module + (type $a (struct)) + (type $b (struct (field i32) (field f32))) + (type $c (struct (field eqref))) + + (func (export "makeA") (result eqref) + struct.new_default $a + ) + (func (export "makeB") (result eqref) + struct.new_default $b + ) + (func (export "makeC") (result eqref) + struct.new_default $c + ) + )`).exports; + let a = makeA(); + + let b = makeB(); + assertEq(b[0], 0); + assertEq(b[1], 0); + + let c = makeC(); + assertEq(c[0], null); +} + +// struct.new_default: valid if all struct fields are defaultable + +wasmFailValidateText(`(module + (type $a (struct (field (ref $a)))) + (func + struct.new_default $a + ) +)`, /defaultable/); + +wasmFailValidateText(`(module + (type $a (struct (field i32) (field i32) (field (ref $a)))) + (func + struct.new_default $a + ) +)`, /defaultable/); + +// Negative tests + +// Attempting to mutate immutable field with struct.set + +assertErrorMessage(() => wasmEvalText( + `(module + (type $node (struct (field i32))) + (func $f (param $p (ref null $node)) + (struct.set $node 0 (local.get $p) (i32.const 37))))`), + WebAssembly.CompileError, + /field is not mutable/); + +// Attempting to store incompatible value in mutable field with struct.set + +assertErrorMessage(() => wasmEvalText( + `(module + (type $node (struct (field (mut i32)))) + (func $f (param $p (ref null $node)) + (struct.set $node 0 (local.get $p) (f32.const 37))))`), + WebAssembly.CompileError, + /expression has type f32 but expected i32/); + +// Out-of-bounds reference for struct.get + +assertErrorMessage(() => wasmEvalText( + `(module + (type $node (struct (field i32))) + (func $f (param $p (ref null $node)) (result i32) + (struct.get $node 1 (local.get $p))))`), + WebAssembly.CompileError, + /field index out of range/); + +// Out-of-bounds reference for struct.set + +assertErrorMessage(() => wasmEvalText( + `(module + (type $node (struct (field (mut i32)))) + (func $f (param $p (ref null $node)) + (struct.set $node 1 (local.get $p) (i32.const 37))))`), + WebAssembly.CompileError, + /field index out of range/); + +// Base pointer is of unrelated type to stated type in struct.get + +assertErrorMessage(() => wasmEvalText( + `(module + (type $node (struct (field i32))) + (type $snort (struct (field f64))) + (func $f (param $p (ref null $snort)) (result i32) + (struct.get $node 0 (local.get $p))))`), + WebAssembly.CompileError, + /expression has type.*but expected.*/); + +// Base pointer is of unrelated type to stated type in struct.set + +assertErrorMessage(() => wasmEvalText( + `(module + (type $node (struct (field (mut i32)))) + (type $snort (struct (field f64))) + (func $f (param $p (ref null $snort)) (result i32) + (struct.set $node 0 (local.get $p) (i32.const 0))))`), + WebAssembly.CompileError, + /expression has type.*but expected.*/); + +// Null pointer dereference in struct.get + +assertErrorMessage(function() { + let ins = wasmEvalText( + `(module + (type $node (struct (field i32))) + (func (export "test") + (drop (call $f (ref.null $node)))) + (func $f (param $p (ref null $node)) (result i32) + (struct.get $node 0 (local.get $p))))`); + ins.exports.test(); +}, + WebAssembly.RuntimeError, + /dereferencing null pointer/); + +// Null pointer dereference in struct.set + +assertErrorMessage(function() { + let ins = wasmEvalText( + `(module + (type $node (struct (field (mut i32)))) + (func (export "test") + (call $f (ref.null $node))) + (func $f (param $p (ref null $node)) + (struct.set $node 0 (local.get $p) (i32.const 0))))`); + ins.exports.test(); +}, + WebAssembly.RuntimeError, + /dereferencing null pointer/); |