From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- js/src/jit-test/tests/wasm/globals.js | 556 ++++++++++++++++++++++++++++++++++ 1 file changed, 556 insertions(+) create mode 100644 js/src/jit-test/tests/wasm/globals.js (limited to 'js/src/jit-test/tests/wasm/globals.js') diff --git a/js/src/jit-test/tests/wasm/globals.js b/js/src/jit-test/tests/wasm/globals.js new file mode 100644 index 0000000000..186ceb6350 --- /dev/null +++ b/js/src/jit-test/tests/wasm/globals.js @@ -0,0 +1,556 @@ +// |jit-test| test-also=--wasm-extended-const; test-also=--no-wasm-extended-const + +const { Instance, Module, LinkError } = WebAssembly; + +// Locally-defined globals +assertErrorMessage(() => wasmEvalText(`(module (global))`), SyntaxError, /wasm text error/); +// A global field in the text format is valid with an empty expression, but this produces an invalid module +assertErrorMessage(() => wasmEvalText(`(module (global i32))`), WebAssembly.CompileError, /popping value/); +assertErrorMessage(() => wasmEvalText(`(module (global (mut i32)))`), WebAssembly.CompileError, /popping value/); + +// Initializer expressions. +wasmFailValidateText(`(module (global i32 (f32.const 13.37)))`, /type mismatch/); +wasmFailValidateText(`(module (global f64 (f32.const 13.37)))`, /type mismatch/); + +wasmFailValidateText(`(module (global i32 (global.get 0)))`, /out of range/); +wasmFailValidateText(`(module (global i32 (global.get 1)) (global i32 (i32.const 1)))`, /out of range/); + +// Test a well-defined global section. +function testInner(type, initialValue, nextValue, coercion) +{ + var module = wasmEvalText(`(module + (global (mut ${type}) (${type}.const ${initialValue})) + (global ${type} (${type}.const ${initialValue})) + + (func $get (result ${type}) (global.get 0)) + (func $set (param ${type}) (global.set 0 (local.get 0))) + + (func $get_cst (result ${type}) (global.get 1)) + + (export "get" (func $get)) + (export "get_cst" (func $get_cst)) + + (export "set" (func $set)) + )`).exports; + + assertEq(module.get(), coercion(initialValue)); + assertEq(module.set(coercion(nextValue)), undefined); + assertEq(module.get(), coercion(nextValue)); + + assertEq(module.get_cst(), coercion(initialValue)); +} + +testInner('i32', 13, 37, x => x|0); +testInner('f32', 13.37, 0.1989, Math.fround); +testInner('f64', 13.37, 0.1989, x => +x); + +// Extended const stuff +if (wasmExtendedConstEnabled()) { + // Basic global shenanigans + { + const module = wasmEvalText(`(module + ;; -2 * (5 - (-10 + 20)) = 10 + (global i32 (i32.mul (i32.const -2) (i32.sub (i32.const 5) (i32.add (i32.const -10) (i32.const 20))))) + ;; ((1 + 2) - (3 * 4)) = -9 + (global i64 (i64.sub (i64.add (i64.const 1) (i64.const 2)) (i64.mul (i64.const 3) (i64.const 4)))) + + (func (export "get0") (result i32) global.get 0) + (func (export "get1") (result i64) global.get 1) + )`).exports; + + assertEq(module.get0(), 10); + assertEq(module.get1(), -9n); + } + + // Example use of dynamic linking + { + // Make a memory for two dynamically-linked modules to share. Each module gets five pages. + const mem = new WebAssembly.Memory({ initial: 15, maximum: 15 }); + + const mod1 = new WebAssembly.Module(wasmTextToBinary(`(module + (memory (import "env" "memory") 15 15) + (global $memBase (import "env" "__memory_base") i32) + (data (offset (global.get $memBase)) "Hello from module 1.") + (data (offset (i32.add (global.get $memBase) (i32.const 65536))) "Goodbye from module 1.") + )`)); + const instance1 = new WebAssembly.Instance(mod1, { + env: { + memory: mem, + __memory_base: 65536 * 5, // this module's memory starts at page 5 + }, + }); + + const mod2 = new WebAssembly.Module(wasmTextToBinary(`(module + (memory (import "env" "memory") 15 15) + (global $memBase (import "env" "__memory_base") i32) + (data (offset (global.get $memBase)) "Hello from module 2.") + (data (offset (i32.add (global.get $memBase) (i32.const 65536))) "Goodbye from module 2.") + )`)); + const instance2 = new WebAssembly.Instance(mod2, { + env: { + memory: mem, + __memory_base: 65536 * 10, // this module's memory starts at page 10 + }, + }); + + // All four strings should now be present in the memory. + + function assertStringInMem(mem, str, addr) { + const bytes = new Uint8Array(mem.buffer).slice(addr, addr + str.length); + let memStr = String.fromCharCode(...bytes); + assertEq(memStr, str); + } + + assertStringInMem(mem, "Hello from module 1.", 65536 * 5); + assertStringInMem(mem, "Goodbye from module 1.", 65536 * 6); + assertStringInMem(mem, "Hello from module 2.", 65536 * 10); + assertStringInMem(mem, "Goodbye from module 2.", 65536 * 11); + } +} + +// Semantic errors. +wasmFailValidateText(`(module (global (mut i32) (i32.const 1337)) (func (global.set 1 (i32.const 0))))`, /(out of range)|(global index out of bounds)/); +wasmFailValidateText(`(module (global i32 (i32.const 1337)) (func (global.set 0 (i32.const 0))))`, /(can't write an immutable global)|(global is immutable)/); + +// Big module with many variables: test that setting one doesn't overwrite the +// other ones. +function get_set(i, type) { + return ` + (func $get_${i} (result ${type}) (global.get ${i})) + (func $set_${i} (param ${type}) (global.set ${i} (local.get 0))) + `; +} + +var module = wasmEvalText(`(module + (global (mut i32) (i32.const 42)) + (global (mut i32) (i32.const 10)) + (global (mut f32) (f32.const 13.37)) + (global (mut f64) (f64.const 13.37)) + (global (mut i32) (i32.const -18)) + + ${get_set(0, 'i32')} + ${get_set(1, 'i32')} + ${get_set(2, 'f32')} + ${get_set(3, 'f64')} + ${get_set(4, 'i32')} + + (export "get0" (func $get_0)) (export "set0" (func $set_0)) + (export "get1" (func $get_1)) (export "set1" (func $set_1)) + (export "get2" (func $get_2)) (export "set2" (func $set_2)) + (export "get3" (func $get_3)) (export "set3" (func $set_3)) + (export "get4" (func $get_4)) (export "set4" (func $set_4)) +)`).exports; + +let values = [42, 10, Math.fround(13.37), 13.37, -18]; +let nextValues = [13, 37, Math.fround(-17.89), 9.3, -13]; +for (let i = 0; i < 5; i++) { + assertEq(module[`get${i}`](), values[i]); + assertEq(module[`set${i}`](nextValues[i]), undefined); + assertEq(module[`get${i}`](), nextValues[i]); + for (let j = 0; j < 5; j++) { + if (i === j) + continue; + assertEq(module[`get${j}`](), values[j]); + } + assertEq(module[`set${i}`](values[i]), undefined); + assertEq(module[`get${i}`](), values[i]); +} + +// Initializer expressions can also be used in elem section initializers. +wasmFailValidateText(`(module (import "globals" "a" (global f32)) (table 4 funcref) (elem (global.get 0) $f) (func $f))`, /type mismatch/); + +module = wasmEvalText(`(module + (import "globals" "a" (global i32)) + (table (export "tbl") 4 funcref) + (elem (global.get 0) $f) + (func $f) + (export "f" (func $f)) +)`, { + globals: { + a: 1 + } +}).exports; +assertEq(module.f, module.tbl.get(1)); + +// Import/export semantics. +module = wasmEvalText(`(module + (import "globals" "x" (global $g i32)) + (func $get (result i32) (global.get $g)) + (export "getter" (func $get)) + (export "value" (global 0)) +)`, { globals: {x: 42} }).exports; + +assertEq(module.getter(), 42); + +// assertEq() will not trigger @@toPrimitive, so we must have a cast here. +assertEq(Number(module.value), 42); + +// Can only import numbers (no implicit coercions). +module = new Module(wasmTextToBinary(`(module + (global (import "globs" "i32") i32) + (global (import "globs" "f32") f32) + (global (import "globs" "f64") f32) +)`)); + +const assertLinkFails = (m, imp, err) => { + assertErrorMessage(() => new Instance(m, imp), LinkError, err); +} + +var imp = { + globs: { + i32: 0, + f32: Infinity, + f64: NaN + } +}; + +let i = new Instance(module, imp); + +for (let v of [ + null, + {}, + "42", + /not a number/, + false, + undefined, + Symbol(), + { valueOf() { return 42; } } +]) { + imp.globs.i32 = v; + assertLinkFails(module, imp, /not a Number/); + + imp.globs.i32 = 0; + imp.globs.f32 = v; + assertLinkFails(module, imp, /not a Number/); + + imp.globs.f32 = Math.fround(13.37); + imp.globs.f64 = v; + assertLinkFails(module, imp, /not a Number/); + + imp.globs.f64 = 13.37; +} + +// Imported globals and locally defined globals use the same index space. +module = wasmEvalText(`(module + (import "globals" "x" (global i32)) + (global i32 (i32.const 1337)) + (export "imported" (global 0)) + (export "defined" (global 1)) +)`, { globals: {x: 42} }).exports; + +assertEq(Number(module.imported), 42); +assertEq(Number(module.defined), 1337); + +// Initializer expressions can reference an imported immutable global. +wasmFailValidateText(`(module (global f32 (f32.const 13.37)) (global i32 (global.get 0)))`, /must reference a global immutable import/); +wasmFailValidateText(`(module (global (mut f32) (f32.const 13.37)) (global i32 (global.get 0)))`, /must reference a global immutable import/); +wasmFailValidateText(`(module (global (mut i32) (i32.const 0)) (global i32 (global.get 0)))`, /must reference a global immutable import/); + +wasmFailValidateText(`(module (import "globals" "a" (global f32)) (global i32 (global.get 0)))`, /type mismatch/); + +function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = assertEq) { + var module = wasmEvalText(`(module + (import "globals" "a" (global ${type})) + + (global $glob_mut (mut ${type}) (global.get 0)) + (global $glob_imm ${type} (global.get 0)) + + (func $get0 (result ${type}) (global.get 0)) + + (func $get1 (result ${type}) (global.get 1)) + (func $set1 (param ${type}) (global.set 1 (local.get 0))) + + (func $get_cst (result ${type}) (global.get 2)) + + (export "get0" (func $get0)) + (export "get1" (func $get1)) + (export "get_cst" (func $get_cst)) + + (export "set1" (func $set1)) + (export "global_imm" (global $glob_imm)) + )`, { + globals: { + a: coercion(initialValue) + } + }).exports; + + assertFunc(module.get0(), coercion(initialValue)); + assertFunc(module.get1(), coercion(initialValue)); + assertFunc(Number(module.global_imm), coercion(initialValue)); + + assertEq(module.set1(coercion(nextValue)), undefined); + assertFunc(module.get1(), coercion(nextValue)); + assertFunc(module.get0(), coercion(initialValue)); + assertFunc(Number(module.global_imm), coercion(initialValue)); + + assertFunc(module.get_cst(), coercion(initialValue)); +} + +testInitExpr('i32', 13, 37, x => x|0); +testInitExpr('f32', 13.37, 0.1989, Math.fround); +testInitExpr('f64', 13.37, 0.1989, x => +x); + +// Int64. + +// Import and export + +// Test inner +var initialValue = '0x123456789abcdef0'; +var nextValue = '0x531642753864975F'; +wasmAssert(`(module + (global (mut i64) (i64.const ${initialValue})) + (global i64 (i64.const ${initialValue})) + (func $get (result i64) (global.get 0)) + (func $set (param i64) (global.set 0 (local.get 0))) + (func $get_cst (result i64) (global.get 1)) + (export "get" (func $get)) + (export "get_cst" (func $get_cst)) + (export "set" (func $set)) +)`, [ + {type: 'i64', func: '$get', expected: initialValue}, + {type: 'i64', func: '$set', args: [`i64.const ${nextValue}`]}, + {type: 'i64', func: '$get', expected: nextValue}, + {type: 'i64', func: '$get_cst', expected: initialValue}, +]); + +// Custom NaN. +{ + let dv = new DataView(new ArrayBuffer(8)); + module = wasmEvalText(`(module + (global $g f64 (f64.const -nan:0xe7ffff1591120)) + (global $h f32 (f32.const -nan:0x651234)) + (export "nan64" (global $g))(export "nan32" (global $h)) + )`, {}).exports; + + dv.setFloat64(0, module.nan64, true); + assertEq(dv.getUint32(4, true), 0x7ff80000); + assertEq(dv.getUint32(0, true), 0x00000000); + + dv.setFloat32(0, module.nan32, true); + assertEq(dv.getUint32(0, true), 0x7fc00000); +} + +// WebAssembly.Global +{ + const Global = WebAssembly.Global; + + // These types should work: + assertEq(new Global({value: "i32"}) instanceof Global, true); + assertEq(new Global({value: "f32"}) instanceof Global, true); + assertEq(new Global({value: "f64"}) instanceof Global, true); + assertEq(new Global({value: "i64"}) instanceof Global, true); // No initial value works + + // Coercion of init value; ".value" accessor + assertEq((new Global({value: "i32"}, 3.14)).value, 3); + assertEq((new Global({value: "f32"}, { valueOf: () => 33.5 })).value, 33.5); + assertEq((new Global({value: "f64"}, "3.25")).value, 3.25); + + // Nothing special about NaN, it coerces just fine + assertEq((new Global({value: "i32"}, NaN)).value, 0); + + // The default init value is zero. + assertEq((new Global({value: "i32"})).value, 0); + assertEq((new Global({value: "f32"})).value, 0); + assertEq((new Global({value: "f64"})).value, 0); + let mod = wasmEvalText(`(module + (import "" "g" (global i64)) + (func (export "f") (result i32) + (i64.eqz (global.get 0))))`, + {"":{g: new Global({value: "i64"})}}); + assertEq(mod.exports.f(), 1); + + { + // "value" is enumerable + let x = new Global({value: "i32"}); + let s = ""; + for ( let i in x ) + s = s + i + ","; + if (getBuildConfiguration().release_or_beta) { + assertEq(s, "valueOf,value,"); + } else { + assertEq(s, "type,valueOf,value,"); + } + } + + // "value" is defined on the prototype, not on the object + assertEq("value" in Global.prototype, true); + + // Can't set the value of an immutable global + assertErrorMessage(() => (new Global({value: "i32"})).value = 10, + TypeError, + /can't set value of immutable global/); + + { + // Can set the value of a mutable global + let g = new Global({value: "i32", mutable: true}, 37); + g.value = 10; + assertEq(g.value, 10); + } + + { + // Misc internal conversions + let g = new Global({value: "i32"}, 42); + + // valueOf + assertEq(g - 5, 37); + + // @@toStringTag + assertEq(g.toString(), "[object WebAssembly.Global]"); + } + + { + // An exported global should appear as a Global instance: + let i = wasmEvalText(`(module (global (export "g") i32 (i32.const 42)))`); + + assertEq(typeof i.exports.g, "object"); + assertEq(i.exports.g instanceof Global, true); + + // An exported global can be imported into another instance even if + // it is an object: + let j = wasmEvalText(`(module + (global (import "" "g") i32) + (func (export "f") (result i32) + (global.get 0)))`, + { "": { "g": i.exports.g }}); + + // And when it is then accessed it has the right value: + assertEq(j.exports.f(), 42); + } + + // Identity of Global objects (independent of mutablity). + { + // When a global is exported twice, the two objects are the same. + let i = wasmEvalText(`(module + (global i32 (i32.const 0)) + (export "a" (global 0)) + (export "b" (global 0)))`); + assertEq(i.exports.a, i.exports.b); + + // When a global is imported and then exported, the exported object is + // the same as the imported object. + let j = wasmEvalText(`(module + (import "" "a" (global i32)) + (export "x" (global 0)))`, + { "": {a: i.exports.a}}); + + assertEq(i.exports.a, j.exports.x); + + // When a global is imported twice (ie aliased) and then exported twice, + // the exported objects are the same, and are also the same as the + // imported object. + let k = wasmEvalText(`(module + (import "" "a" (global i32)) + (import "" "b" (global i32)) + (export "x" (global 0)) + (export "y" (global 1)))`, + { "": {a: i.exports.a, + b: i.exports.a}}); + + assertEq(i.exports.a, k.exports.x); + assertEq(k.exports.x, k.exports.y); + } + + // Mutability + { + let i = wasmEvalText(`(module + (global (export "g") (mut i32) (i32.const 37)) + (func (export "getter") (result i32) + (global.get 0)) + (func (export "setter") (param i32) + (global.set 0 (local.get 0))))`); + + let j = wasmEvalText(`(module + (import "" "g" (global (mut i32))) + (func (export "getter") (result i32) + (global.get 0)) + (func (export "setter") (param i32) + (global.set 0 (local.get 0))))`, + {"": {g: i.exports.g}}); + + // Initial values + assertEq(i.exports.g.value, 37); + assertEq(i.exports.getter(), 37); + assertEq(j.exports.getter(), 37); + + // Set in i, observe everywhere + i.exports.setter(42); + + assertEq(i.exports.g.value, 42); + assertEq(i.exports.getter(), 42); + assertEq(j.exports.getter(), 42); + + // Set in j, observe everywhere + j.exports.setter(78); + + assertEq(i.exports.g.value, 78); + assertEq(i.exports.getter(), 78); + assertEq(j.exports.getter(), 78); + + // Set on global object, observe everywhere + i.exports.g.value = 197; + + assertEq(i.exports.g.value, 197); + assertEq(i.exports.getter(), 197); + assertEq(j.exports.getter(), 197); + } + + // Mutability of import declaration and imported value have to match + { + const mutErr = /imported global mutability mismatch/; + + let m1 = new Module(wasmTextToBinary(`(module + (import "m" "g" (global i32)))`)); + + // Mutable Global matched to immutable import + let gm = new Global({value: "i32", mutable: true}, 42); + assertErrorMessage(() => new Instance(m1, {m: {g: gm}}), + LinkError, + mutErr); + + let m2 = new Module(wasmTextToBinary(`(module + (import "m" "g" (global (mut i32))))`)); + + // Immutable Global matched to mutable import + let gi = new Global({value: "i32", mutable: false}, 42); + assertErrorMessage(() => new Instance(m2, {m: {g: gi}}), + LinkError, + mutErr); + + // Constant value is the same as immutable Global + assertErrorMessage(() => new Instance(m2, {m: {g: 42}}), + LinkError, + mutErr); + } + + // TEST THIS LAST + + // "value" is deletable + assertEq(delete Global.prototype.value, true); + assertEq("value" in Global.prototype, false); + + // ADD NO MORE TESTS HERE! +} + +// Standard wat syntax: the parens around the initializer expression are +// optional. +{ + let i1 = wasmEvalText( + `(module + (global $g i32 i32.const 37) + (func (export "f") (result i32) (global.get $g)))`); + assertEq(i1.exports.f(), 37); + + let i2 = wasmEvalText( + `(module + (global $g (mut f64) f64.const 42.0) + (func (export "f") (result f64) (global.get $g)))`); + assertEq(i2.exports.f(), 42); + + let i3 = wasmEvalText( + `(module + (global $x (import "m" "x") i32) + (global $g i32 global.get $x) + (func (export "f") (result i32) (global.get $g)))`, + {m:{x:86}}); + assertEq(i3.exports.f(), 86); +} -- cgit v1.2.3