Source code
Revision control
Copy as Markdown
Other Tools
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);
// 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);
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);
}