diff options
Diffstat (limited to 'js/src/jit-test/tests/wasm/gc/structs.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/gc/structs.js | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/gc/structs.js b/js/src/jit-test/tests/wasm/gc/structs.js new file mode 100644 index 0000000000..388541c4ee --- /dev/null +++ b/js/src/jit-test/tests/wasm/gc/structs.js @@ -0,0 +1,724 @@ +// |jit-test| skip-if: !wasmGcEnabled() + +// This tests a bunch of wasm struct stuff, but not i8 or i16 fields. +// See structs2.js for i8/i16 field tests. + +var conf = getBuildConfiguration(); + +var bin = wasmTextToBinary( + `(module + (func $x1 (import "m" "x1") (type $f1)) + (func $x2 (import "m" "x2") (type $f2)) + + (table 2 funcref) + (elem (i32.const 0) $doit $doitagain) + + ;; Type array has a mix of types + + (type $f1 (func (param i32) (result i32))) + + (type $point (struct + (field $point_x i32) + (field $point_y i32))) + + (type $f2 (func (param f64) (result f64))) + + (type $int_node (struct + (field $intbox_val (mut i32)) + (field $intbox_next (mut externref)))) + + ;; Test all the types. + + (type $omni (struct + (field $omni_i32 i32) + (field $omni_i32m (mut i32)) + (field $omni_i64 i64) + (field $omni_i64m (mut i64)) + (field $omni_f32 f32) + (field $omni_f32m (mut f32)) + (field $omni_f64 f64) + (field $omni_f64m (mut f64)) + (field $omni_externref externref) + (field $omni_externrefm (mut externref)))) + + ;; Various ways to reference a type in the middle of the + ;; type array, make sure we get the right one + + (func (export "hello") (param f64) (param i32) (result f64) + (call_indirect (type $f2) (local.get 0) (local.get 1))) + + (func $doit (param f64) (result f64) + (f64.sqrt (local.get 0))) + + (func $doitagain (param f64) (result f64) + (f64.mul (local.get 0) (local.get 0))) + + (func (export "x1") (param i32) (result i32) + (call $x1 (local.get 0))) + + (func (export "x2") (param f64) (result f64) + (call $x2 (local.get 0))) + + ;; Useful for testing to ensure that the type is not type #0 here. + + (func (export "mk_point") (result eqref) + (struct.new $point (i32.const 37) (i32.const 42))) + + (func (export "mk_int_node") (param i32) (param externref) (result eqref) + (struct.new $int_node (local.get 0) (local.get 1))) + + ;; Too big to fit in an InlineTypedObject. + + (type $bigger (struct + (field $a i32) + (field $b i32) + (field $c i32) + (field $d i32) + (field $e i32) + (field $f i32) + (field $g i32) + (field $h i32) + (field $i i32) + (field $j i32) + (field $k i32) + (field $l i32) + (field $m i32) + (field $n i32) + (field $o i32) + (field $p i32) + (field $q i32) + (field $r i32) + (field $s i32) + (field $t i32) + (field $u i32) + (field $v i32) + (field $w i32) + (field $x i32) + (field $y i32) + (field $z i32) + (field $aa i32) + (field $ab i32) + (field $ac i32) + (field $ad i32) + (field $ae i32) + (field $af i32) + (field $ag i32) + (field $ah i32) + (field $ai i32) + (field $aj i32) + (field $ak i32) + (field $al i32) + (field $am i32) + (field $an i32) + (field $ao i32) + (field $ap i32) + (field $aq i32) + (field $ar i32) + (field $as i32) + (field $at i32) + (field $au i32) + (field $av i32) + (field $aw i32) + (field $ax i32) + (field $ay i32) + (field $az i32))) + + (func (export "mk_bigger") (result eqref) + (struct.new $bigger + (i32.const 0) + (i32.const 1) + (i32.const 2) + (i32.const 3) + (i32.const 4) + (i32.const 5) + (i32.const 6) + (i32.const 7) + (i32.const 8) + (i32.const 9) + (i32.const 10) + (i32.const 11) + (i32.const 12) + (i32.const 13) + (i32.const 14) + (i32.const 15) + (i32.const 16) + (i32.const 17) + (i32.const 18) + (i32.const 19) + (i32.const 20) + (i32.const 21) + (i32.const 22) + (i32.const 23) + (i32.const 24) + (i32.const 25) + (i32.const 26) + (i32.const 27) + (i32.const 28) + (i32.const 29) + (i32.const 30) + (i32.const 31) + (i32.const 32) + (i32.const 33) + (i32.const 34) + (i32.const 35) + (i32.const 36) + (i32.const 37) + (i32.const 38) + (i32.const 39) + (i32.const 40) + (i32.const 41) + (i32.const 42) + (i32.const 43) + (i32.const 44) + (i32.const 45) + (i32.const 46) + (i32.const 47) + (i32.const 48) + (i32.const 49) + (i32.const 50) + (i32.const 51))) + + (type $withfloats (struct + (field $f1 f32) + (field $f2 f64) + (field $f3 externref) + (field $f4 f32) + (field $f5 i32))) + + (func (export "mk_withfloats") + (param f32) (param f64) (param externref) (param f32) (param i32) + (result eqref) + (struct.new $withfloats (local.get 0) (local.get 1) (local.get 2) (local.get 3) (local.get 4))) + + )`) + +var mod = new WebAssembly.Module(bin); +var ins = new WebAssembly.Instance(mod, {m:{x1(x){ return x*3 }, x2(x){ return Math.PI }}}).exports; + +assertEq(ins.hello(4.0, 0), 2.0) +assertEq(ins.hello(4.0, 1), 16.0) + +assertEq(ins.x1(12), 36) +assertEq(ins.x2(8), Math.PI) + +var point = ins.mk_point(); +assertEq(0 in point, true); +assertEq(1 in point, true); +assertEq(2 in point, false); +assertEq(point[0], 37); +assertEq(point[1], 42); + +var int_node = ins.mk_int_node(78, point); +assertEq(int_node[0], 78); +assertEq(int_node[1], point); + +var bigger = ins.mk_bigger(); +for ( let i=0; i < 52; i++ ) + assertEq(bigger[i], i); + +assertEq(bigger[-1], undefined); +assertEq(bigger[52], undefined); + +var withfloats = ins.mk_withfloats(1/3, Math.PI, bigger, 5/6, 0x1337); +assertEq(withfloats[0], Math.fround(1/3)); +assertEq(withfloats[1], Math.PI); +assertEq(withfloats[2], bigger); +assertEq(withfloats[3], Math.fround(5/6)); +assertEq(withfloats[4], 0x1337); + +// A simple stress test + +var stress = wasmTextToBinary( + `(module + (type $node (struct (field i32) (field (ref null $node)))) + (func (export "iota1") (param $n i32) (result eqref) + (local $list (ref null $node)) + (block $exit + (loop $loop + (br_if $exit (i32.eqz (local.get $n))) + (local.set $list (struct.new $node (local.get $n) (local.get $list))) + (local.set $n (i32.sub (local.get $n) (i32.const 1))) + (br $loop))) + (local.get $list)))`); +var stressIns = new WebAssembly.Instance(new WebAssembly.Module(stress)).exports; +var stressLevel = conf.x64 && !conf.tsan && !conf.asan && !conf.valgrind ? 100000 : 1000; +var the_list = stressIns.iota1(stressLevel); +for (let i=1; i <= stressLevel; i++) { + assertEq(the_list[0], i); + the_list = the_list[1]; +} +assertEq(the_list, null); + +// Fields and their exposure in JS. We can't export types yet so hide them +// inside the module with globals. + +// i64 fields. + +{ + let txt = + `(module + (type $big (struct + (field (mut i32)) + (field (mut i64)) + (field (mut i32)))) + + (func (export "set") (param eqref) + (local (ref null $big)) + (local.set 1 (ref.cast (ref null $big) (local.get 0))) + (struct.set $big 1 (local.get 1) (i64.const 0x3333333376544567))) + + (func (export "set2") (param $p eqref) + (struct.set $big 1 + (ref.cast (ref null $big) (local.get $p)) + (i64.const 0x3141592653589793))) + + (func (export "low") (param $p eqref) (result i32) + (i32.wrap/i64 (struct.get $big 1 (ref.cast (ref null $big) (local.get $p))))) + + (func (export "high") (param $p eqref) (result i32) + (i32.wrap/i64 (i64.shr_u + (struct.get $big 1 (ref.cast (ref null $big) (local.get $p))) + (i64.const 32)))) + + (func (export "mk") (result eqref) + (struct.new $big (i32.const 0x7aaaaaaa) (i64.const 0x4201020337) (i32.const 0x6bbbbbbb))) + + )`; + + let ins = wasmEvalText(txt).exports; + + let v = ins.mk(); + assertEq(typeof v, "object"); + assertEq(v[0], 0x7aaaaaaa); + assertEq(v[1], 0x4201020337n); + assertEq(ins.low(v), 0x01020337); + assertEq(ins.high(v), 0x42); + assertEq(v[2], 0x6bbbbbbb); + + ins.set(v); + assertEq(v[0], 0x7aaaaaaa); + assertEq(v[1], 0x3333333376544567n); + assertEq(v[2], 0x6bbbbbbb); + + ins.set2(v); + assertEq(v[1], 0x3141592653589793n); + assertEq(ins.low(v), 0x53589793); + assertEq(ins.high(v), 0x31415926) +} + +{ + let txt = + `(module + (type $big (struct + (field (mut i32)) + (field (mut i64)) + (field (mut i32)))) + + (global $g (mut (ref null $big)) (ref.null $big)) + + (func (export "make") (result eqref) + (global.set $g + (struct.new $big (i32.const 0x7aaaaaaa) (i64.const 0x4201020337) (i32.const 0x6bbbbbbb))) + (global.get $g)) + + (func (export "update0") (param $x i32) + (struct.set $big 0 (global.get $g) (local.get $x))) + + (func (export "get0") (result i32) + (struct.get $big 0 (global.get $g))) + + (func (export "update1") (param $hi i32) (param $lo i32) + (struct.set $big 1 (global.get $g) + (i64.or + (i64.shl (i64.extend_u/i32 (local.get $hi)) (i64.const 32)) + (i64.extend_u/i32 (local.get $lo))))) + + (func (export "get1_low") (result i32) + (i32.wrap/i64 (struct.get $big 1 (global.get $g)))) + + (func (export "get1_high") (result i32) + (i32.wrap/i64 + (i64.shr_u (struct.get $big 1 (global.get $g)) (i64.const 32)))) + + (func (export "update2") (param $x i32) + (struct.set $big 2 (global.get $g) (local.get $x))) + + (func (export "get2") (result i32) + (struct.get $big 2 (global.get $g))) + + )`; + + let ins = wasmEvalText(txt).exports; + + let v = ins.make(); + assertEq(v[0], 0x7aaaaaaa); + assertEq(v[1], 0x4201020337n); + assertEq(v[2], 0x6bbbbbbb); + + ins.update0(0x45367101); + assertEq(v[0], 0x45367101); + assertEq(ins.get0(), 0x45367101); + assertEq(v[1], 0x4201020337n); + assertEq(v[2], 0x6bbbbbbb); + + ins.update2(0x62345123); + assertEq(v[0], 0x45367101); + assertEq(v[1], 0x4201020337n); + assertEq(ins.get2(), 0x62345123); + assertEq(v[2], 0x62345123); + + ins.update1(0x77777777, 0x22222222); + assertEq(v[0], 0x45367101); + assertEq(ins.get1_low(), 0x22222222); + assertEq(ins.get1_high(), 0x77777777); + assertEq(v[1], 0x7777777722222222n); + assertEq(v[2], 0x62345123); +} + + +var bin = wasmTextToBinary( + `(module + (type $cons (struct (field i32) (field (ref null $cons)))) + + (global $g (mut (ref null $cons)) (ref.null $cons)) + + (func (export "push") (param i32) + (global.set $g (struct.new $cons (local.get 0) (global.get $g)))) + + (func (export "top") (result i32) + (struct.get $cons 0 (global.get $g))) + + (func (export "pop") + (global.set $g (struct.get $cons 1 (global.get $g)))) + + (func (export "is_empty") (result i32) + (ref.is_null (global.get $g))) + + )`); + +var mod = new WebAssembly.Module(bin); +var ins = new WebAssembly.Instance(mod).exports; +ins.push(37); +ins.push(42); +ins.push(86); +assertEq(ins.top(), 86); +ins.pop(); +assertEq(ins.top(), 42); +ins.pop(); +assertEq(ins.top(), 37); +ins.pop(); +assertEq(ins.is_empty(), 1); +assertErrorMessage(() => ins.pop(), + WebAssembly.RuntimeError, + /dereferencing null pointer/); + +// Check that a wrapped object cannot be passed as an eqref even if the wrapper +// points to the right type. This is a temporary restriction, until we're able +// to avoid dealing with wrappers inside the engine. + +{ + var ins = wasmEvalText( + `(module + (type $Node (struct (field i32))) + (func (export "mk") (result eqref) + (struct.new $Node (i32.const 37))) + (func (export "f") (param $n eqref) (result eqref) + (ref.cast (ref null $Node) (local.get $n))))`).exports; + var n = ins.mk(); + assertEq(ins.f(n), n); + assertErrorMessage(() => ins.f(wrapWithProto(n, {})), TypeError, /can only pass a WebAssembly GC object/); +} + +// Field names. + +// Test that names map to the right fields. + +{ + let ins = new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary( + `(module + (type $s (struct + (field $x i32) + (field $y i32))) + + (func $f (param $p (ref null $s)) (result i32) + (struct.get $s $x (local.get $p))) + + (func $g (param $p (ref null $s)) (result i32) + (struct.get $s $y (local.get $p))) + + (func (export "testf") (param $n i32) (result i32) + (call $f (struct.new $s (local.get $n) (i32.mul (local.get $n) (i32.const 2))))) + + (func (export "testg") (param $n i32) (result i32) + (call $g (struct.new $s (local.get $n) (i32.mul (local.get $n) (i32.const 2))))) + + )`))).exports; + + assertEq(ins.testf(10), 10); + assertEq(ins.testg(10), 20); +} + +// Test that field names must be unique in the module. + +assertErrorMessage(() => wasmTextToBinary( + `(module + (type $s (struct (field $x i32))) + (type $t (struct (field $x i32))) + )`), + SyntaxError, + /duplicate identifier for field/); + +// negative tests + +// Wrong type passed as initializer + +assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` +(module + (type $r (struct (field i32))) + (func $f (param f64) (result eqref) + (struct.new $r (local.get 0))) +)`)), +WebAssembly.CompileError, /type mismatch/); + +// Too few values passed for initializer + +assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` +(module + (type $r (struct (field i32) (field i32))) + (func $f (result eqref) + (struct.new $r (i32.const 0))) +)`)), +WebAssembly.CompileError, /popping value from empty stack/); + +// Too many values passed for initializer, sort of + +assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` +(module + (type $r (struct (field i32) (field i32))) + (func $f (result eqref) + (i32.const 0) + (i32.const 1) + (i32.const 2) + struct.new $r) +)`)), +WebAssembly.CompileError, /unused values/); + +// Not referencing a structure type + +assertErrorMessage(() => new WebAssembly.Module(wasmTextToBinary(` +(module + (type (func (param i32) (result i32))) + (func $f (result eqref) + (struct.new 0)) +)`)), +WebAssembly.CompileError, /not a struct type/); + +// Nominal type equivalence for structs, but the prefix rule allows this +// conversion to succeed. + +wasmEvalText(` + (module + (type $p (struct (field i32))) + (type $q (struct (field i32))) + (func $f (result (ref null $p)) + (struct.new $q (i32.const 0)))) +`); + +// The field name is optional, so this should work. + +wasmEvalText(` +(module + (type $s (struct (field i32)))) +`) + +// Empty structs are OK. + +wasmEvalText(` +(module + (type $s (struct))) +`) + +// Multiply defined structures. + +assertErrorMessage(() => wasmEvalText(` +(module + (type $s (struct (field $x i32))) + (type $s (struct (field $y i32)))) +`), +SyntaxError, /duplicate type identifier/); + +// Bogus type definition syntax. + +assertErrorMessage(() => wasmEvalText(` +(module + (type $s)) +`), +SyntaxError, /wasm text error/); + +assertErrorMessage(() => wasmEvalText(` +(module + (type $s (field $x i32))) +`), +SyntaxError, /expected one of: `func`, `struct`, `array`/); + +assertErrorMessage(() => wasmEvalText(` +(module + (type $s (struct (field $x i31)))) +`), +SyntaxError, /wasm text error/); + +assertErrorMessage(() => wasmEvalText(` +(module + (type $s (struct (fjeld $x i32)))) +`), +SyntaxError, /wasm text error/); + +assertErrorMessage(() => wasmEvalText(` +(module + (type $s (struct abracadabra))) +`), +SyntaxError, /wasm text error/); + +// Function should not reference struct type: syntactic test + +assertErrorMessage(() => wasmEvalText(` +(module + (type $s (struct)) + (type $f (func (param i32) (result i32))) + (func (type 0) (param i32) (result i32) (unreachable))) +`), +WebAssembly.CompileError, /signature index references non-signature/); + +// Can't set immutable fields from JS + +{ + let ins = wasmEvalText( + `(module + (type $s (struct + (field i32) + (field (mut i64)))) + (func (export "make") (result eqref) + (struct.new $s (i32.const 37) (i64.const 42))))`).exports; + let v = ins.make(); + assertErrorMessage(() => v[0] = 12, + Error, + /setting immutable field/); + assertErrorMessage(() => v[1] = 12, + Error, + /setting immutable field/); +} + +// Function should not reference struct type: binary test + +var bad = new Uint8Array([0x00, 0x61, 0x73, 0x6d, + 0x01, 0x00, 0x00, 0x00, + + 0x01, // Type section + 0x03, // Section size + 0x01, // One type + 0x5f, // Struct + 0x00, // Zero fields + + 0x03, // Function section + 0x02, // Section size + 0x01, // One function + 0x00, // Type of function + + 0x0a, // Code section + 0x05, // Section size + 0x01, // One body + 0x03, // Body size + 0x00, // Zero locals + 0x00, // UNREACHABLE + 0x0b]); // END + +assertErrorMessage(() => new WebAssembly.Module(bad), + WebAssembly.CompileError, /signature index references non-signature/); + +// Exercise alias-analysis code for struct access +{ + let txt = + `(module + (type $meh (struct)) + (type $hasOOL (struct + ;; In-line storage + (field i64) (field i64) + (field $ILnonref (mut i64)) (field $ILref (mut eqref)) + (field i64) (field i64) (field i64) (field i64) + (field i64) (field i64) (field i64) (field i64) + (field i64) (field i64) (field i64) (field i64) + ;; Out-of-line storage (or maybe it starts earlier, but + ;; definitely not after this point). + (field $OOLnonref (mut i64)) (field $OOLref (mut eqref))) + ) + (func (export "create") (result eqref) + (struct.new $hasOOL + (i64.const 1) (i64.const 2) + (i64.const 9876) (ref.null $meh) + (i64.const 3) (i64.const 4) (i64.const 5) (i64.const 6) + (i64.const 7) (i64.const 8) (i64.const 9) (i64.const 10) + (i64.const 11) (i64.const 12) (i64.const 13) (i64.const 14) + (i64.const 4321) (ref.null $meh)) + ) + ;; Write to an OOL field, then an IL field, then to an OOL field, so + ;; that we can at least check (from inspection of the optimised MIR) + ;; that the GVN+alias analysis causes the OOL block pointer not to be + ;; reloaded for the second OOL write. First for non-ref fields .. + (func (export "threeSetsNonReffy") (param eqref) + (local (ref $hasOOL)) + (local.set 1 (ref.as_non_null (ref.cast (ref null $hasOOL) (local.get 0)))) + (struct.set $hasOOL 16 (local.get 1) (i64.const 1337)) ;; set $OOLnonref + (struct.set $hasOOL 2 (local.get 1) (i64.const 7331)) ;; set $ILnonref + (struct.set $hasOOL 16 (local.get 1) (i64.const 9009)) ;; set $OOLnonref + ) + ;; and the same for ref fields. + (func (export "threeSetsReffy") (param eqref) + (local (ref $hasOOL)) + (local.set 1 (ref.as_non_null (ref.cast (ref null $hasOOL) (local.get 0)))) + (struct.set $hasOOL 17 (local.get 1) (ref.null $meh)) ;; set $OOLref + (struct.set $hasOOL 3 (local.get 1) (ref.null $meh)) ;; set $ILref + (struct.set $hasOOL 17 (local.get 1) (ref.null $meh)) ;; set $OOLref + ) + )`; + let exports = wasmEvalText(txt).exports; +} + +////////////////////////////////////////////////////////////////////////////// +// +// Checks for requests to create structs with more than MaxStructFields, where +// MaxStructFields == 1000. + +function structNewOfManyFields(numFields) { + let defString = "(type $s (struct "; + for (i = 0; i < numFields; i++) { + defString += "(field i32) "; + } + defString += "))"; + + let insnString = "(struct.new $s "; + for (i = 0; i < numFields; i++) { + insnString += "(i32.const 1337) "; + } + insnString += ")"; + + return "(module " + + defString + + " (func (export \"create\") (result eqref) " + + insnString + + "))"; +} + +{ + // 2000 fields is allowable + let exports = wasmEvalText(structNewOfManyFields(2000)).exports; + let s = exports.create(); + assertEq(s, s); +} +{ + // but 2001 is not + assertErrorMessage(() => wasmEvalText(structNewOfManyFields(2001)), + WebAssembly.CompileError, + /too many fields in struct/); +} + +// FIXME: also check struct.new_default, once it is available in both compilers. |