diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/jit-test/tests/wasm/ref-types/externref.js | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/wasm/ref-types/externref.js')
-rw-r--r-- | js/src/jit-test/tests/wasm/ref-types/externref.js | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/ref-types/externref.js b/js/src/jit-test/tests/wasm/ref-types/externref.js new file mode 100644 index 0000000000..37a9de1da0 --- /dev/null +++ b/js/src/jit-test/tests/wasm/ref-types/externref.js @@ -0,0 +1,482 @@ +// Dummy constructor. +function Baguette(calories) { + this.calories = calories; +} + +// Type checking. + +const { validate, CompileError } = WebAssembly; + +assertErrorMessage(() => wasmEvalText(`(module + (func (result externref) + i32.const 42 + ) +)`), CompileError, mismatchError('i32', 'externref')); + +assertErrorMessage(() => wasmEvalText(`(module + (func (result externref) + i32.const 0 + ref.null extern + i32.const 42 + select (result externref) + ) +)`), CompileError, /type mismatch/); + +assertErrorMessage(() => wasmEvalText(`(module + (func (result i32) + ref.null extern + if + i32.const 42 + end + ) +)`), CompileError, mismatchError('externref', 'i32')); + + +// Basic compilation tests. + +let simpleTests = [ + "(module (func (drop (ref.null extern))))", + "(module (func $test (local externref)))", + "(module (func $test (param externref)))", + "(module (func $test (result externref) (ref.null extern)))", + "(module (func $test (block (result externref) (unreachable)) unreachable))", + "(module (func $test (result i32) (local externref) (ref.is_null (local.get 0))))", + `(module (import "a" "b" (func (param externref))))`, + `(module (import "a" "b" (func (result externref))))`, + `(module (global externref (ref.null extern)))`, + `(module (global (mut externref) (ref.null extern)))`, +]; + +for (let src of simpleTests) { + wasmEvalText(src, {a:{b(){}}}); + assertEq(validate(wasmTextToBinary(src)), true); +} + +// Basic behavioral tests. + +let { exports } = wasmEvalText(`(module + (func (export "is_null") (result i32) + ref.null extern + ref.is_null + ) + + (func $sum (param i32) (result i32) + local.get 0 + i32.const 42 + i32.add + ) + + (func (export "is_null_spill") (result i32) + ref.null extern + i32.const 58 + call $sum + drop + ref.is_null + ) + + (func (export "is_null_local") (result i32) (local externref) + ref.null extern + local.set 0 + i32.const 58 + call $sum + drop + local.get 0 + ref.is_null + ) + )`); + +assertEq(exports.is_null(), 1); +assertEq(exports.is_null_spill(), 1); +assertEq(exports.is_null_local(), 1); + +// ExternRef param and result in wasm functions. + +exports = wasmEvalText(`(module + (func (export "is_null") (param $ref externref) (result i32) + local.get $ref + ref.is_null + ) + + (func (export "ref_or_null") (param $ref externref) (param $selector i32) (result externref) + local.get $ref + ref.null extern + local.get $selector + select (result externref) + ) + + (func $recursive (export "nested") (param $ref externref) (param $i i32) (result externref) + ;; i == 10 => ret $ref + local.get $i + i32.const 10 + i32.eq + if + local.get $ref + return + end + + local.get $ref + + local.get $i + i32.const 1 + i32.add + + call $recursive + ) +)`).exports; + +assertEq(exports.is_null(undefined), 0); +assertEq(exports.is_null(null), 1); +assertEq(exports.is_null({}), 0); +assertEq(exports.is_null("hi"), 0); +assertEq(exports.is_null(3), 0); +assertEq(exports.is_null(3.5), 0); +assertEq(exports.is_null(true), 0); +assertEq(exports.is_null(Symbol("croissant")), 0); +assertEq(exports.is_null(new Baguette(100)), 0); + +let baguette = new Baguette(42); +assertEq(exports.ref_or_null(null, 0), null); +assertEq(exports.ref_or_null(baguette, 0), null); + +let ref = exports.ref_or_null(baguette, 1); +assertEq(ref, baguette); +assertEq(ref.calories, baguette.calories); + +ref = exports.nested(baguette, 0); +assertEq(ref, baguette); +assertEq(ref.calories, baguette.calories); + +// Make sure grow-memory isn't blocked by the lack of gc. +(function() { + assertEq(wasmEvalText(`(module + (memory 0 64) + (func (export "f") (param externref) (result i32) + i32.const 10 + memory.grow + drop + memory.size + ) +)`).exports.f({}), 10); +})(); + +// More interesting use cases about control flow joins. + +function assertJoin(body) { + let val = { i: -1 }; + assertEq(wasmEvalText(`(module + (func (export "test") (param $ref externref) (param $i i32) (result externref) + ${body} + ) + )`).exports.test(val), val); + assertEq(val.i, -1); +} + +assertJoin("(block (result externref) local.get $ref)"); +assertJoin("(block $out (result externref) local.get $ref br $out)"); +assertJoin("(loop (result externref) local.get $ref)"); + +assertJoin(`(block $out (result externref) (loop $top (result externref) + local.get $i + i32.const 1 + i32.add + local.tee $i + i32.const 10 + i32.eq + if + local.get $ref + return + end + br $top)) +`); + +assertJoin(`(block $out (loop $top + local.get $i + i32.const 1 + i32.add + local.tee $i + i32.const 10 + i32.le_s + if + br $top + else + local.get $ref + return + end + )) unreachable +`); + +assertJoin(`(block $out (result externref) (loop $top + local.get $ref + local.get $i + i32.const 1 + i32.add + local.tee $i + i32.const 10 + i32.eq + br_if $out + br $top + ) unreachable) +`); + +assertJoin(`(block $out (result externref) (block $unreachable (result externref) (loop $top + local.get $ref + local.get $i + i32.const 1 + i32.add + local.tee $i + br_table $unreachable $out + ) unreachable)) +`); + +let x = { i: 42 }, y = { f: 53 }; +exports = wasmEvalText(`(module + (func (export "test") (param $lhs externref) (param $rhs externref) (param $i i32) (result externref) + local.get $lhs + local.get $rhs + local.get $i + select (result externref) + ) +)`).exports; + +let result = exports.test(x, y, 0); +assertEq(result, y); +assertEq(result.i, undefined); +assertEq(result.f, 53); +assertEq(x.i, 42); + +result = exports.test(x, y, 1); +assertEq(result, x); +assertEq(result.i, 42); +assertEq(result.f, undefined); +assertEq(y.f, 53); + +// ExternRef in params/result of imported functions. + +let firstBaguette = new Baguette(13), + secondBaguette = new Baguette(37); + +let imports = { + i: 0, + myBaguette: null, + funcs: { + param(x) { + if (this.i === 0) { + assertEq(x, firstBaguette); + assertEq(x.calories, 13); + assertEq(secondBaguette !== null, true); + } else if (this.i === 1 || this.i === 2) { + assertEq(x, secondBaguette); + assertEq(x.calories, 37); + assertEq(firstBaguette !== null, true); + } else if (this.i === 3) { + assertEq(x, null); + } else { + firstBaguette = null; + secondBaguette = null; + gc(); // evil mode + } + this.i++; + }, + ret() { + return imports.myBaguette; + } + } +}; + +exports = wasmEvalText(`(module + (import "funcs" "ret" (func $ret (result externref))) + (import "funcs" "param" (func $param (param externref))) + + (func (export "param") (param $x externref) (param $y externref) + local.get $y + local.get $x + call $param + call $param + ) + + (func (export "ret") (result externref) + call $ret + ) +)`, imports).exports; + +exports.param(firstBaguette, secondBaguette); +exports.param(secondBaguette, null); +exports.param(firstBaguette, secondBaguette); + +imports.myBaguette = null; +assertEq(exports.ret(), null); + +imports.myBaguette = new Baguette(1337); +assertEq(exports.ret(), imports.myBaguette); + +// Check lazy stubs generation. + +exports = wasmEvalText(`(module + (import "funcs" "mirror" (func $mirror (param externref) (result externref))) + (import "funcs" "augment" (func $augment (param externref) (result externref))) + + (global $count_f (mut i32) (i32.const 0)) + (global $count_g (mut i32) (i32.const 0)) + + (func $f (param $param externref) (result externref) + i32.const 1 + global.get $count_f + i32.add + global.set $count_f + + local.get $param + call $augment + ) + + (func $g (param $param externref) (result externref) + i32.const 1 + global.get $count_g + i32.add + global.set $count_g + + local.get $param + call $mirror + ) + + (table (export "table") 10 funcref) + (elem (i32.const 0) $f $g $mirror $augment) + (type $table_type (func (param externref) (result externref))) + + (func (export "call_indirect") (param $i i32) (param $ref externref) (result externref) + local.get $ref + local.get $i + call_indirect (type $table_type) + ) + + (func (export "count_f") (result i32) global.get $count_f) + (func (export "count_g") (result i32) global.get $count_g) +)`, { + funcs: { + mirror(x) { + return x; + }, + augment(x) { + x.i++; + x.newProp = "hello"; + return x; + } + } +}).exports; + +x = { i: 19 }; +assertEq(exports.table.get(0)(x), x); +assertEq(x.i, 20); +assertEq(x.newProp, "hello"); +assertEq(exports.count_f(), 1); +assertEq(exports.count_g(), 0); + +x = { i: 21 }; +assertEq(exports.table.get(1)(x), x); +assertEq(x.i, 21); +assertEq(typeof x.newProp, "undefined"); +assertEq(exports.count_f(), 1); +assertEq(exports.count_g(), 1); + +x = { i: 22 }; +assertEq(exports.table.get(2)(x), x); +assertEq(x.i, 22); +assertEq(typeof x.newProp, "undefined"); +assertEq(exports.count_f(), 1); +assertEq(exports.count_g(), 1); + +x = { i: 23 }; +assertEq(exports.table.get(3)(x), x); +assertEq(x.i, 24); +assertEq(x.newProp, "hello"); +assertEq(exports.count_f(), 1); +assertEq(exports.count_g(), 1); + +// Globals. + +// ExternRef globals in wasm modules. + +assertErrorMessage(() => wasmEvalText(`(module (global (import "glob" "externref") externref))`, { glob: { externref: new WebAssembly.Global({ value: 'i32' }, 42) } }), + WebAssembly.LinkError, + /imported global type mismatch/); + +assertErrorMessage(() => wasmEvalText(`(module (global (import "glob" "i32") i32))`, { glob: { i32: {} } }), + WebAssembly.LinkError, + /import object field 'i32' is not a Number/); + +imports = { + constants: { + imm_null: null, + imm_bread: new Baguette(321), + mut_null: new WebAssembly.Global({ value: "externref", mutable: true }, null), + mut_bread: new WebAssembly.Global({ value: "externref", mutable: true }, new Baguette(123)) + } +}; + +exports = wasmEvalText(`(module + (global $g_imp_imm_null (import "constants" "imm_null") externref) + (global $g_imp_imm_bread (import "constants" "imm_bread") externref) + + (global $g_imp_mut_null (import "constants" "mut_null") (mut externref)) + (global $g_imp_mut_bread (import "constants" "mut_bread") (mut externref)) + + (global $g_imm_null externref (ref.null extern)) + (global $g_imm_getglob externref (global.get $g_imp_imm_bread)) + (global $g_mut (mut externref) (ref.null extern)) + + (func (export "imm_null") (result externref) global.get $g_imm_null) + (func (export "imm_getglob") (result externref) global.get $g_imm_getglob) + + (func (export "imp_imm_null") (result externref) global.get $g_imp_imm_null) + (func (export "imp_imm_bread") (result externref) global.get $g_imp_imm_bread) + (func (export "imp_mut_null") (result externref) global.get $g_imp_mut_null) + (func (export "imp_mut_bread") (result externref) global.get $g_imp_mut_bread) + + (func (export "set_imp_null") (param externref) local.get 0 global.set $g_imp_mut_null) + (func (export "set_imp_bread") (param externref) local.get 0 global.set $g_imp_mut_bread) + + (func (export "set_mut") (param externref) local.get 0 global.set $g_mut) + (func (export "get_mut") (result externref) global.get $g_mut) +)`, imports).exports; + +assertEq(exports.imp_imm_null(), imports.constants.imm_null); +assertEq(exports.imp_imm_bread(), imports.constants.imm_bread); + +assertEq(exports.imm_null(), null); +assertEq(exports.imm_getglob(), imports.constants.imm_bread); + +assertEq(exports.imp_mut_null(), imports.constants.mut_null.value); +assertEq(exports.imp_mut_bread(), imports.constants.mut_bread.value); + +let brandNewBaguette = new Baguette(1000); +exports.set_imp_null(brandNewBaguette); +assertEq(exports.imp_mut_null(), brandNewBaguette); +assertEq(exports.imp_mut_bread(), imports.constants.mut_bread.value); + +exports.set_imp_bread(null); +assertEq(exports.imp_mut_null(), brandNewBaguette); +assertEq(exports.imp_mut_bread(), null); + +assertEq(exports.get_mut(), null); +let glutenFreeBaguette = new Baguette("calories-free bread"); +exports.set_mut(glutenFreeBaguette); +assertEq(exports.get_mut(), glutenFreeBaguette); +assertEq(exports.get_mut().calories, "calories-free bread"); + +// Make sure that dead code doesn't prevent compilation. +wasmEvalText( + `(module + (func + (return) + (ref.null extern) + (drop) + ) + )`); + +wasmEvalText( + `(module + (func (param externref) + (return) + (ref.is_null (local.get 0)) + (drop) + ) + )`); |