Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

1
test/fixtures/wpt/wasm/jsapi/META.yml vendored Normal file
View File

@ -0,0 +1 @@
spec: https://webassembly.github.io/spec/js-api/

View File

@ -0,0 +1,100 @@
function assert_function_name(fn, name, description) {
const propdesc = Object.getOwnPropertyDescriptor(fn, "name");
assert_equals(typeof propdesc, "object", `${description} should have name property`);
assert_false(propdesc.writable, "writable", `${description} name should not be writable`);
assert_false(propdesc.enumerable, "enumerable", `${description} name should not be enumerable`);
assert_true(propdesc.configurable, "configurable", `${description} name should be configurable`);
assert_equals(propdesc.value, name, `${description} name should be ${name}`);
}
function assert_function_length(fn, length, description) {
const propdesc = Object.getOwnPropertyDescriptor(fn, "length");
assert_equals(typeof propdesc, "object", `${description} should have length property`);
assert_false(propdesc.writable, "writable", `${description} length should not be writable`);
assert_false(propdesc.enumerable, "enumerable", `${description} length should not be enumerable`);
assert_true(propdesc.configurable, "configurable", `${description} length should be configurable`);
assert_equals(propdesc.value, length, `${description} length should be ${length}`);
}
function assert_exported_function(fn, { name, length }, description) {
if (WebAssembly.Function === undefined) {
assert_equals(Object.getPrototypeOf(fn), Function.prototype,
`${description}: prototype`);
} else {
assert_equals(Object.getPrototypeOf(fn), WebAssembly.Function.prototype,
`${description}: prototype`);
}
assert_function_name(fn, name, description);
assert_function_length(fn, length, description);
}
function assert_Instance(instance, expected_exports) {
assert_equals(Object.getPrototypeOf(instance), WebAssembly.Instance.prototype,
"prototype");
assert_true(Object.isExtensible(instance), "extensible");
assert_equals(instance.exports, instance.exports, "exports should be idempotent");
const exports = instance.exports;
assert_equals(Object.getPrototypeOf(exports), null, "exports prototype");
assert_false(Object.isExtensible(exports), "extensible exports");
assert_array_equals(Object.keys(exports), Object.keys(expected_exports), "matching export keys");
for (const [key, expected] of Object.entries(expected_exports)) {
const property = Object.getOwnPropertyDescriptor(exports, key);
assert_equals(typeof property, "object", `${key} should be present`);
assert_false(property.writable, `${key}: writable`);
assert_true(property.enumerable, `${key}: enumerable`);
assert_false(property.configurable, `${key}: configurable`);
const actual = property.value;
assert_true(Object.isExtensible(actual), `${key}: extensible`);
switch (expected.kind) {
case "function":
assert_exported_function(actual, expected, `value of ${key}`);
break;
case "global":
assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype,
`value of ${key}: prototype`);
assert_equals(actual.value, expected.value, `value of ${key}: value`);
assert_equals(actual.valueOf(), expected.value, `value of ${key}: valueOf()`);
break;
case "memory":
assert_equals(Object.getPrototypeOf(actual), WebAssembly.Memory.prototype,
`value of ${key}: prototype`);
assert_equals(Object.getPrototypeOf(actual.buffer), ArrayBuffer.prototype,
`value of ${key}: prototype of buffer`);
assert_equals(actual.buffer.byteLength, 0x10000 * expected.size, `value of ${key}: size of buffer`);
const array = new Uint8Array(actual.buffer);
assert_equals(array[0], 0, `value of ${key}: first element of buffer`);
assert_equals(array[array.byteLength - 1], 0, `value of ${key}: last element of buffer`);
break;
case "table":
assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype,
`value of ${key}: prototype`);
assert_equals(actual.length, expected.length, `value of ${key}: length of table`);
break;
}
}
}
function assert_WebAssemblyInstantiatedSource(actual, expected_exports={}) {
assert_equals(Object.getPrototypeOf(actual), Object.prototype,
"Prototype");
assert_true(Object.isExtensible(actual), "Extensibility");
const module = Object.getOwnPropertyDescriptor(actual, "module");
assert_equals(typeof module, "object", "module: type of descriptor");
assert_true(module.writable, "module: writable");
assert_true(module.enumerable, "module: enumerable");
assert_true(module.configurable, "module: configurable");
assert_equals(Object.getPrototypeOf(module.value), WebAssembly.Module.prototype,
"module: prototype");
const instance = Object.getOwnPropertyDescriptor(actual, "instance");
assert_equals(typeof instance, "object", "instance: type of descriptor");
assert_true(instance.writable, "instance: writable");
assert_true(instance.enumerable, "instance: enumerable");
assert_true(instance.configurable, "instance: configurable");
assert_Instance(instance.value, expected_exports);
}

View File

@ -0,0 +1,185 @@
/**
* `t` should be a function that takes at least three arguments:
*
* - the name of the test;
* - the expected error (to be passed to `assert_throws_js`);
* - a function that takes a `WasmModuleBuilder` and initializes it;
* - (optionally) an options object.
*
* The function is expected to create a test that checks if instantiating a
* module with the result of the `WasmModuleBuilder` and the options object
* (if any) yields the correct error.
*/
function test_bad_imports(t) {
function value_type(type) {
switch (type) {
case "i32": return kWasmI32;
case "i64": return kWasmI64;
case "f32": return kWasmF32;
case "f64": return kWasmF64;
default: throw new TypeError(`Unexpected type ${type}`);
}
}
for (const value of [null, true, "", Symbol(), 1, 0.1, NaN]) {
t(`Non-object imports argument: ${format_value(value)}`,
TypeError,
builder => {},
value);
}
for (const value of [undefined, null, true, "", Symbol(), 1, 0.1, NaN]) {
const imports = {
"module": value,
};
t(`Non-object module: ${format_value(value)}`,
TypeError,
builder => {
builder.addImport("module", "fn", kSig_v_v);
},
imports);
}
t(`Missing imports argument`,
TypeError,
builder => {
builder.addImport("module", "fn", kSig_v_v);
});
for (const [value, name] of [[undefined, "undefined"], [{}, "empty object"], [{ "module\0": null }, "wrong property"]]) {
t(`Imports argument with missing property: ${name}`,
TypeError,
builder => {
builder.addImport("module", "fn", kSig_v_v);
},
value);
}
for (const value of [undefined, null, true, "", Symbol(), 1, 0.1, NaN, {}]) {
t(`Importing a function with an incorrectly-typed value: ${format_value(value)}`,
WebAssembly.LinkError,
builder => {
builder.addImport("module", "fn", kSig_v_v);
},
{
"module": {
"fn": value,
},
});
}
const nonGlobals = [
[undefined],
[null],
[true],
[""],
[Symbol()],
[{}, "plain object"],
[WebAssembly.Global, "WebAssembly.Global"],
[WebAssembly.Global.prototype, "WebAssembly.Global.prototype"],
[Object.create(WebAssembly.Global.prototype), "Object.create(WebAssembly.Global.prototype)"],
];
for (const type of ["i32", "i64", "f32", "f64"]) {
const extendedNonGlobals = nonGlobals.concat([
type === "i64" ? [0, "Number"] : [0n, "BigInt"],
[new WebAssembly.Global({value: type === "f32" ? "f64" : "f32"}), "WebAssembly.Global object (wrong value type)"],
]);
for (const [value, name = format_value(value)] of extendedNonGlobals) {
t(`Importing an ${type} global with an incorrectly-typed value: ${name}`,
WebAssembly.LinkError,
builder => {
builder.addImportedGlobal("module", "global", value_type(type));
},
{
"module": {
"global": value,
},
});
}
}
for (const type of ["i32", "i64", "f32", "f64"]) {
const value = type === "i64" ? 0n : 0;
t(`Importing an ${type} mutable global with a primitive value`,
WebAssembly.LinkError,
builder => {
builder.addImportedGlobal("module", "global", value_type(type), true);
},
{
"module": {
"global": value,
},
});
const global = new WebAssembly.Global({ "value": type }, value);
t(`Importing an ${type} mutable global with an immutable Global object`,
WebAssembly.LinkError,
builder => {
builder.addImportedGlobal("module", "global", value_type(type), true);
},
{
"module": {
"global": global,
},
});
}
const nonMemories = [
[undefined],
[null],
[true],
[""],
[Symbol()],
[1],
[0.1],
[NaN],
[{}, "plain object"],
[WebAssembly.Memory, "WebAssembly.Memory"],
[WebAssembly.Memory.prototype, "WebAssembly.Memory.prototype"],
[Object.create(WebAssembly.Memory.prototype), "Object.create(WebAssembly.Memory.prototype)"],
[new WebAssembly.Memory({"initial": 256}), "WebAssembly.Memory object (too large)"],
];
for (const [value, name = format_value(value)] of nonMemories) {
t(`Importing memory with an incorrectly-typed value: ${name}`,
WebAssembly.LinkError,
builder => {
builder.addImportedMemory("module", "memory", 0, 128);
},
{
"module": {
"memory": value,
},
});
}
const nonTables = [
[undefined],
[null],
[true],
[""],
[Symbol()],
[1],
[0.1],
[NaN],
[{}, "plain object"],
[WebAssembly.Table, "WebAssembly.Table"],
[WebAssembly.Table.prototype, "WebAssembly.Table.prototype"],
[Object.create(WebAssembly.Table.prototype), "Object.create(WebAssembly.Table.prototype)"],
[new WebAssembly.Table({"element": "anyfunc", "initial": 256}), "WebAssembly.Table object (too large)"],
];
for (const [value, name = format_value(value)] of nonTables) {
t(`Importing table with an incorrectly-typed value: ${name}`,
WebAssembly.LinkError,
builder => {
builder.addImportedTable("module", "table", 0, 128);
},
{
"module": {
"table": value,
},
});
}
}

View File

@ -0,0 +1,85 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
function assert_Module(module) {
assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype,
"Prototype");
assert_true(Object.isExtensible(module), "Extensibility");
}
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
promise_test(t => {
return promise_rejects_js(t, TypeError, WebAssembly.compile());
}, "Missing argument");
promise_test(t => {
const invalidArguments = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
ArrayBuffer,
ArrayBuffer.prototype,
Array.from(emptyModuleBinary),
];
return Promise.all(invalidArguments.map(argument => {
return promise_rejects_js(t, TypeError, WebAssembly.compile(argument),
`compile(${format_value(argument)})`);
}));
}, "Invalid arguments");
promise_test(() => {
const fn = WebAssembly.compile;
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly,
];
return Promise.all(thisValues.map(thisValue => {
return fn.call(thisValue, emptyModuleBinary).then(assert_Module);
}));
}, "Branding");
test(() => {
const promise = WebAssembly.compile(emptyModuleBinary);
assert_equals(Object.getPrototypeOf(promise), Promise.prototype, "prototype");
assert_true(Object.isExtensible(promise), "extensibility");
}, "Promise type");
promise_test(t => {
const buffer = new Uint8Array();
return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compile(buffer));
}, "Empty buffer");
promise_test(t => {
const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0]));
return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compile(buffer));
}, "Invalid code");
promise_test(() => {
return WebAssembly.compile(emptyModuleBinary).then(assert_Module);
}, "Result type");
promise_test(() => {
return WebAssembly.compile(emptyModuleBinary, {}).then(assert_Module);
}, "Stray argument");
promise_test(() => {
const buffer = new WasmModuleBuilder().toBuffer();
assert_equals(buffer[0], 0);
const promise = WebAssembly.compile(buffer);
buffer[0] = 1;
return promise.then(assert_Module);
}, "Changing the buffer");

View File

@ -0,0 +1,22 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=/wasm/jsapi/bad-imports.js
test_bad_imports((name, error, build, ...arguments) => {
promise_test(t => {
const builder = new WasmModuleBuilder();
build(builder);
const buffer = builder.toBuffer();
const module = new WebAssembly.Module(buffer);
return promise_rejects_js(t, error, WebAssembly.instantiate(module, ...arguments));
}, `WebAssembly.instantiate(module): ${name}`);
});
test_bad_imports((name, error, build, ...arguments) => {
promise_test(t => {
const builder = new WasmModuleBuilder();
build(builder);
const buffer = builder.toBuffer();
return promise_rejects_js(t, error, WebAssembly.instantiate(buffer, ...arguments));
}, `WebAssembly.instantiate(buffer): ${name}`);
});

View File

@ -0,0 +1,152 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/instanceTestFactory.js
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
promise_test(t => {
return promise_rejects_js(t, TypeError, WebAssembly.instantiate());
}, "Missing arguments");
promise_test(() => {
const fn = WebAssembly.instantiate;
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly,
];
return Promise.all(thisValues.map(thisValue => {
return fn.call(thisValue, emptyModuleBinary).then(assert_WebAssemblyInstantiatedSource);
}));
}, "Branding");
promise_test(t => {
const invalidArguments = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Module,
WebAssembly.Module.prototype,
ArrayBuffer,
ArrayBuffer.prototype,
Array.from(emptyModuleBinary),
];
return Promise.all(invalidArguments.map(argument => {
return promise_rejects_js(t, TypeError, WebAssembly.instantiate(argument),
`instantiate(${format_value(argument)})`);
}));
}, "Invalid arguments");
test(() => {
const promise = WebAssembly.instantiate(emptyModuleBinary);
assert_equals(Object.getPrototypeOf(promise), Promise.prototype, "prototype");
assert_true(Object.isExtensible(promise), "extensibility");
}, "Promise type");
for (const [name, fn] of instanceTestFactory) {
promise_test(() => {
const { buffer, args, exports, verify } = fn();
return WebAssembly.instantiate(buffer, ...args).then(result => {
assert_WebAssemblyInstantiatedSource(result, exports);
verify(result.instance);
});
}, `${name}: BufferSource argument`);
promise_test(() => {
const { buffer, args, exports, verify } = fn();
const module = new WebAssembly.Module(buffer);
return WebAssembly.instantiate(module, ...args).then(instance => {
assert_Instance(instance, exports);
verify(instance);
});
}, `${name}: Module argument`);
}
promise_test(() => {
const builder = new WasmModuleBuilder();
builder.addImportedGlobal("module", "global", kWasmI32);
const buffer = builder.toBuffer();
const order = [];
const imports = {
get module() {
order.push("module getter");
return {
get global() {
order.push("global getter");
return 0;
},
}
},
};
const expected = [
"module getter",
"global getter",
];
const p = WebAssembly.instantiate(buffer, imports);
assert_array_equals(order, []);
return p.then(result => {
assert_WebAssemblyInstantiatedSource(result);
assert_array_equals(order, expected);
});
}, "Synchronous options handling: Buffer argument");
promise_test(() => {
const builder = new WasmModuleBuilder();
builder.addImportedGlobal("module", "global", kWasmI32);
const buffer = builder.toBuffer();
const module = new WebAssembly.Module(buffer);
const order = [];
const imports = {
get module() {
order.push("module getter");
return {
get global() {
order.push("global getter");
return 0;
},
}
},
};
const expected = [
"module getter",
"global getter",
];
const p = WebAssembly.instantiate(module, imports);
assert_array_equals(order, expected);
return p.then(instance => assert_Instance(instance, {}));
}, "Synchronous options handling: Module argument");
promise_test(t => {
const buffer = new Uint8Array();
return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.instantiate(buffer));
}, "Empty buffer");
promise_test(t => {
const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0]));
return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.instantiate(buffer));
}, "Invalid code");
promise_test(() => {
const buffer = new WasmModuleBuilder().toBuffer();
assert_equals(buffer[0], 0);
const promise = WebAssembly.instantiate(buffer);
buffer[0] = 1;
return promise.then(assert_WebAssemblyInstantiatedSource);
}, "Changing the buffer");

View File

@ -0,0 +1,149 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=/wasm/jsapi/assertions.js
const type_if_fi = makeSig([kWasmF64, kWasmI32], [kWasmI32, kWasmF64]);
promise_test(async () => {
const builder = new WasmModuleBuilder();
builder
.addFunction("swap", type_if_fi)
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 0,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
const result = await WebAssembly.instantiate(buffer);
const swapped = result.instance.exports.swap(4.2, 7);
assert_true(Array.isArray(swapped));
assert_equals(Object.getPrototypeOf(swapped), Array.prototype);
assert_array_equals(swapped, [7, 4.2]);
}, "multiple return values from wasm to js");
promise_test(async () => {
const builder = new WasmModuleBuilder();
const swap = builder
.addFunction("swap", type_if_fi)
.addBody([
kExprLocalGet, 1,
kExprLocalGet, 0,
kExprReturn,
]);
builder
.addFunction("callswap", kSig_i_v)
.addBody([
...wasmF64Const(4.2),
...wasmI32Const(7),
kExprCallFunction, swap.index,
kExprDrop,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
const result = await WebAssembly.instantiate(buffer);
const swapped = result.instance.exports.callswap();
assert_equals(swapped, 7);
}, "multiple return values inside wasm");
promise_test(async () => {
const builder = new WasmModuleBuilder();
const fnIndex = builder.addImport("module", "fn", type_if_fi);
builder
.addFunction("callfn", kSig_i_v)
.addBody([
...wasmF64Const(4.2),
...wasmI32Const(7),
kExprCallFunction, fnIndex,
kExprDrop,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
const actual = [];
const imports = {
"module": {
fn(f32, i32) {
assert_equals(f32, 4.2);
assert_equals(i32, 7);
const result = [2, 7.3];
let i = 0;
return {
get [Symbol.iterator]() {
actual.push("@@iterator getter");
return function iterator() {
actual.push("@@iterator call");
return {
get next() {
actual.push("next getter");
return function next(...args) {
assert_array_equals(args, []);
let j = ++i;
actual.push(`next call ${j}`);
if (j > result.length) {
return {
get done() {
actual.push(`done call ${j}`);
return true;
}
};
}
return {
get done() {
actual.push(`done call ${j}`);
return false;
},
get value() {
actual.push(`value call ${j}`);
return {
get valueOf() {
actual.push(`valueOf get ${j}`);
return function() {
actual.push(`valueOf call ${j}`);
return result[j - 1];
};
}
};
}
};
};
}
};
}
},
};
},
}
};
const { instance } = await WebAssembly.instantiate(buffer, imports);
const result = instance.exports.callfn();
assert_equals(result, 2);
assert_array_equals(actual, [
"@@iterator getter",
"@@iterator call",
"next getter",
"next call 1",
"done call 1",
"value call 1",
"next call 2",
"done call 2",
"value call 2",
"next call 3",
"done call 3",
"valueOf get 1",
"valueOf call 1",
"valueOf get 2",
"valueOf call 2",
]);
}, "multiple return values from js to wasm");

View File

@ -0,0 +1,42 @@
// META: global=window,dedicatedworker,jsshell
"use strict";
// https://webidl.spec.whatwg.org/#es-namespaces
// https://webassembly.github.io/spec/js-api/#namespacedef-webassembly
test(() => {
assert_own_property(WebAssembly, Symbol.toStringTag);
const propDesc = Object.getOwnPropertyDescriptor(WebAssembly, Symbol.toStringTag);
assert_equals(propDesc.value, "WebAssembly", "value");
assert_equals(propDesc.writable, false, "writable");
assert_equals(propDesc.enumerable, false, "enumerable");
assert_equals(propDesc.configurable, true, "configurable");
}, "@@toStringTag exists on the namespace object with the appropriate descriptor");
test(() => {
assert_equals(WebAssembly.toString(), "[object WebAssembly]");
assert_equals(Object.prototype.toString.call(WebAssembly), "[object WebAssembly]");
}, "Object.prototype.toString applied to the namespace object");
test(t => {
assert_own_property(WebAssembly, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object");
t.add_cleanup(() => {
Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "WebAssembly" });
});
Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "Test" });
assert_equals(WebAssembly.toString(), "[object Test]");
assert_equals(Object.prototype.toString.call(WebAssembly), "[object Test]");
}, "Object.prototype.toString applied after modifying the namespace object's @@toStringTag");
test(t => {
assert_own_property(WebAssembly, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object");
t.add_cleanup(() => {
Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "WebAssembly" });
});
assert_true(delete WebAssembly[Symbol.toStringTag]);
assert_equals(WebAssembly.toString(), "[object Object]");
assert_equals(Object.prototype.toString.call(WebAssembly), "[object Object]");
}, "Object.prototype.toString applied after deleting @@toStringTag");

View File

@ -0,0 +1,99 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
test(() => {
assert_throws_js(TypeError, () => WebAssembly.validate());
}, "Missing argument");
test(() => {
const invalidArguments = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
ArrayBuffer,
ArrayBuffer.prototype,
Array.from(emptyModuleBinary),
];
for (const argument of invalidArguments) {
assert_throws_js(TypeError, () => WebAssembly.validate(argument),
`validate(${format_value(argument)})`);
}
}, "Invalid arguments");
test(() => {
const fn = WebAssembly.validate;
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly,
];
for (const thisValue of thisValues) {
assert_true(fn.call(thisValue, emptyModuleBinary), `this=${format_value(thisValue)}`);
}
}, "Branding");
const modules = [
// Incomplete header.
[[], false],
[[0x00], false],
[[0x00, 0x61], false],
[[0x00, 0x61, 0x73], false],
[[0x00, 0x61, 0x73, 0x6d], false],
[[0x00, 0x61, 0x73, 0x6d, 0x01], false],
[[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00], false],
[[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00], false],
// Complete header.
[[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], true],
// Invalid version.
[[0x00, 0x61, 0x73, 0x6d, 0x00, 0x00, 0x00, 0x00], false],
[[0x00, 0x61, 0x73, 0x6d, 0x02, 0x00, 0x00, 0x00], false],
// Nameless custom section.
[[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00], false],
// Custom section with empty name.
[[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00], true],
// Custom section with name "a".
[[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x61], true],
];
const bufferTypes = [
Uint8Array,
Int8Array,
Uint16Array,
Int16Array,
Uint32Array,
Int32Array,
];
for (const [module, expected] of modules) {
const name = module.map(n => n.toString(16)).join(" ");
for (const bufferType of bufferTypes) {
if (module.length % bufferType.BYTES_PER_ELEMENT === 0) {
test(() => {
const bytes = new Uint8Array(module);
const moduleBuffer = new bufferType(bytes.buffer);
assert_equals(WebAssembly.validate(moduleBuffer), expected);
}, `Validating module [${name}] in ${bufferType.name}`);
}
}
}
test(() => {
assert_true(WebAssembly.validate(emptyModuleBinary, {}));
}, "Stray argument");

View File

@ -0,0 +1,13 @@
// META: global=jsshell
test(() => {
assert_not_own_property(WebAssembly.CompileError.prototype, Symbol.toStringTag);
}, "WebAssembly.CompileError");
test(() => {
assert_not_own_property(WebAssembly.LinkError.prototype, Symbol.toStringTag);
}, "WebAssembly.LinkError");
test(() => {
assert_not_own_property(WebAssembly.RuntimeError.prototype, Symbol.toStringTag);
}, "WebAssembly.RuntimeError");

View File

@ -0,0 +1,121 @@
// META: global=window,worker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
function assert_throws_wasm(fn, message) {
try {
fn();
assert_not_reached(`expected to throw with ${message}`);
} catch (e) {
assert_true(e instanceof WebAssembly.Exception, `Error should be a WebAssembly.Exception with ${message}`);
}
}
promise_test(async () => {
const kWasmAnyRef = 0x6f;
const kSig_v_r = makeSig([kWasmAnyRef], []);
const builder = new WasmModuleBuilder();
const tagIndex = builder.addTag(kSig_v_r);
builder.addFunction("throw_param", kSig_v_r)
.addBody([
kExprLocalGet, 0,
kExprThrow, tagIndex,
])
.exportFunc();
const buffer = builder.toBuffer();
const {instance} = await WebAssembly.instantiate(buffer, {});
const values = [
undefined,
null,
true,
false,
"test",
Symbol(),
0,
1,
4.2,
NaN,
Infinity,
{},
() => {},
];
for (const v of values) {
assert_throws_wasm(() => instance.exports.throw_param(v), String(v));
}
}, "Wasm function throws argument");
promise_test(async () => {
const builder = new WasmModuleBuilder();
const tagIndex = builder.addTag(kSig_v_a);
builder.addFunction("throw_null", kSig_v_v)
.addBody([
kExprRefNull, kWasmAnyFunc,
kExprThrow, tagIndex,
])
.exportFunc();
const buffer = builder.toBuffer();
const {instance} = await WebAssembly.instantiate(buffer, {});
assert_throws_wasm(() => instance.exports.throw_null());
}, "Wasm function throws null");
promise_test(async () => {
const builder = new WasmModuleBuilder();
const tagIndex = builder.addTag(kSig_v_i);
builder.addFunction("throw_int", kSig_v_v)
.addBody([
...wasmI32Const(7),
kExprThrow, tagIndex,
])
.exportFunc();
const buffer = builder.toBuffer();
const {instance} = await WebAssembly.instantiate(buffer, {});
assert_throws_wasm(() => instance.exports.throw_int());
}, "Wasm function throws integer");
promise_test(async () => {
const builder = new WasmModuleBuilder();
const fnIndex = builder.addImport("module", "fn", kSig_v_v);
const tagIndex= builder.addTag(kSig_v_r);
builder.addFunction("catch_exception", kSig_r_v)
.addBody([
kExprTry, kWasmStmt,
kExprCallFunction, fnIndex,
kExprCatch, tagIndex,
kExprReturn,
kExprEnd,
kExprRefNull, kWasmAnyRef,
])
.exportFunc();
const buffer = builder.toBuffer();
const error = new Error();
const fn = () => { throw error };
const {instance} = await WebAssembly.instantiate(buffer, {
module: { fn }
});
assert_throws_exactly(error, () => instance.exports.catch_exception());
}, "Imported JS function throws");
promise_test(async () => {
const builder = new WasmModuleBuilder();
const fnIndex = builder.addImport("module", "fn", kSig_v_v);
builder.addFunction("catch_and_rethrow", kSig_r_v)
.addBody([
kExprTry, kWasmStmt,
kExprCallFunction, fnIndex,
kExprCatchAll,
kExprRethrow, 0x00,
kExprEnd,
kExprRefNull, kWasmAnyRef,
])
.exportFunc();
const buffer = builder.toBuffer();
const error = new Error();
const fn = () => { throw error };
const {instance} = await WebAssembly.instantiate(buffer, {
module: { fn }
});
assert_throws_exactly(error, () => instance.exports.catch_and_rethrow());
}, "Imported JS function throws, Wasm catches and rethrows");

View File

@ -0,0 +1,62 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
test(() => {
assert_function_name(
WebAssembly.Exception,
"Exception",
"WebAssembly.Exception"
);
}, "name");
test(() => {
assert_function_length(WebAssembly.Exception, 1, "WebAssembly.Exception");
}, "length");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Exception());
}, "No arguments");
test(() => {
const tag = new WebAssembly.Tag({ parameters: [] });
assert_throws_js(TypeError, () => WebAssembly.Exception(tag));
}, "Calling");
test(() => {
const invalidArguments = [
undefined,
null,
false,
true,
"",
"test",
Symbol(),
1,
NaN,
{},
];
for (const invalidArgument of invalidArguments) {
assert_throws_js(
TypeError,
() => new WebAssembly.Exception(invalidArgument),
`new Exception(${format_value(invalidArgument)})`
);
}
}, "Invalid descriptor argument");
test(() => {
const typesAndArgs = [
["i32", 123n],
["i32", Symbol()],
["f32", 123n],
["f64", 123n],
["i64", undefined],
];
for (const typeAndArg of typesAndArgs) {
const tag = new WebAssembly.Tag({ parameters: [typeAndArg[0]] });
assert_throws_js(
TypeError,
() => new WebAssembly.Exception(tag, typeAndArg[1])
);
}
}, "Invalid exception argument");

View File

@ -0,0 +1,54 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/memory/assertions.js
test(() => {
const tag = new WebAssembly.Tag({ parameters: [] });
const exn = new WebAssembly.Exception(tag, []);
assert_throws_js(TypeError, () => exn.getArg());
assert_throws_js(TypeError, () => exn.getArg(tag));
}, "Missing arguments");
test(() => {
const invalidValues = [undefined, null, true, "", Symbol(), 1, {}];
const tag = new WebAssembly.Tag({ parameters: [] });
const exn = new WebAssembly.Exception(tag, []);
for (argument of invalidValues) {
assert_throws_js(TypeError, () => exn.getArg(argument, 0));
}
}, "Invalid exception argument");
test(() => {
const tag = new WebAssembly.Tag({ parameters: [] });
const exn = new WebAssembly.Exception(tag, []);
assert_throws_js(RangeError, () => exn.getArg(tag, 1));
}, "Index out of bounds");
test(() => {
const outOfRangeValues = [
undefined,
NaN,
Infinity,
-Infinity,
-1,
0x100000000,
0x1000000000,
"0x100000000",
{
valueOf() {
return 0x100000000;
},
},
];
const tag = new WebAssembly.Tag({ parameters: [] });
const exn = new WebAssembly.Exception(tag, []);
for (const value of outOfRangeValues) {
assert_throws_js(RangeError, () => exn.getArg(tag, value));
}
}, "Getting out-of-range argument");
test(() => {
const tag = new WebAssembly.Tag({ parameters: ["i32"] });
const exn = new WebAssembly.Exception(tag, [42]);
assert_equals(exn.getArg(tag, 0), 42);
}, "getArg");

View File

@ -0,0 +1,61 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/wasm-module-builder.js
test(() => {
const tag = new WebAssembly.Tag({ parameters: ["i32"] });
const exn = new WebAssembly.Exception(tag, [42]);
const exn_same_payload = new WebAssembly.Exception(tag, [42]);
const exn_diff_payload = new WebAssembly.Exception(tag, [53]);
const builder = new WasmModuleBuilder();
const jsfuncIndex = builder.addImport("module", "jsfunc", kSig_v_v);
const tagIndex = builder.addImportedTag("module", "tag", kSig_v_i);
const imports = {
module: {
jsfunc: function() { throw exn; },
tag: tag
}
};
builder
.addFunction("catch_rethrow", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprCallFunction, jsfuncIndex,
kExprCatch, tagIndex,
kExprDrop,
kExprRethrow, 0x00,
kExprEnd
])
.exportFunc();
builder
.addFunction("catch_all_rethrow", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprCallFunction, jsfuncIndex,
kExprCatchAll,
kExprRethrow, 0x00,
kExprEnd
])
.exportFunc();
const buffer = builder.toBuffer();
WebAssembly.instantiate(buffer, imports).then(result => {
try {
result.instance.exports.catch_rethrow();
} catch (e) {
assert_equals(e, exn);
assert_not_equals(e, exn_same_payload);
assert_not_equals(e, exn_diff_payload);
}
try {
result.instance.exports.catch_all_rethrow();
} catch (e) {
assert_equals(e, exn);
assert_not_equals(e, exn_same_payload);
assert_not_equals(e, exn_diff_payload);
}
});
}, "Identity check");

View File

@ -0,0 +1,25 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/memory/assertions.js
test(() => {
const tag = new WebAssembly.Tag({ parameters: [] });
const exn = new WebAssembly.Exception(tag, []);
assert_throws_js(TypeError, () => exn.is());
}, "Missing arguments");
test(() => {
const invalidValues = [undefined, null, true, "", Symbol(), 1, {}];
const tag = new WebAssembly.Tag({ parameters: [] });
const exn = new WebAssembly.Exception(tag, []);
for (argument of invalidValues) {
assert_throws_js(TypeError, () => exn.is(argument));
}
}, "Invalid exception argument");
test(() => {
const tag1 = new WebAssembly.Tag({ parameters: ["i32"] });
const tag2 = new WebAssembly.Tag({ parameters: ["i32"] });
const exn = new WebAssembly.Exception(tag1, [42]);
assert_true(exn.is(tag1));
assert_false(exn.is(tag2));
}, "is");

View File

@ -0,0 +1,21 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const argument = { parameters: [] };
const tag = new WebAssembly.Tag(argument);
const exn = new WebAssembly.Exception(tag, []);
assert_class_string(exn, "WebAssembly.Exception");
}, "Object.prototype.toString on an Exception");
test(() => {
assert_own_property(WebAssembly.Exception.prototype, Symbol.toStringTag);
const propDesc = Object.getOwnPropertyDescriptor(
WebAssembly.Exception.prototype,
Symbol.toStringTag
);
assert_equals(propDesc.value, "WebAssembly.Exception", "value");
assert_equals(propDesc.configurable, true, "configurable");
assert_equals(propDesc.enumerable, false, "enumerable");
assert_equals(propDesc.writable, false, "writable");
}, "@@toStringTag exists on the prototype with the appropriate descriptor");

View File

@ -0,0 +1,16 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function addxy(x, y) {
return x + y
}
test(() => {
var fun = new WebAssembly.Function({parameters: ["i32", "i32"], results: ["i32"]}, addxy);
assert_equals(fun(1, 2), 3)
}, "test calling function")
test(() => {
var fun = new WebAssembly.Function({parameters: ["i32", "i32"], results: ["i32"]}, addxy);
assert_throws_js(TypeError, () => new fun(1, 2));
}, "test constructing function");

View File

@ -0,0 +1,65 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function addxy(x, y) {
return x + y
}
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_function_name(WebAssembly.Function, "Function", "WebAssembly.Function");
}, "name");
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_function_length(WebAssembly.Function, 2, "WebAssembly.Function");
}, "length");
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_throws_js(TypeError, () => new WebAssembly.Function());
const argument = {parameters: [], results: []};
assert_throws_js(TypeError, () => new WebAssembly.Function(argument));
}, "Too few arguments");
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
const arguments = [{parameters: ["i32", "i32"], results: ["i32"]}, addxy];
assert_throws_js(TypeError, () => WebAssembly.Function(...arguments));
}, "Calling");
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
var fun = new WebAssembly.Function({parameters: ["i32", "i32"], results: ["i32"]}, addxy);
assert_true(fun instanceof WebAssembly.Function)
}, "construct with JS function")
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: []}, addxy))
}, "fail with missing results")
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_throws_js(TypeError, () => new WebAssembly.Function({results: []}, addxy))
}, "fail with missing parameters")
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [1], results: [true]}, addxy))
}, "fail with non-string parameters & results")
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: ["invalid"], results: ["invalid"]}, addxy))
}, "fail with non-existent parameter and result type")
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [], results: []}, 72))
}, "fail with non-function object")
test(() => {
assert_implements(WebAssembly.Function, "WebAssembly.Function is not implemented");
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [], results: []}, {}))
}, "fail to construct with non-callable object")

View File

@ -0,0 +1,30 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function testfunc(n) {}
test(() => {
var table = new WebAssembly.Table({element: "anyfunc", initial: 3})
var func1 = new WebAssembly.Function({parameters: ["i32"], results: []}, testfunc)
table.set(0, func1)
var func2 = new WebAssembly.Function({parameters: ["f32"], results: []}, testfunc)
table.set(1, func2)
var func3 = new WebAssembly.Function({parameters: ["i64"], results: []}, testfunc)
table.set(2, func3)
var first = table.get(0)
assert_true(first instanceof WebAssembly.Function)
assert_equals(first, func1)
assert_equals(first.type().parameters[0], func1.type().parameters[0])
var second = table.get(1)
assert_true(second instanceof WebAssembly.Function)
assert_equals(second, func2)
assert_equals(second.type().parameters[0], func2.type().parameters[0])
var third = table.get(2)
assert_true(third instanceof WebAssembly.Function)
assert_equals(third, func3)
assert_equals(third.type().parameters[0], func3.type().parameters[0])
}, "Test insertion into table")

View File

@ -0,0 +1,28 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function addNumbers(x, y, z) {
return x+y+z;
}
function doNothing() {}
function assert_function(functype, func) {
var wasmFunc = new WebAssembly.Function(functype, func);
assert_equals(functype.parameters.length, wasmFunc.type().parameters.length);
for(let i = 0; i < functype.parameters.length; i++) {
assert_equals(functype.parameters[i], wasmFunc.type().parameters[i]);
}
assert_equals(functype.results.length, wasmFunc.type().results.length);
for(let i = 0; i < functype.results.length; i++) {
assert_equals(functype.results[i], wasmFunc.type().results[i]);
}
}
test(() => {
assert_function({results: [], parameters: []}, doNothing);
}, "Check empty results and parameters")
test(() => {
assert_function({results: ["f64"], parameters: ["i32", "i64", "f32"]}, addNumbers)
}, "Check all types")

View File

@ -0,0 +1,45 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Entry settings object for host functions when the function realm is different from the test realm</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/wasm/jsapi/wasm-module-builder.js"></script>
<script src="/wasm/jsapi/functions/helper.js"></script>
<!-- This is what would normally be considered the entry page. However, we use functions from the
resources/function/function.html realm. So window.open() should resolve relative to that realm
inside host functions. -->
<iframe src="resources/entry-incumbent.html"></iframe>
<iframe src="resources/function/function.html" id="function-frame"></iframe>
<script>
setup({ explicit_done: true });
const relativeURL = "resources/window-to-open.html";
const expectedURL = (new URL(relativeURL, document.querySelector("#function-frame").src)).href;
const incumbentWindow = frames[0];
const functionWindow = frames[1];
const FunctionFromAnotherWindow = functionWindow.Function;
window.onload = () => {
async_test(t => {
t.add_cleanup(() => { delete functionWindow.args; });
functionWindow.args = [incumbentWindow, relativeURL, t, assert_equals, expectedURL];
const func = FunctionFromAnotherWindow(`
const [incumbentWindow, relativeURL, t, assert_equals, expectedURL] = window.args;
const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
w.onload = t.step_func_done(() => {
t.add_cleanup(() => w.close());
assert_equals(w.location.href, expectedURL);
});
`);
call_later(func);
}, "Start function");
done();
};
</script>

View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Entry settings object for host functions</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/wasm/jsapi/wasm-module-builder.js"></script>
<script src="/wasm/jsapi/functions/helper.js"></script>
<!-- This is the entry page, so window.open() should resolve relative to it, even inside host functions. -->
<iframe src="resources/entry-incumbent.html"></iframe>
<script>
setup({ explicit_done: true });
const relativeURL = "resources/window-to-open.html";
const expectedURL = (new URL(relativeURL, location.href)).href;
const incumbentWindow = frames[0];
window.onload = () => {
async_test(t => {
const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
w.onload = t.step_func_done(() => {
t.add_cleanup(() => w.close());
assert_equals(w.location.href, expectedURL);
});
}, "Sanity check: this all works as expected synchronously");
async_test(t => {
// No t.step_func because that could change the realms
call_later(() => {
const w = incumbentWindow.runWindowOpenVeryIndirectly(relativeURL);
w.onload = t.step_func_done(() => {
t.add_cleanup(() => w.close());
assert_equals(w.location.href, expectedURL);
});
});
}, "Start function");
done();
};
</script>

View File

@ -0,0 +1,12 @@
function call_later(f) {
const builder = new WasmModuleBuilder();
const functionIndex = builder.addImport("module", "imported", kSig_v_v);
builder.addStart(functionIndex);
const buffer = builder.toBuffer();
WebAssembly.instantiate(buffer, {
"module": {
"imported": f,
}
});
}

View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Incumbent settings object for host functions</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<!-- This is the entry page. -->
<iframe src="resources/incumbent-incumbent.html"></iframe>
<script>
setup({ explicit_done: true });
// postMessage should pick the incumbent page as its .source value to set on the MessageEvent, even
// inside host functions.
const expectedURL = (new URL("resources/incumbent-incumbent.html", location.href)).href;
let testId = 0;
window.onload = () => {
const relevantWindow = frames[0].document.querySelector("#r").contentWindow;
function setupTest(t) {
++testId;
const thisTestId = testId;
relevantWindow.addEventListener("messagereceived", t.step_func(e => {
const [receivedTestId, receivedSourceURL] = e.detail;
if (receivedTestId !== thisTestId) {
return;
}
assert_equals(receivedSourceURL, expectedURL);
t.done();
}));
return thisTestId;
}
async_test(t => {
const thisTestId = setupTest(t);
frames[0].runWindowPostMessageVeryIndirectly(thisTestId, "*");
}, "Sanity check: this all works as expected synchronously");
async_test(t => {
const thisTestId = setupTest(t);
frames[0].runWindowPostMessageVeryIndirectlyWithNoUserCode(thisTestId, "*");
}, "Start function");
done();
};
</script>

View File

@ -0,0 +1,5 @@
A couple notes about the files scattered in this `resources/` directory:
* The nested directory structure is necessary here so that relative URL resolution can be tested; we need different sub-paths for each document.
* The semi-duplicate `window-to-open.html`s scattered throughout are present because Firefox, at least, does not fire `Window` `load` events for 404s, so we want to ensure that no matter which global is used, `window`'s `load` event is hit and our tests can proceed.

View File

@ -0,0 +1,4 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Current page used as a test helper</title>

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>If the current settings object is used this page will be opened</title>

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Incumbent page used as a test helper</title>
<iframe src="relevant/relevant.html" id="r"></iframe>
<iframe src="current/current.html" id="c"></iframe>
<script>
const relevant = document.querySelector("#r");
const current = document.querySelector("#c");
window.runWindowOpenVeryIndirectly = (...args) => {
return current.contentWindow.open.call(relevant.contentWindow, ...args);
};
</script>

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Realm for a host function used as a test helper</title>

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>If the function's settings object is used this page will be opened</title>

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Incumbent page used as a test helper</title>
<script src="/wasm/jsapi/wasm-module-builder.js"></script>
<script src="/wasm/jsapi/functions/helper.js"></script>
<iframe src="relevant/relevant.html" id="r"></iframe>
<iframe src="current/current.html" id="c"></iframe>
<script>
const relevant = document.querySelector("#r");
const current = document.querySelector("#c");
window.runWindowPostMessageVeryIndirectly = (...args) => {
return current.contentWindow.postMessage.call(relevant.contentWindow, ...args);
};
// This tests the backup incumbent settings object stack scenario, by avoiding putting user code on the stack.
window.runWindowPostMessageVeryIndirectlyWithNoUserCode = (...args) => {
const runWindowPostMessage = current.contentWindow.postMessage.bind(relevant.contentWindow, ...args);
call_later(runWindowPostMessage);
};
</script>

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>Relevant page used as a test helper</title>
<script>
// incumbent.html will end up posting a message to here. We need to signal back the "source".
window.onmessage = e => {
const testId = e.data;
const sourceURL = e.source.document.URL;
window.dispatchEvent(new CustomEvent("messagereceived", { detail: [testId, sourceURL] }));
};
</script>

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>If the relevant settings object is used this page will be opened</title>

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>If the incumbent settings object is used this page will be opened</title>

View File

@ -0,0 +1,3 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>If the entry settings object is used this page will be opened</title>

View File

@ -0,0 +1,171 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function assert_Global(actual, expected) {
assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype,
"prototype");
assert_true(Object.isExtensible(actual), "extensible");
assert_equals(actual.value, expected, "value");
assert_equals(actual.valueOf(), expected, "valueOf");
}
test(() => {
assert_function_name(WebAssembly.Global, "Global", "WebAssembly.Global");
}, "name");
test(() => {
assert_function_length(WebAssembly.Global, 1, "WebAssembly.Global");
}, "length");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Global());
}, "No arguments");
test(() => {
const argument = { "value": "i32" };
assert_throws_js(TypeError, () => WebAssembly.Global(argument));
}, "Calling");
test(() => {
const order = [];
new WebAssembly.Global({
get value() {
order.push("descriptor value");
return {
toString() {
order.push("descriptor value toString");
return "f64";
},
};
},
get mutable() {
order.push("descriptor mutable");
return false;
},
}, {
valueOf() {
order.push("value valueOf()");
}
});
assert_array_equals(order, [
"descriptor mutable",
"descriptor value",
"descriptor value toString",
"value valueOf()",
]);
}, "Order of evaluation");
test(() => {
const invalidArguments = [
undefined,
null,
false,
true,
"",
"test",
Symbol(),
1,
NaN,
{},
];
for (const invalidArgument of invalidArguments) {
assert_throws_js(TypeError,
() => new WebAssembly.Global(invalidArgument),
`new Global(${format_value(invalidArgument)})`);
}
}, "Invalid descriptor argument");
test(() => {
const invalidTypes = ["i16", "i128", "f16", "f128", "u32", "u64", "i32\0"];
for (const value of invalidTypes) {
const argument = { value };
assert_throws_js(TypeError, () => new WebAssembly.Global(argument));
}
}, "Invalid type argument");
test(() => {
const argument = { "value": "v128" };
assert_throws_js(TypeError, () => new WebAssembly.Global(argument));
}, "Construct v128 global");
test(() => {
const argument = { "value": "i64" };
const global = new WebAssembly.Global(argument);
assert_Global(global, 0n);
}, "i64 with default");
for (const type of ["i32", "f32", "f64"]) {
test(() => {
const argument = { "value": type };
const global = new WebAssembly.Global(argument);
assert_Global(global, 0);
}, `Default value for type ${type}`);
const valueArguments = [
[undefined, 0],
[null, 0],
[true, 1],
[false, 0],
[2, 2],
["3", 3],
[{ toString() { return "5" } }, 5, "object with toString returning string"],
[{ valueOf() { return "8" } }, 8, "object with valueOf returning string"],
[{ toString() { return 6 } }, 6, "object with toString returning number"],
[{ valueOf() { return 9 } }, 9, "object with valueOf returning number"],
];
for (const [value, expected, name = format_value(value)] of valueArguments) {
test(() => {
const argument = { "value": type };
const global = new WebAssembly.Global(argument, value);
assert_Global(global, expected);
}, `Explicit value ${name} for type ${type}`);
}
test(() => {
const argument = { "value": type };
assert_throws_js(TypeError, () => new WebAssembly.Global(argument, 0n));
}, `BigInt value for type ${type}`);
}
const valueArguments = [
[undefined, 0n],
[true, 1n],
[false, 0n],
["3", 3n],
[123n, 123n],
[{ toString() { return "5" } }, 5n, "object with toString returning string"],
[{ valueOf() { return "8" } }, 8n, "object with valueOf returning string"],
[{ toString() { return 6n } }, 6n, "object with toString returning bigint"],
[{ valueOf() { return 9n } }, 9n, "object with valueOf returning bigint"],
];
for (const [value, expected, name = format_value(value)] of valueArguments) {
test(() => {
const argument = { "value": "i64" };
const global = new WebAssembly.Global(argument, value);
assert_Global(global, expected);
}, `Explicit value ${name} for type i64`);
}
const invalidBigints = [
null,
666,
{ toString() { return 5 } },
{ valueOf() { return 8 } },
Symbol(),
];
for (const invalidBigint of invalidBigints) {
test(() => {
var argument = { "value": "i64" };
assert_throws_js(TypeError, () => new WebAssembly.Global(argument, invalidBigint));
}, `Pass non-bigint as i64 Global value: ${format_value(invalidBigint)}`);
}
test(() => {
const argument = { "value": "i32" };
const global = new WebAssembly.Global(argument, 0, {});
assert_Global(global, 0);
}, "Stray argument");

View File

@ -0,0 +1,17 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const argument = { "value": "i32" };
const global = new WebAssembly.Global(argument);
assert_class_string(global, "WebAssembly.Global");
}, "Object.prototype.toString on an Global");
test(() => {
assert_own_property(WebAssembly.Global.prototype, Symbol.toStringTag);
const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, Symbol.toStringTag);
assert_equals(propDesc.value, "WebAssembly.Global", "value");
assert_equals(propDesc.configurable, true, "configurable");
assert_equals(propDesc.enumerable, false, "enumerable");
assert_equals(propDesc.writable, false, "writable");
}, "@@toStringTag exists on the prototype with the appropriate descriptor");

View File

@ -0,0 +1,65 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function assert_type(argument) {
const myglobal = new WebAssembly.Global(argument);
const globaltype = myglobal.type();
assert_equals(globaltype.value, argument.value);
assert_equals(globaltype.mutable, argument.mutable);
}
test(() => {
assert_type({ "value": "i32", "mutable": true});
}, "i32, mutable");
test(() => {
assert_type({ "value": "i32", "mutable": false});
}, "i32, immutable");
test(() => {
assert_type({ "value": "i64", "mutable": true});
}, "i64, mutable");
test(() => {
assert_type({ "value": "i64", "mutable": false});
}, "i64, immutable");
test(() => {
assert_type({ "value": "f32", "mutable": true});
}, "f32, mutable");
test(() => {
assert_type({ "value": "f32", "mutable": false});
}, "f32, immutable");
test(() => {
assert_type({ "value": "f64", "mutable": true});
}, "f64, mutable");
test(() => {
assert_type({ "value": "f64", "mutable": false});
}, "f64, immutable");
test(() => {
assert_type({"value": "externref", "mutable": true})
}, "externref, mutable")
test(() => {
assert_type({"value": "externref", "mutable": false})
}, "externref, immutable")
test(() => {
assert_type({"value": "funcref", "mutable": true})
}, "funcref, mutable")
test(() => {
assert_type({"value": "funcref", "mutable": false})
}, "funcref, immutable")
test(() => {
const myglobal = new WebAssembly.Global({"value": "i32", "mutable": true});
const propertyNames = Object.getOwnPropertyNames(myglobal.type());
assert_equals(propertyNames[0], "mutable");
assert_equals(propertyNames[1], "value");
}, "key ordering");

View File

@ -0,0 +1,152 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Global,
WebAssembly.Global.prototype,
];
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value");
assert_equals(typeof desc, "object");
const getter = desc.get;
assert_equals(typeof getter, "function");
const setter = desc.set;
assert_equals(typeof setter, "function");
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => getter.call(thisValue), `getter with this=${format_value(thisValue)}`);
assert_throws_js(TypeError, () => setter.call(thisValue, 1), `setter with this=${format_value(thisValue)}`);
}
}, "Branding");
for (const type of ["i32", "i64", "f32", "f64"]) {
const [initial, value, invalid] = type === "i64" ? [0n, 1n, 2] : [0, 1, 2n];
const immutableOptions = [
[{}, "missing"],
[{ "mutable": undefined }, "undefined"],
[{ "mutable": null }, "null"],
[{ "mutable": false }, "false"],
[{ "mutable": "" }, "empty string"],
[{ "mutable": 0 }, "zero"],
];
for (const [opts, name] of immutableOptions) {
test(() => {
opts.value = type;
const global = new WebAssembly.Global(opts);
assert_equals(global.value, initial, "initial value");
assert_equals(global.valueOf(), initial, "initial valueOf");
assert_throws_js(TypeError, () => global.value = value);
assert_equals(global.value, initial, "post-set value");
assert_equals(global.valueOf(), initial, "post-set valueOf");
}, `Immutable ${type} (${name})`);
test(t => {
opts.value = type;
const global = new WebAssembly.Global(opts);
assert_equals(global.value, initial, "initial value");
assert_equals(global.valueOf(), initial, "initial valueOf");
const value = {
valueOf: t.unreached_func("should not call valueOf"),
toString: t.unreached_func("should not call toString"),
};
assert_throws_js(TypeError, () => global.value = value);
assert_equals(global.value, initial, "post-set value");
assert_equals(global.valueOf(), initial, "post-set valueOf");
}, `Immutable ${type} with ToNumber side-effects (${name})`);
}
const mutableOptions = [
[{ "mutable": true }, "true"],
[{ "mutable": 1 }, "one"],
[{ "mutable": "x" }, "string"],
[Object.create({ "mutable": true }), "true on prototype"],
];
for (const [opts, name] of mutableOptions) {
test(() => {
opts.value = type;
const global = new WebAssembly.Global(opts);
assert_equals(global.value, initial, "initial value");
assert_equals(global.valueOf(), initial, "initial valueOf");
global.value = value;
assert_throws_js(TypeError, () => global.value = invalid);
assert_equals(global.value, value, "post-set value");
assert_equals(global.valueOf(), value, "post-set valueOf");
}, `Mutable ${type} (${name})`);
}
}
test(() => {
const argument = { "value": "i64", "mutable": true };
const global = new WebAssembly.Global(argument);
assert_equals(global.value, 0n, "initial value using ToJSValue");
const valid = [
[123n, 123n],
[2n ** 63n, - (2n ** 63n)],
[true, 1n],
[false, 0n],
["456", 456n],
];
for (const [input, output] of valid) {
global.value = input;
assert_equals(global.valueOf(), output, "post-set valueOf");
}
const invalid = [
undefined,
null,
0,
1,
4.2,
Symbol(),
];
for (const input of invalid) {
assert_throws_js(TypeError, () => global.value = input);
}
}, "i64 mutability");
test(() => {
const argument = { "value": "i32", "mutable": true };
const global = new WebAssembly.Global(argument);
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value");
assert_equals(typeof desc, "object");
const setter = desc.set;
assert_equals(typeof setter, "function");
assert_throws_js(TypeError, () => setter.call(global));
}, "Calling setter without argument");
test(() => {
const argument = { "value": "i32", "mutable": true };
const global = new WebAssembly.Global(argument);
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value");
assert_equals(typeof desc, "object");
const getter = desc.get;
assert_equals(typeof getter, "function");
const setter = desc.set;
assert_equals(typeof setter, "function");
assert_equals(getter.call(global, {}), 0);
assert_equals(setter.call(global, 1, {}), undefined);
assert_equals(global.value, 1);
}, "Stray argument");

View File

@ -0,0 +1,28 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const argument = { "value": "i32" };
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Global,
WebAssembly.Global.prototype,
];
const fn = WebAssembly.Global.prototype.valueOf;
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => fn.call(thisValue), `this=${format_value(thisValue)}`);
}
}, "Branding");
test(() => {
const argument = { "value": "i32" };
const global = new WebAssembly.Global(argument, 0);
assert_equals(global.valueOf({}), 0);
}, "Stray argument");

View File

@ -0,0 +1,22 @@
// META: script=/resources/WebIDLParser.js
// META: script=/resources/idlharness.js
// META: script=../resources/load_wasm.js
'use strict';
// https://webassembly.github.io/spec/js-api/
idl_test(
['wasm-js-api'],
[],
async idl_array => {
self.mod = await createWasmModule();
self.instance = new WebAssembly.Instance(self.mod);
idl_array.add_objects({
Memory: [new WebAssembly.Memory({initial: 1024})],
Module: [self.mod],
Instance: [self.instance],
});
}
);

View File

@ -0,0 +1,13 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=/wasm/jsapi/bad-imports.js
test_bad_imports((name, error, build, ...arguments) => {
test(() => {
const builder = new WasmModuleBuilder();
build(builder);
const buffer = builder.toBuffer();
const module = new WebAssembly.Module(buffer);
assert_throws_js(error, () => new WebAssembly.Instance(module, ...arguments));
}, `new WebAssembly.Instance(module): ${name}`);
});

View File

@ -0,0 +1,54 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
function getExports() {
const builder = new WasmModuleBuilder();
builder
.addFunction("fn", kSig_v_d)
.addBody([])
.exportFunc();
builder.setTableBounds(1);
builder.addExportOfKind("table", kExternalTable, 0);
builder.addGlobal(kWasmI32, false).exportAs("global");
builder.addMemory(4, 8, true);
const buffer = builder.toBuffer();
const module = new WebAssembly.Module(buffer);
const instance = new WebAssembly.Instance(module);
return instance.exports;
}
test(() => {
const exports = getExports();
const builder = new WasmModuleBuilder();
const functionIndex = builder.addImport("module", "imported", kSig_v_d);
builder.addExport("exportedFunction", functionIndex);
const globalIndex = builder.addImportedGlobal("module", "global", kWasmI32);
builder.addExportOfKind("exportedGlobal", kExternalGlobal, globalIndex);
builder.addImportedMemory("module", "memory", 4);
builder.exportMemoryAs("exportedMemory");
const tableIndex = builder.addImportedTable("module", "table", 1);
builder.addExportOfKind("exportedTable", kExternalTable, tableIndex);
const buffer = builder.toBuffer();
const module = new WebAssembly.Module(buffer);
const instance = new WebAssembly.Instance(module, {
"module": {
"imported": exports.fn,
"global": exports.global,
"memory": exports.memory,
"table": exports.table,
}
});
assert_equals(instance.exports.exportedFunction, exports.fn);
assert_equals(instance.exports.exportedGlobal, exports.global);
assert_equals(instance.exports.exportedMemory, exports.memory);
assert_equals(instance.exports.exportedTable, exports.table);
});

View File

@ -0,0 +1,54 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/instanceTestFactory.js
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
test(() => {
assert_function_name(WebAssembly.Instance, "Instance", "WebAssembly.Instance");
}, "name");
test(() => {
assert_function_length(WebAssembly.Instance, 1, "WebAssembly.Instance");
}, "length");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Instance());
}, "No arguments");
test(() => {
const invalidArguments = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Module,
WebAssembly.Module.prototype,
];
for (const argument of invalidArguments) {
assert_throws_js(TypeError, () => new WebAssembly.Instance(argument),
`new Instance(${format_value(argument)})`);
}
}, "Non-Module arguments");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
assert_throws_js(TypeError, () => WebAssembly.Instance(module));
}, "Calling");
for (const [name, fn] of instanceTestFactory) {
test(() => {
const { buffer, args, exports, verify } = fn();
const module = new WebAssembly.Module(buffer);
const instance = new WebAssembly.Instance(module, ...args);
assert_Instance(instance, exports);
verify(instance);
}, name);
}

View File

@ -0,0 +1,66 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
test(() => {
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Instance,
WebAssembly.Instance.prototype,
];
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports");
assert_equals(typeof desc, "object");
const getter = desc.get;
assert_equals(typeof getter, "function");
assert_equals(typeof desc.set, "undefined");
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`);
}
}, "Branding");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const instance = new WebAssembly.Instance(module);
const exports = instance.exports;
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports");
assert_equals(typeof desc, "object");
const getter = desc.get;
assert_equals(typeof getter, "function");
assert_equals(getter.call(instance, {}), exports);
}, "Stray argument");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const instance = new WebAssembly.Instance(module);
const exports = instance.exports;
instance.exports = {};
assert_equals(instance.exports, exports, "Should not change the exports");
}, "Setting (sloppy mode)");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const instance = new WebAssembly.Instance(module);
const exports = instance.exports;
assert_throws_js(TypeError, () => {
"use strict";
instance.exports = {};
});
assert_equals(instance.exports, exports, "Should not change the exports");
}, "Setting (strict mode)");

View File

@ -0,0 +1,19 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
test(() => {
const emptyModuleBinary = new WasmModuleBuilder().toBuffer();
const module = new WebAssembly.Module(emptyModuleBinary);
const instance = new WebAssembly.Instance(module);
assert_class_string(instance, "WebAssembly.Instance");
}, "Object.prototype.toString on an Instance");
test(() => {
assert_own_property(WebAssembly.Instance.prototype, Symbol.toStringTag);
const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, Symbol.toStringTag);
assert_equals(propDesc.value, "WebAssembly.Instance", "value");
assert_equals(propDesc.configurable, true, "configurable");
assert_equals(propDesc.enumerable, false, "enumerable");
assert_equals(propDesc.writable, false, "writable");
}, "@@toStringTag exists on the prototype with the appropriate descriptor");

View File

@ -0,0 +1,761 @@
const instanceTestFactory = [
[
"Empty module without imports argument",
function() {
return {
buffer: emptyModuleBinary,
args: [],
exports: {},
verify: () => {},
};
}
],
[
"Empty module with undefined imports argument",
function() {
return {
buffer: emptyModuleBinary,
args: [undefined],
exports: {},
verify: () => {},
};
}
],
[
"Empty module with empty imports argument",
function() {
return {
buffer: emptyModuleBinary,
args: [{}],
exports: {},
verify: () => {},
};
}
],
[
"getter order for imports object",
function() {
const builder = new WasmModuleBuilder();
builder.addImportedGlobal("module", "global1", kWasmI32);
builder.addImportedGlobal("module2", "global3", kWasmI32);
builder.addImportedMemory("module", "memory", 0, 128);
builder.addImportedGlobal("module", "global2", kWasmI32);
const buffer = builder.toBuffer();
const order = [];
const imports = {
get module() {
order.push("module getter");
return {
get global1() {
order.push("global1 getter");
return 0;
},
get global2() {
order.push("global2 getter");
return 0;
},
get memory() {
order.push("memory getter");
return new WebAssembly.Memory({ "initial": 64, maximum: 128 });
},
}
},
get module2() {
order.push("module2 getter");
return {
get global3() {
order.push("global3 getter");
return 0;
},
}
},
};
const expected = [
"module getter",
"global1 getter",
"module2 getter",
"global3 getter",
"module getter",
"memory getter",
"module getter",
"global2 getter",
];
return {
buffer,
args: [imports],
exports: {},
verify: () => assert_array_equals(order, expected),
};
}
],
[
"imports",
function() {
const builder = new WasmModuleBuilder();
builder.addImport("module", "fn", kSig_v_v);
builder.addImportedGlobal("module", "global", kWasmI32);
builder.addImportedMemory("module", "memory", 0, 128);
builder.addImportedTable("module", "table", 0, 128);
const buffer = builder.toBuffer();
const imports = {
"module": {
"fn": function() {},
"global": 0,
"memory": new WebAssembly.Memory({ "initial": 64, maximum: 128 }),
"table": new WebAssembly.Table({ "element": "anyfunc", "initial": 64, maximum: 128 }),
},
get "module2"() {
assert_unreached("Should not get modules that are not imported");
},
};
return {
buffer,
args: [imports],
exports: {},
verify: () => {},
};
}
],
[
"imports with empty module names",
function() {
const builder = new WasmModuleBuilder();
builder.addImport("", "fn", kSig_v_v);
builder.addImportedGlobal("", "global", kWasmI32);
builder.addImportedMemory("", "memory", 0, 128);
builder.addImportedTable("", "table", 0, 128);
const buffer = builder.toBuffer();
const imports = {
"": {
"fn": function() {},
"global": 0,
"memory": new WebAssembly.Memory({ "initial": 64, maximum: 128 }),
"table": new WebAssembly.Table({ "element": "anyfunc", "initial": 64, maximum: 128 }),
},
};
return {
buffer,
args: [imports],
exports: {},
verify: () => {},
};
}
],
[
"imports with empty names",
function() {
const builder = new WasmModuleBuilder();
builder.addImport("a", "", kSig_v_v);
builder.addImportedGlobal("b", "", kWasmI32);
builder.addImportedMemory("c", "", 0, 128);
builder.addImportedTable("d", "", 0, 128);
const buffer = builder.toBuffer();
const imports = {
"a": { "": function() {} },
"b": { "": 0 },
"c": { "": new WebAssembly.Memory({ "initial": 64, maximum: 128 }) },
"d": { "": new WebAssembly.Table({ "element": "anyfunc", "initial": 64, maximum: 128 }) },
};
return {
buffer,
args: [imports],
exports: {},
verify: () => {},
};
}
],
[
"exports with empty name: function",
function() {
const builder = new WasmModuleBuilder();
builder
.addFunction("", kSig_v_d)
.addBody([])
.exportFunc();
const buffer = builder.toBuffer();
const exports = {
"": { "kind": "function", "name": "0", "length": 1 },
};
return {
buffer,
args: [],
exports,
verify: () => {},
};
}
],
[
"exports with empty name: table",
function() {
const builder = new WasmModuleBuilder();
builder.setTableBounds(1);
builder.addExportOfKind("", kExternalTable, 0);
const buffer = builder.toBuffer();
const exports = {
"": { "kind": "table", "length": 1 },
};
return {
buffer,
args: [],
exports,
verify: () => {},
};
}
],
[
"exports with empty name: global",
function() {
const builder = new WasmModuleBuilder();
builder.addGlobal(kWasmI32, true)
.exportAs("")
.init = 7;
const buffer = builder.toBuffer();
const exports = {
"": { "kind": "global", "value": 7 },
};
return {
buffer,
args: [],
exports,
verify: () => {},
};
}
],
[
"No imports",
function() {
const builder = new WasmModuleBuilder();
builder
.addFunction("fn", kSig_v_d)
.addBody([])
.exportFunc();
builder
.addFunction("fn2", kSig_v_v)
.addBody([])
.exportFunc();
builder.setTableBounds(1);
builder.addExportOfKind("table", kExternalTable, 0);
builder.addGlobal(kWasmI32, true)
.exportAs("global")
.init = 7;
builder.addGlobal(kWasmF64, true)
.exportAs("global2")
.init = 1.2;
builder.addMemory(4, 8, true);
const buffer = builder.toBuffer();
const exports = {
"fn": { "kind": "function", "name": "0", "length": 1 },
"fn2": { "kind": "function", "name": "1", "length": 0 },
"table": { "kind": "table", "length": 1 },
"global": { "kind": "global", "value": 7 },
"global2": { "kind": "global", "value": 1.2 },
"memory": { "kind": "memory", "size": 4 },
};
return {
buffer,
args: [],
exports,
verify: () => {},
};
}
],
[
"exports and imports",
function() {
const value = 102;
const builder = new WasmModuleBuilder();
const index = builder.addImportedGlobal("module", "global", kWasmI32);
builder
.addFunction("fn", kSig_i_v)
.addBody([
kExprGlobalGet,
index,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
const imports = {
"module": {
"global": value,
},
};
const exports = {
"fn": { "kind": "function", "name": "0", "length": 0 },
};
return {
buffer,
args: [imports],
exports,
verify: instance => assert_equals(instance.exports.fn(), value)
};
}
],
[
"i64 exports and imports",
function() {
const value = 102n;
const builder = new WasmModuleBuilder();
const index = builder.addImportedGlobal("module", "global", kWasmI64);
builder
.addFunction("fn", kSig_l_v)
.addBody([
kExprGlobalGet,
index,
kExprReturn,
])
.exportFunc();
const index2 = builder.addImportedGlobal("module", "global2", kWasmI64);
builder.addExportOfKind("global", kExternalGlobal, index2);
const buffer = builder.toBuffer();
const imports = {
"module": {
"global": value,
"global2": 2n ** 63n,
},
};
const exports = {
"fn": { "kind": "function", "name": "0", "length": 0 },
"global": { "kind": "global", "value": -(2n ** 63n) },
};
return {
buffer,
args: [imports],
exports,
verify: instance => assert_equals(instance.exports.fn(), value)
};
}
],
[
"import with i32-returning function",
function() {
const builder = new WasmModuleBuilder();
const fnIndex = builder.addImport("module", "fn", kSig_i_v);
const fn2 = builder
.addFunction("fn2", kSig_v_v)
.addBody([
kExprCallFunction,
fnIndex,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
let called = false;
const imports = {
"module": {
"fn": function() {
called = true;
return 6n;
},
},
};
return {
buffer,
args: [imports],
exports: {
"fn2": { "kind": "function", "name": String(fn2.index), "length": 0 },
},
verify: instance => {
assert_throws_js(TypeError, () => instance.exports.fn2());
assert_true(called, "Should have called into JS");
}
};
}
],
[
"import with function that takes and returns i32",
function() {
const builder = new WasmModuleBuilder();
const fnIndex = builder.addImport("module", "fn", kSig_i_i);
const fn2 = builder
.addFunction("fn2", kSig_i_v)
.addBody([
kExprI32Const, 0x66,
kExprCallFunction,
fnIndex,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
let called = false;
const imports = {
"module": {
"fn": function(n) {
called = true;
assert_equals(n, -26);
return { valueOf() { return 6; } };
},
},
};
return {
buffer,
args: [imports],
exports: {
"fn2": { "kind": "function", "name": String(fn2.index), "length": 0 },
},
verify: instance => {
assert_equals(instance.exports.fn2(), 6);
assert_true(called, "Should have called into JS");
}
};
}
],
[
"import with i64-returning function",
function() {
const builder = new WasmModuleBuilder();
const fnIndex = builder.addImport("module", "fn", kSig_l_v);
const fn2 = builder
.addFunction("fn2", kSig_v_v)
.addBody([
kExprCallFunction,
fnIndex,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
let called = false;
const imports = {
"module": {
"fn": function() {
called = true;
return 6;
},
},
};
return {
buffer,
args: [imports],
exports: {
"fn2": { "kind": "function", "name": String(fn2.index), "length": 0 },
},
verify: instance => {
assert_throws_js(TypeError, () => instance.exports.fn2());
assert_true(called, "Should have called into JS");
}
};
}
],
[
"import with function that takes and returns i64",
function() {
const builder = new WasmModuleBuilder();
const fnIndex = builder.addImport("module", "fn", kSig_l_l);
const fn2 = builder
.addFunction("fn2", kSig_l_v)
.addBody([
kExprI64Const, 0x66,
kExprCallFunction,
fnIndex,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
let called = false;
const imports = {
"module": {
"fn": function(n) {
called = true;
assert_equals(n, -26n);
return { valueOf() { return 6n; } };
},
},
};
return {
buffer,
args: [imports],
exports: {
"fn2": { "kind": "function", "name": String(fn2.index), "length": 0 },
},
verify: instance => {
assert_equals(instance.exports.fn2(), 6n);
assert_true(called, "Should have called into JS");
}
};
}
],
[
"import with i32-taking function",
function() {
const builder = new WasmModuleBuilder();
const fn = builder
.addFunction("fn", kSig_v_i)
.addBody([
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
return {
buffer,
args: [],
exports: {
"fn": { "kind": "function", "name": String(fn.index), "length": 1 },
},
verify: instance => assert_throws_js(TypeError, () => instance.exports.fn(6n))
};
}
],
[
"import with i64-taking function",
function() {
const builder = new WasmModuleBuilder();
const fn = builder
.addFunction("fn", kSig_v_l)
.addBody([
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
return {
buffer,
args: [],
exports: {
"fn": { "kind": "function", "name": String(fn.index), "length": 1 },
},
verify: instance => assert_throws_js(TypeError, () => instance.exports.fn(6))
};
}
],
[
"export i64-returning function",
function() {
const builder = new WasmModuleBuilder();
const fn = builder
.addFunction("fn", kSig_l_v)
.addBody([
kExprI64Const, 0x66,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
return {
buffer,
args: [],
exports: {
"fn": { "kind": "function", "name": String(fn.index), "length": 0 },
},
verify: instance => assert_equals(instance.exports.fn(), -26n)
};
}
],
[
"i32 mutable WebAssembly.Global import",
function() {
const initial = 102;
const value = new WebAssembly.Global({ "value": "i32", "mutable": true }, initial);
const builder = new WasmModuleBuilder();
const index = builder.addImportedGlobal("module", "global", kWasmI32, true);
const fn = builder
.addFunction("fn", kSig_i_v)
.addBody([
kExprGlobalGet,
index,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
const imports = {
"module": {
"global": value,
},
};
const exports = {
"fn": { "kind": "function", "name": String(fn.index), "length": 0 },
};
return {
buffer,
args: [imports],
exports,
verify: instance => {
assert_equals(instance.exports.fn(), initial);
const after = 201;
value.value = after;
assert_equals(instance.exports.fn(), after);
}
};
}
],
[
"i64 mutable WebAssembly.Global import",
function() {
const initial = 102n;
const value = new WebAssembly.Global({ "value": "i64", "mutable": true }, initial);
const builder = new WasmModuleBuilder();
const index = builder.addImportedGlobal("module", "global", kWasmI64, true);
const fn = builder
.addFunction("fn", kSig_l_v)
.addBody([
kExprGlobalGet,
index,
kExprReturn,
])
.exportFunc();
const buffer = builder.toBuffer();
const imports = {
"module": {
"global": value,
},
};
const exports = {
"fn": { "kind": "function", "name": String(fn.index), "length": 0 },
};
return {
buffer,
args: [imports],
exports,
verify: instance => {
assert_equals(instance.exports.fn(), initial);
const after = 201n;
value.value = after;
assert_equals(instance.exports.fn(), after);
}
};
}
],
[
"Multiple i64 arguments",
function() {
const builder = new WasmModuleBuilder();
const fn = builder
.addFunction("fn", kSig_l_ll)
.addBody([
kExprLocalGet, 1,
])
.exportFunc();
const buffer = builder.toBuffer();
const exports = {
"fn": { "kind": "function", "name": String(fn.index), "length": 2 },
};
return {
buffer,
args: [],
exports,
verify: instance => {
const fn = instance.exports.fn;
assert_equals(fn(1n, 0n), 0n);
assert_equals(fn(1n, 123n), 123n);
assert_equals(fn(1n, -123n), -123n);
assert_equals(fn(1n, "5"), 5n);
assert_throws_js(TypeError, () => fn(1n, 5));
}
};
}
],
[
"stray argument",
function() {
return {
buffer: emptyModuleBinary,
args: [{}, {}],
exports: {},
verify: () => {}
};
}
],
];

View File

@ -0,0 +1,160 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function test_operations(object, object_name, operations) {
for (const [name, length] of operations) {
test(() => {
const propdesc = Object.getOwnPropertyDescriptor(object, name);
assert_equals(typeof propdesc, "object");
assert_true(propdesc.writable, "writable");
assert_true(propdesc.enumerable, "enumerable");
assert_true(propdesc.configurable, "configurable");
assert_equals(propdesc.value, object[name]);
}, `${object_name}.${name}`);
test(() => {
assert_function_name(object[name], name, `${object_name}.${name}`);
}, `${object_name}.${name}: name`);
test(() => {
assert_function_length(object[name], length, `${object_name}.${name}`);
}, `${object_name}.${name}: length`);
}
}
function test_attributes(object, object_name, attributes) {
for (const [name, mutable] of attributes) {
test(() => {
const propdesc = Object.getOwnPropertyDescriptor(object, name);
assert_equals(typeof propdesc, "object");
assert_true(propdesc.enumerable, "enumerable");
assert_true(propdesc.configurable, "configurable");
}, `${object_name}.${name}`);
test(() => {
const propdesc = Object.getOwnPropertyDescriptor(object, name);
assert_equals(typeof propdesc, "object");
assert_equals(typeof propdesc.get, "function");
assert_function_name(propdesc.get, "get " + name, `getter for "${name}"`);
assert_function_length(propdesc.get, 0, `getter for "${name}"`);
}, `${object_name}.${name}: getter`);
test(() => {
const propdesc = Object.getOwnPropertyDescriptor(object, name);
assert_equals(typeof propdesc, "object");
if (mutable) {
assert_equals(typeof propdesc.set, "function");
assert_function_name(propdesc.set, "set " + name, `setter for "${name}"`);
assert_function_length(propdesc.set, 1, `setter for "${name}"`);
} else {
assert_equals(typeof propdesc.set, "undefined");
}
}, `${object_name}.${name}: setter`);
}
}
test(() => {
const propdesc = Object.getOwnPropertyDescriptor(this, "WebAssembly");
assert_equals(typeof propdesc, "object");
assert_true(propdesc.writable, "writable");
assert_false(propdesc.enumerable, "enumerable");
assert_true(propdesc.configurable, "configurable");
assert_equals(propdesc.value, this.WebAssembly);
}, "WebAssembly: property descriptor");
test(() => {
assert_throws_js(TypeError, () => WebAssembly());
}, "WebAssembly: calling");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly());
}, "WebAssembly: constructing");
const interfaces = [
"Module",
"Instance",
"Memory",
"Table",
"Global",
"CompileError",
"LinkError",
"RuntimeError",
];
for (const name of interfaces) {
test(() => {
const propdesc = Object.getOwnPropertyDescriptor(WebAssembly, name);
assert_equals(typeof propdesc, "object");
assert_true(propdesc.writable, "writable");
assert_false(propdesc.enumerable, "enumerable");
assert_true(propdesc.configurable, "configurable");
assert_equals(propdesc.value, WebAssembly[name]);
}, `WebAssembly.${name}: property descriptor`);
test(() => {
const interface_object = WebAssembly[name];
const propdesc = Object.getOwnPropertyDescriptor(interface_object, "prototype");
assert_equals(typeof propdesc, "object");
assert_false(propdesc.writable, "writable");
assert_false(propdesc.enumerable, "enumerable");
assert_false(propdesc.configurable, "configurable");
}, `WebAssembly.${name}: prototype`);
test(() => {
const interface_object = WebAssembly[name];
const interface_prototype_object = interface_object.prototype;
const propdesc = Object.getOwnPropertyDescriptor(interface_prototype_object, "constructor");
assert_equals(typeof propdesc, "object");
assert_true(propdesc.writable, "writable");
assert_false(propdesc.enumerable, "enumerable");
assert_true(propdesc.configurable, "configurable");
assert_equals(propdesc.value, interface_object);
}, `WebAssembly.${name}: prototype.constructor`);
}
test_operations(WebAssembly, "WebAssembly", [
["validate", 1],
["compile", 1],
["instantiate", 1],
]);
test_operations(WebAssembly.Module, "WebAssembly.Module", [
["exports", 1],
["imports", 1],
["customSections", 2],
]);
test_attributes(WebAssembly.Instance.prototype, "WebAssembly.Instance", [
["exports", false],
]);
test_operations(WebAssembly.Memory.prototype, "WebAssembly.Memory", [
["grow", 1],
]);
test_attributes(WebAssembly.Memory.prototype, "WebAssembly.Memory", [
["buffer", false],
]);
test_operations(WebAssembly.Table.prototype, "WebAssembly.Table", [
["grow", 1],
["get", 1],
["set", 1],
]);
test_attributes(WebAssembly.Table.prototype, "WebAssembly.Table", [
["length", false],
]);
test_operations(WebAssembly.Global.prototype, "WebAssembly.Global", [
["valueOf", 0],
]);
test_attributes(WebAssembly.Global.prototype, "WebAssembly.Global", [
["value", true],
]);

View File

@ -0,0 +1,38 @@
function assert_ArrayBuffer(actual, { size=0, shared=false, detached=false }, message) {
// https://github.com/WebAssembly/spec/issues/840
// See https://github.com/whatwg/html/issues/5380 for why not `self.SharedArrayBuffer`
const isShared = !("isView" in actual.constructor);
assert_equals(isShared, shared, `${message}: constructor`);
const sharedString = shared ? "Shared" : "";
assert_equals(actual.toString(), `[object ${sharedString}ArrayBuffer]`, `${message}: toString()`);
assert_equals(Object.getPrototypeOf(actual).toString(), `[object ${sharedString}ArrayBuffer]`, `${message}: prototype toString()`);
if (detached) {
// https://github.com/tc39/ecma262/issues/678
let byteLength;
try {
byteLength = actual.byteLength;
} catch (e) {
byteLength = 0;
}
assert_equals(byteLength, 0, `${message}: detached size`);
} else {
assert_equals(actual.byteLength, 0x10000 * size, `${message}: size`);
if (size > 0) {
const array = new Uint8Array(actual);
assert_equals(array[0], 0, `${message}: first element`);
assert_equals(array[array.byteLength - 1], 0, `${message}: last element`);
}
}
assert_equals(Object.isFrozen(actual), shared, "buffer frozen");
assert_equals(Object.isExtensible(actual), !shared, "buffer extensibility");
}
function assert_Memory(memory, { size=0, shared=false }) {
assert_equals(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype,
"prototype");
assert_true(Object.isExtensible(memory), "extensible");
// https://github.com/WebAssembly/spec/issues/840
assert_equals(memory.buffer, memory.buffer, "buffer should be idempotent");
assert_ArrayBuffer(memory.buffer, { size, shared });
}

View File

@ -0,0 +1,64 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Memory,
WebAssembly.Memory.prototype,
];
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer");
assert_equals(typeof desc, "object");
const getter = desc.get;
assert_equals(typeof getter, "function");
assert_equals(typeof desc.set, "undefined");
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`);
}
}, "Branding");
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
const buffer = memory.buffer;
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer");
assert_equals(typeof desc, "object");
const getter = desc.get;
assert_equals(typeof getter, "function");
assert_equals(getter.call(memory, {}), buffer);
}, "Stray argument");
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
const memory2 = new WebAssembly.Memory(argument);
const buffer = memory.buffer;
assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers");
memory.buffer = memory2.buffer;
assert_equals(memory.buffer, buffer, "Should not change the buffer");
}, "Setting (sloppy mode)");
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
const memory2 = new WebAssembly.Memory(argument);
const buffer = memory.buffer;
assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers");
assert_throws_js(TypeError, () => {
"use strict";
memory.buffer = memory2.buffer;
});
assert_equals(memory.buffer, buffer, "Should not change the buffer");
}, "Setting (strict mode)");

View File

@ -0,0 +1,54 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/memory/assertions.js
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": 10, "shared": true }));
}, "Shared memory without maximum");
test(t => {
const order = [];
new WebAssembly.Memory({
get maximum() {
order.push("maximum");
return {
valueOf() {
order.push("maximum valueOf");
return 1;
},
};
},
get initial() {
order.push("initial");
return {
valueOf() {
order.push("initial valueOf");
return 1;
},
};
},
get shared() {
order.push("shared");
return {
valueOf: t.unreached_func("should not call shared valueOf"),
};
},
});
assert_array_equals(order, [
"initial",
"initial valueOf",
"maximum",
"maximum valueOf",
"shared",
]);
}, "Order of evaluation for descriptor (with shared)");
test(() => {
const argument = { "initial": 4, "maximum": 10, shared: true };
const memory = new WebAssembly.Memory(argument);
assert_Memory(memory, { "size": 4, "shared": true });
}, "Shared memory");

View File

@ -0,0 +1,20 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/memory/assertions.js
test(() => {
const argument = { initial: 5, minimum: 6 };
assert_throws_js(TypeError, () => new WebAssembly.Memory(argument));
}, "Initializing with both initial and minimum");
test(() => {
const argument = { minimum: 0 };
const memory = new WebAssembly.Memory(argument);
assert_Memory(memory, { "size": 0 });
}, "Zero minimum");
test(() => {
const argument = { minimum: 4 };
const memory = new WebAssembly.Memory(argument);
assert_Memory(memory, { "size": 4 });
}, "Non-zero minimum");

View File

@ -0,0 +1,139 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/memory/assertions.js
test(() => {
assert_function_name(WebAssembly.Memory, "Memory", "WebAssembly.Memory");
}, "name");
test(() => {
assert_function_length(WebAssembly.Memory, 1, "WebAssembly.Memory");
}, "length");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Memory());
}, "No arguments");
test(() => {
const argument = { "initial": 0 };
assert_throws_js(TypeError, () => WebAssembly.Memory(argument));
}, "Calling");
test(() => {
const invalidArguments = [
undefined,
null,
false,
true,
"",
"test",
Symbol(),
1,
NaN,
{},
];
for (const invalidArgument of invalidArguments) {
assert_throws_js(TypeError,
() => new WebAssembly.Memory(invalidArgument),
`new Memory(${format_value(invalidArgument)})`);
}
}, "Invalid descriptor argument");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": undefined }));
}, "Undefined initial value in descriptor");
const outOfRangeValues = [
NaN,
Infinity,
-Infinity,
-1,
0x100000000,
0x1000000000,
];
for (const value of outOfRangeValues) {
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": value }));
}, `Out-of-range initial value in descriptor: ${format_value(value)}`);
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": 0, "maximum": value }));
}, `Out-of-range maximum value in descriptor: ${format_value(value)}`);
}
test(() => {
assert_throws_js(RangeError, () => new WebAssembly.Memory({ "initial": 10, "maximum": 9 }));
}, "Initial value exceeds maximum");
test(() => {
const proxy = new Proxy({}, {
has(o, x) {
assert_unreached(`Should not call [[HasProperty]] with ${x}`);
},
get(o, x) {
// Due to the requirement not to supply both minimum and initial, we need to ignore one of them.
switch (x) {
case "shared":
return false;
case "initial":
case "maximum":
return 0;
default:
return undefined;
}
},
});
new WebAssembly.Memory(proxy);
}, "Proxy descriptor");
test(() => {
const order = [];
new WebAssembly.Memory({
get maximum() {
order.push("maximum");
return {
valueOf() {
order.push("maximum valueOf");
return 1;
},
};
},
get initial() {
order.push("initial");
return {
valueOf() {
order.push("initial valueOf");
return 1;
},
};
},
});
assert_array_equals(order, [
"initial",
"initial valueOf",
"maximum",
"maximum valueOf",
]);
}, "Order of evaluation for descriptor");
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
assert_Memory(memory, { "size": 0 });
}, "Zero initial");
test(() => {
const argument = { "initial": 4 };
const memory = new WebAssembly.Memory(argument);
assert_Memory(memory, { "size": 4 });
}, "Non-zero initial");
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument, {});
assert_Memory(memory, { "size": 0 });
}, "Stray argument");

View File

@ -0,0 +1,189 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/memory/assertions.js
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
assert_throws_js(TypeError, () => memory.grow());
}, "Missing arguments");
test(t => {
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Memory,
WebAssembly.Memory.prototype,
];
const argument = {
valueOf: t.unreached_func("Should not touch the argument (valueOf)"),
toString: t.unreached_func("Should not touch the argument (toString)"),
};
const fn = WebAssembly.Memory.prototype.grow;
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`);
}
}, "Branding");
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
const oldMemory = memory.buffer;
assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
const result = memory.grow(2);
assert_equals(result, 0);
const newMemory = memory.buffer;
assert_not_equals(oldMemory, newMemory);
assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing");
}, "Zero initial");
test(() => {
const argument = { "initial": { valueOf() { return 0 } } };
const memory = new WebAssembly.Memory(argument);
const oldMemory = memory.buffer;
assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
const result = memory.grow({ valueOf() { return 2 } });
assert_equals(result, 0);
const newMemory = memory.buffer;
assert_not_equals(oldMemory, newMemory);
assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing");
}, "Zero initial with valueOf");
test(() => {
const argument = { "initial": 3 };
const memory = new WebAssembly.Memory(argument);
const oldMemory = memory.buffer;
assert_ArrayBuffer(oldMemory, { "size": 3 }, "Buffer before growing");
const result = memory.grow(2);
assert_equals(result, 3);
const newMemory = memory.buffer;
assert_not_equals(oldMemory, newMemory);
assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
assert_ArrayBuffer(newMemory, { "size": 5 }, "New buffer after growing");
}, "Non-zero initial");
test(() => {
const argument = { "initial": 0, "maximum": 2 };
const memory = new WebAssembly.Memory(argument);
const oldMemory = memory.buffer;
assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
const result = memory.grow(2);
assert_equals(result, 0);
const newMemory = memory.buffer;
assert_not_equals(oldMemory, newMemory);
assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing");
}, "Zero initial with respected maximum");
test(() => {
const argument = { "initial": 0, "maximum": 2 };
const memory = new WebAssembly.Memory(argument);
const oldMemory = memory.buffer;
assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
const result = memory.grow(1);
assert_equals(result, 0);
const newMemory = memory.buffer;
assert_not_equals(oldMemory, newMemory);
assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing once");
assert_ArrayBuffer(newMemory, { "size": 1 }, "New buffer after growing once");
const result2 = memory.grow(1);
assert_equals(result2, 1);
const newestMemory = memory.buffer;
assert_not_equals(newMemory, newestMemory);
assert_ArrayBuffer(oldMemory, { "detached": true }, "New buffer after growing twice");
assert_ArrayBuffer(newMemory, { "detached": true }, "New buffer after growing twice");
assert_ArrayBuffer(newestMemory, { "size": 2 }, "Newest buffer after growing twice");
}, "Zero initial with respected maximum grown twice");
test(() => {
const argument = { "initial": 1, "maximum": 2 };
const memory = new WebAssembly.Memory(argument);
const oldMemory = memory.buffer;
assert_ArrayBuffer(oldMemory, { "size": 1 }, "Buffer before growing");
assert_throws_js(RangeError, () => memory.grow(2));
assert_equals(memory.buffer, oldMemory);
assert_ArrayBuffer(memory.buffer, { "size": 1 }, "Buffer before trying to grow");
}, "Zero initial growing too much");
const outOfRangeValues = [
undefined,
NaN,
Infinity,
-Infinity,
-1,
0x100000000,
0x1000000000,
"0x100000000",
{ valueOf() { return 0x100000000; } },
];
for (const value of outOfRangeValues) {
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
assert_throws_js(TypeError, () => memory.grow(value));
}, `Out-of-range argument: ${format_value(value)}`);
}
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
const oldMemory = memory.buffer;
assert_ArrayBuffer(oldMemory, { "size": 0 }, "Buffer before growing");
const result = memory.grow(2, {});
assert_equals(result, 0);
const newMemory = memory.buffer;
assert_not_equals(oldMemory, newMemory);
assert_ArrayBuffer(oldMemory, { "detached": true }, "Old buffer after growing");
assert_ArrayBuffer(newMemory, { "size": 2 }, "New buffer after growing");
}, "Stray argument");
test(() => {
const argument = { "initial": 1, "maximum": 2, "shared": true };
const memory = new WebAssembly.Memory(argument);
const oldMemory = memory.buffer;
assert_ArrayBuffer(oldMemory, { "size": 1, "shared": true }, "Buffer before growing");
const result = memory.grow(1);
assert_equals(result, 1);
const newMemory = memory.buffer;
assert_not_equals(oldMemory, newMemory);
assert_ArrayBuffer(oldMemory, { "size": 1, "shared": true }, "Old buffer after growing");
assert_ArrayBuffer(newMemory, { "size": 2, "shared": true }, "New buffer after growing");
// The old and new buffers must have the same value for the
// [[ArrayBufferData]] internal slot.
const oldArray = new Uint8Array(oldMemory);
const newArray = new Uint8Array(newMemory);
assert_equals(oldArray[0], 0, "old first element");
assert_equals(newArray[0], 0, "new first element");
oldArray[0] = 1;
assert_equals(oldArray[0], 1, "old first element");
assert_equals(newArray[0], 1, "new first element");
}, "Growing shared memory does not detach old buffer");

View File

@ -0,0 +1,17 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const argument = { "initial": 0 };
const memory = new WebAssembly.Memory(argument);
assert_class_string(memory, "WebAssembly.Memory");
}, "Object.prototype.toString on an Memory");
test(() => {
assert_own_property(WebAssembly.Memory.prototype, Symbol.toStringTag);
const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, Symbol.toStringTag);
assert_equals(propDesc.value, "WebAssembly.Memory", "value");
assert_equals(propDesc.configurable, true, "configurable");
assert_equals(propDesc.enumerable, false, "enumerable");
assert_equals(propDesc.writable, false, "writable");
}, "@@toStringTag exists on the prototype with the appropriate descriptor");

View File

@ -0,0 +1,37 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function assert_type(argument) {
const memory = new WebAssembly.Memory(argument);
const memorytype = memory.type()
assert_equals(memorytype.minimum, argument.minimum);
assert_equals(memorytype.maximum, argument.maximum);
if (argument.shared !== undefined) {
assert_equals(memorytype.shared, argument.shared);
}
}
test(() => {
assert_type({ "minimum": 0 });
}, "Zero initial, no maximum");
test(() => {
assert_type({ "minimum": 5 });
}, "Non-zero initial, no maximum");
test(() => {
assert_type({ "minimum": 0, "maximum": 0 });
}, "Zero maximum");
test(() => {
assert_type({ "minimum": 0, "maximum": 5 });
}, "None-zero maximum");
test(() => {
assert_type({ "minimum": 0, "maximum": 10, "shared": false});
}, "non-shared memory");
test(() => {
assert_type({ "minimum": 0, "maximum": 10, "shared": true});
}, "shared memory");

View File

@ -0,0 +1,69 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=/wasm/jsapi/assertions.js
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
test(() => {
assert_function_name(WebAssembly.Module, "Module", "WebAssembly.Module");
}, "name");
test(() => {
assert_function_length(WebAssembly.Module, 1, "WebAssembly.Module");
}, "length");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Module());
}, "No arguments");
test(() => {
assert_throws_js(TypeError, () => WebAssembly.Module(emptyModuleBinary));
}, "Calling");
test(() => {
const invalidArguments = [
undefined,
null,
true,
"test",
Symbol(),
7,
NaN,
{},
ArrayBuffer,
ArrayBuffer.prototype,
Array.from(emptyModuleBinary),
];
for (const argument of invalidArguments) {
assert_throws_js(TypeError, () => new WebAssembly.Module(argument),
`new Module(${format_value(argument)})`);
}
}, "Invalid arguments");
test(() => {
const buffer = new Uint8Array();
assert_throws_js(WebAssembly.CompileError, () => new WebAssembly.Module(buffer));
}, "Empty buffer");
test(() => {
const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0]));
assert_throws_js(WebAssembly.CompileError, () => new WebAssembly.Module(buffer));
}, "Invalid code");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype);
}, "Prototype");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
assert_true(Object.isExtensible(module));
}, "Extensibility");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary, {});
assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype);
}, "Stray argument");

View File

@ -0,0 +1,140 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
function assert_ArrayBuffer(buffer, expected) {
assert_equals(Object.getPrototypeOf(buffer), ArrayBuffer.prototype, "Prototype");
assert_true(Object.isExtensible(buffer), "isExtensible");
assert_array_equals(new Uint8Array(buffer), expected);
}
function assert_sections(sections, expected) {
assert_true(Array.isArray(sections), "Should be array");
assert_equals(Object.getPrototypeOf(sections), Array.prototype, "Prototype");
assert_true(Object.isExtensible(sections), "isExtensible");
assert_equals(sections.length, expected.length);
for (let i = 0; i < expected.length; ++i) {
assert_ArrayBuffer(sections[i], expected[i]);
}
}
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
test(() => {
assert_throws_js(TypeError, () => WebAssembly.Module.customSections());
const module = new WebAssembly.Module(emptyModuleBinary);
assert_throws_js(TypeError, () => WebAssembly.Module.customSections(module));
}, "Missing arguments");
test(() => {
const invalidArguments = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Module,
WebAssembly.Module.prototype,
];
for (const argument of invalidArguments) {
assert_throws_js(TypeError, () => WebAssembly.Module.customSections(argument, ""),
`customSections(${format_value(argument)})`);
}
}, "Non-Module arguments");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const fn = WebAssembly.Module.customSections;
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Module,
WebAssembly.Module.prototype,
];
for (const thisValue of thisValues) {
assert_sections(fn.call(thisValue, module, ""), []);
}
}, "Branding");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
assert_sections(WebAssembly.Module.customSections(module, ""), []);
}, "Empty module");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
assert_not_equals(WebAssembly.Module.customSections(module, ""),
WebAssembly.Module.customSections(module, ""));
}, "Empty module: array caching");
test(() => {
const bytes1 = [87, 101, 98, 65, 115, 115, 101, 109, 98, 108, 121];
const bytes2 = [74, 83, 65, 80, 73];
const builder = new WasmModuleBuilder();
builder.addCustomSection("name", bytes1);
builder.addCustomSection("name", bytes2);
builder.addCustomSection("foo", bytes1);
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
assert_sections(WebAssembly.Module.customSections(module, "name"), [
bytes1,
bytes2,
])
assert_sections(WebAssembly.Module.customSections(module, "foo"), [
bytes1,
])
assert_sections(WebAssembly.Module.customSections(module, ""), [])
assert_sections(WebAssembly.Module.customSections(module, "\0"), [])
assert_sections(WebAssembly.Module.customSections(module, "name\0"), [])
assert_sections(WebAssembly.Module.customSections(module, "foo\0"), [])
}, "Custom sections");
test(() => {
const bytes = [87, 101, 98, 65, 115, 115, 101, 109, 98, 108, 121];
const name = "yee\uD801\uDC37eey"
const builder = new WasmModuleBuilder();
builder.addCustomSection(name, bytes);
const buffer = builder.toBuffer();
const module = new WebAssembly.Module(buffer);
assert_sections(WebAssembly.Module.customSections(module, name), [
bytes,
]);
assert_sections(WebAssembly.Module.customSections(module, "yee\uFFFDeey"), []);
assert_sections(WebAssembly.Module.customSections(module, "yee\uFFFD\uFFFDeey"), []);
}, "Custom sections with surrogate pairs");
test(() => {
const bytes = [87, 101, 98, 65, 115, 115, 101, 109, 98, 108, 121];
const builder = new WasmModuleBuilder();
builder.addCustomSection("na\uFFFDme", bytes);
const buffer = builder.toBuffer();
const module = new WebAssembly.Module(buffer);
assert_sections(WebAssembly.Module.customSections(module, "name"), []);
assert_sections(WebAssembly.Module.customSections(module, "na\uFFFDme"), [
bytes,
]);
assert_sections(WebAssembly.Module.customSections(module, "na\uDC01me"), []);
}, "Custom sections with U+FFFD");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
assert_sections(WebAssembly.Module.customSections(module, "", {}), []);
}, "Stray argument");

View File

@ -0,0 +1,185 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
function assert_ModuleExportDescriptor(export_, expected) {
assert_equals(Object.getPrototypeOf(export_), Object.prototype, "Prototype");
assert_true(Object.isExtensible(export_), "isExtensible");
const name = Object.getOwnPropertyDescriptor(export_, "name");
assert_true(name.writable, "name: writable");
assert_true(name.enumerable, "name: enumerable");
assert_true(name.configurable, "name: configurable");
assert_equals(name.value, expected.name);
const kind = Object.getOwnPropertyDescriptor(export_, "kind");
assert_true(kind.writable, "kind: writable");
assert_true(kind.enumerable, "kind: enumerable");
assert_true(kind.configurable, "kind: configurable");
assert_equals(kind.value, expected.kind);
}
function assert_exports(exports, expected) {
assert_true(Array.isArray(exports), "Should be array");
assert_equals(Object.getPrototypeOf(exports), Array.prototype, "Prototype");
assert_true(Object.isExtensible(exports), "isExtensible");
assert_equals(exports.length, expected.length);
for (let i = 0; i < expected.length; ++i) {
assert_ModuleExportDescriptor(exports[i], expected[i]);
}
}
test(() => {
assert_throws_js(TypeError, () => WebAssembly.Module.exports());
}, "Missing arguments");
test(() => {
const invalidArguments = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Module,
WebAssembly.Module.prototype,
];
for (const argument of invalidArguments) {
assert_throws_js(TypeError, () => WebAssembly.Module.exports(argument),
`exports(${format_value(argument)})`);
}
}, "Non-Module arguments");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const fn = WebAssembly.Module.exports;
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Module,
WebAssembly.Module.prototype,
];
for (const thisValue of thisValues) {
assert_array_equals(fn.call(thisValue, module), []);
}
}, "Branding");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const exports = WebAssembly.Module.exports(module);
assert_true(Array.isArray(exports));
}, "Return type");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const exports = WebAssembly.Module.exports(module);
assert_exports(exports, []);
}, "Empty module");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
assert_not_equals(WebAssembly.Module.exports(module), WebAssembly.Module.exports(module));
}, "Empty module: array caching");
test(() => {
const builder = new WasmModuleBuilder();
builder
.addFunction("fn", kSig_v_v)
.addBody([])
.exportFunc();
builder
.addFunction("fn2", kSig_v_v)
.addBody([])
.exportFunc();
builder.setTableBounds(1);
builder.addExportOfKind("table", kExternalTable, 0);
builder.addGlobal(kWasmI32, true)
.exportAs("global")
.init = 7;
builder.addGlobal(kWasmF64, true)
.exportAs("global2")
.init = 1.2;
builder.addMemory(0, 256, true);
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const exports = WebAssembly.Module.exports(module);
const expected = [
{ "kind": "function", "name": "fn" },
{ "kind": "function", "name": "fn2" },
{ "kind": "table", "name": "table" },
{ "kind": "global", "name": "global" },
{ "kind": "global", "name": "global2" },
{ "kind": "memory", "name": "memory" },
];
assert_exports(exports, expected);
}, "exports");
test(() => {
const builder = new WasmModuleBuilder();
builder
.addFunction("", kSig_v_v)
.addBody([])
.exportFunc();
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const exports = WebAssembly.Module.exports(module);
const expected = [
{ "kind": "function", "name": "" },
];
assert_exports(exports, expected);
}, "exports with empty name: function");
test(() => {
const builder = new WasmModuleBuilder();
builder.setTableBounds(1);
builder.addExportOfKind("", kExternalTable, 0);
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const exports = WebAssembly.Module.exports(module);
const expected = [
{ "kind": "table", "name": "" },
];
assert_exports(exports, expected);
}, "exports with empty name: table");
test(() => {
const builder = new WasmModuleBuilder();
builder.addGlobal(kWasmI32, true)
.exportAs("")
.init = 7;
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const exports = WebAssembly.Module.exports(module);
const expected = [
{ "kind": "global", "name": "" },
];
assert_exports(exports, expected);
}, "exports with empty name: global");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const exports = WebAssembly.Module.exports(module, {});
assert_exports(exports, []);
}, "Stray argument");

View File

@ -0,0 +1,185 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
function assert_ModuleImportDescriptor(import_, expected) {
assert_equals(Object.getPrototypeOf(import_), Object.prototype, "Prototype");
assert_true(Object.isExtensible(import_), "isExtensible");
const module = Object.getOwnPropertyDescriptor(import_, "module");
assert_true(module.writable, "module: writable");
assert_true(module.enumerable, "module: enumerable");
assert_true(module.configurable, "module: configurable");
assert_equals(module.value, expected.module);
const name = Object.getOwnPropertyDescriptor(import_, "name");
assert_true(name.writable, "name: writable");
assert_true(name.enumerable, "name: enumerable");
assert_true(name.configurable, "name: configurable");
assert_equals(name.value, expected.name);
const kind = Object.getOwnPropertyDescriptor(import_, "kind");
assert_true(kind.writable, "kind: writable");
assert_true(kind.enumerable, "kind: enumerable");
assert_true(kind.configurable, "kind: configurable");
assert_equals(kind.value, expected.kind);
}
function assert_imports(imports, expected) {
assert_true(Array.isArray(imports), "Should be array");
assert_equals(Object.getPrototypeOf(imports), Array.prototype, "Prototype");
assert_true(Object.isExtensible(imports), "isExtensible");
assert_equals(imports.length, expected.length);
for (let i = 0; i < expected.length; ++i) {
assert_ModuleImportDescriptor(imports[i], expected[i]);
}
}
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
test(() => {
assert_throws_js(TypeError, () => WebAssembly.Module.imports());
}, "Missing arguments");
test(() => {
const invalidArguments = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Module,
WebAssembly.Module.prototype,
];
for (const argument of invalidArguments) {
assert_throws_js(TypeError, () => WebAssembly.Module.imports(argument),
`imports(${format_value(argument)})`);
}
}, "Non-Module arguments");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const fn = WebAssembly.Module.imports;
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Module,
WebAssembly.Module.prototype,
];
for (const thisValue of thisValues) {
assert_array_equals(fn.call(thisValue, module), []);
}
}, "Branding");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const imports = WebAssembly.Module.imports(module);
assert_true(Array.isArray(imports));
}, "Return type");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const imports = WebAssembly.Module.imports(module);
assert_imports(imports, []);
}, "Empty module");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
assert_not_equals(WebAssembly.Module.imports(module), WebAssembly.Module.imports(module));
}, "Empty module: array caching");
test(() => {
const builder = new WasmModuleBuilder();
builder.addImport("module", "fn", kSig_v_v);
builder.addImportedGlobal("module", "global", kWasmI32);
builder.addImportedMemory("module", "memory", 0, 128);
builder.addImportedTable("module", "table", 0, 128);
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const imports = WebAssembly.Module.imports(module);
const expected = [
{ "module": "module", "kind": "function", "name": "fn" },
{ "module": "module", "kind": "global", "name": "global" },
{ "module": "module", "kind": "memory", "name": "memory" },
{ "module": "module", "kind": "table", "name": "table" },
];
assert_imports(imports, expected);
}, "imports");
test(() => {
const builder = new WasmModuleBuilder();
builder.addImport("", "fn", kSig_v_v);
builder.addImportedGlobal("", "global", kWasmI32);
builder.addImportedMemory("", "memory", 0, 128);
builder.addImportedTable("", "table", 0, 128);
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const imports = WebAssembly.Module.imports(module);
const expected = [
{ "module": "", "kind": "function", "name": "fn" },
{ "module": "", "kind": "global", "name": "global" },
{ "module": "", "kind": "memory", "name": "memory" },
{ "module": "", "kind": "table", "name": "table" },
];
assert_imports(imports, expected);
}, "imports with empty module name");
test(() => {
const builder = new WasmModuleBuilder();
builder.addImport("a", "", kSig_v_v);
builder.addImportedGlobal("b", "", kWasmI32);
builder.addImportedMemory("c", "", 0, 128);
builder.addImportedTable("d", "", 0, 128);
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const imports = WebAssembly.Module.imports(module);
const expected = [
{ "module": "a", "kind": "function", "name": "" },
{ "module": "b", "kind": "global", "name": "" },
{ "module": "c", "kind": "memory", "name": "" },
{ "module": "d", "kind": "table", "name": "" },
];
assert_imports(imports, expected);
}, "imports with empty names");
test(() => {
const builder = new WasmModuleBuilder();
builder.addImport("", "", kSig_v_v);
builder.addImportedGlobal("", "", kWasmI32);
builder.addImportedMemory("", "", 0, 128);
builder.addImportedTable("", "", 0, 128);
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const imports = WebAssembly.Module.imports(module);
const expected = [
{ "module": "", "kind": "function", "name": "" },
{ "module": "", "kind": "global", "name": "" },
{ "module": "", "kind": "memory", "name": "" },
{ "module": "", "kind": "table", "name": "" },
];
assert_imports(imports, expected);
}, "imports with empty module names and names");
test(() => {
const module = new WebAssembly.Module(emptyModuleBinary);
const imports = WebAssembly.Module.imports(module, {});
assert_imports(imports, []);
}, "Stray argument");

View File

@ -0,0 +1,18 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
test(() => {
const emptyModuleBinary = new WasmModuleBuilder().toBuffer();
const module = new WebAssembly.Module(emptyModuleBinary);
assert_class_string(module, "WebAssembly.Module");
}, "Object.prototype.toString on an Module");
test(() => {
assert_own_property(WebAssembly.Module.prototype, Symbol.toStringTag);
const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Module.prototype, Symbol.toStringTag);
assert_equals(propDesc.value, "WebAssembly.Module", "value");
assert_equals(propDesc.configurable, true, "configurable");
assert_equals(propDesc.enumerable, false, "enumerable");
assert_equals(propDesc.writable, false, "writable");
}, "@@toStringTag exists on the prototype with the appropriate descriptor");

View File

@ -0,0 +1,95 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>WebAssembly JS API: Default [[Prototype]] value is from NewTarget's Realm</title>
<link rel="help" href="https://webidl.spec.whatwg.org/#internally-create-a-new-object-implementing-the-interface">
<link rel="help" href="https://tc39.es/ecma262/#sec-nativeerror">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="wasm-module-builder.js"></script>
<body>
<iframe id="constructor-iframe" hidden></iframe>
<iframe id="new-target-iframe" hidden></iframe>
<iframe id="other-iframe" hidden></iframe>
<script>
"use strict";
const constructorRealm = document.querySelector("#constructor-iframe").contentWindow;
const newTargetRealm = document.querySelector("#new-target-iframe").contentWindow;
const otherRealm = document.querySelector("#other-iframe").contentWindow;
const emptyModuleBinary = new WasmModuleBuilder().toBuffer();
const interfaces = [
["Module", emptyModuleBinary],
["Instance", new WebAssembly.Module(emptyModuleBinary)],
["Memory", {initial: 0}],
["Table", {element: "anyfunc", initial: 0}],
["Global", {value: "i32"}],
// See step 2 of https://tc39.es/ecma262/#sec-nativeerror
["CompileError"],
["LinkError"],
["RuntimeError"],
];
const primitives = [
undefined,
null,
false,
true,
0,
-1,
"",
"str",
Symbol(),
];
const getNewTargets = function* (realm) {
for (const primitive of primitives) {
const newTarget = new realm.Function();
newTarget.prototype = primitive;
yield [newTarget, "cross-realm NewTarget with `" + format_value(primitive) + "` prototype"];
}
// GetFunctionRealm (https://tc39.es/ecma262/#sec-getfunctionrealm) coverage:
const bindOther = otherRealm.Function.prototype.bind;
const ProxyOther = otherRealm.Proxy;
const bound = new realm.Function();
bound.prototype = undefined;
yield [bindOther.call(bound), "bound cross-realm NewTarget with `undefined` prototype"];
const boundBound = new realm.Function();
boundBound.prototype = null;
yield [bindOther.call(bindOther.call(boundBound)), "bound bound cross-realm NewTarget with `null` prototype"];
const boundProxy = new realm.Function();
boundProxy.prototype = false;
yield [bindOther.call(new ProxyOther(boundProxy, {})), "bound Proxy of cross-realm NewTarget with `false` prototype"];
const proxy = new realm.Function();
proxy.prototype = true;
yield [new ProxyOther(proxy, {}), "Proxy of cross-realm NewTarget with `true` prototype"];
const proxyProxy = new realm.Function();
proxyProxy.prototype = -0;
yield [new ProxyOther(new ProxyOther(proxyProxy, {}), {}), "Proxy of Proxy of cross-realm NewTarget with `-0` prototype"];
const proxyBound = new realm.Function();
proxyBound.prototype = NaN;
yield [new ProxyOther(bindOther.call(proxyBound), {}), "Proxy of bound cross-realm NewTarget with `NaN` prototype"];
};
for (const [interfaceName, constructorArg] of interfaces) {
for (const [newTarget, testDescription] of getNewTargets(newTargetRealm)) {
test(() => {
const Constructor = constructorRealm.WebAssembly[interfaceName];
const object = Reflect.construct(Constructor, [constructorArg], newTarget);
const NewTargetConstructor = newTargetRealm.WebAssembly[interfaceName];
assert_true(object instanceof NewTargetConstructor);
assert_equals(Object.getPrototypeOf(object), NewTargetConstructor.prototype);
}, `WebAssembly.${interfaceName}: ${testDescription}`);
}
}
</script>
</body>

View File

@ -0,0 +1,43 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/wasm-module-builder.js
let emptyModuleBinary;
setup(() => {
emptyModuleBinary = new WasmModuleBuilder().toBuffer();
});
test(() => {
class _Module extends WebAssembly.Module {}
let module = new _Module(emptyModuleBinary);
assert_true(module instanceof _Module, "_Module instanceof _Module");
assert_true(module instanceof WebAssembly.Module, "_Module instanceof WebAssembly.Module");
}, "_Module");
test(() => {
class _Instance extends WebAssembly.Instance {}
let instance = new _Instance(new WebAssembly.Module(emptyModuleBinary));
assert_true(instance instanceof _Instance, "_Instance instanceof _Instance");
assert_true(instance instanceof WebAssembly.Instance, "_Instance instanceof WebAssembly.Instance");
}, "_Instance");
test(() => {
class _Memory extends WebAssembly.Memory {}
let memory = new _Memory({initial: 0, maximum: 1});
assert_true(memory instanceof _Memory, "_Memory instanceof _Memory");
assert_true(memory instanceof WebAssembly.Memory, "_Memory instanceof WebAssembly.Memory");
}, "_Memory");
test(() => {
class _Table extends WebAssembly.Table {}
let table = new _Table({initial: 0, element: "anyfunc"});
assert_true(table instanceof _Table, "_Table instanceof _Table");
assert_true(table instanceof WebAssembly.Table, "_Table instanceof WebAssembly.Table");
}, "_Table");
test(() => {
class _Global extends WebAssembly.Global {}
let global = new _Global({value: "i32", mutable: false}, 0);
assert_true(global instanceof _Global, "_Global instanceof _Global");
assert_true(global instanceof WebAssembly.Global, "_Global instanceof WebAssembly.Global");
}, "_Global");

View File

@ -0,0 +1,24 @@
function assert_equal_to_array(table, expected, message) {
assert_equals(table.length, expected.length, `${message}: length`);
// The argument check in get() happens before the range check, and negative numbers
// are illegal, hence will throw TypeError per spec.
assert_throws_js(TypeError, () => table.get(-1), `${message}: table.get(-1)`);
for (let i = 0; i < expected.length; ++i) {
assert_equals(table.get(i), expected[i], `${message}: table.get(${i} of ${expected.length})`);
}
assert_throws_js(RangeError, () => table.get(expected.length),
`${message}: table.get(${expected.length} of ${expected.length})`);
assert_throws_js(RangeError, () => table.get(expected.length + 1),
`${message}: table.get(${expected.length + 1} of ${expected.length})`);
}
function assert_Table(actual, expected) {
assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype,
"prototype");
assert_true(Object.isExtensible(actual), "extensible");
assert_equals(actual.length, expected.length, "length");
for (let i = 0; i < expected.length; ++i) {
assert_equals(actual.get(i), null, `actual.get(${i})`);
}
}

View File

@ -0,0 +1,20 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/table/assertions.js
test(() => {
const argument = { "element": "anyfunc", "initial": 0, "minimum": 0 };
assert_throws_js(TypeError, () => new WebAssembly.Table(argument));
}, "Initializing with both initial and minimum");
test(() => {
const argument = { "element": "anyfunc", "minimum": 0 };
const table = new WebAssembly.Table(argument);
assert_Table(table, { "length": 0 });
}, "Zero minimum");
test(() => {
const argument = { "element": "anyfunc", "minimum": 5 };
const table = new WebAssembly.Table(argument);
assert_Table(table, { "length": 5 });
}, "Non-zero minimum");

View File

@ -0,0 +1,208 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/table/assertions.js
test(() => {
assert_function_name(WebAssembly.Table, "Table", "WebAssembly.Table");
}, "name");
test(() => {
assert_function_length(WebAssembly.Table, 1, "WebAssembly.Table");
}, "length");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Table());
}, "No arguments");
test(() => {
const argument = { "element": "anyfunc", "initial": 0 };
assert_throws_js(TypeError, () => WebAssembly.Table(argument));
}, "Calling");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Table({}));
}, "Empty descriptor");
test(() => {
const invalidArguments = [
undefined,
null,
false,
true,
"",
"test",
Symbol(),
1,
NaN,
{},
];
for (const invalidArgument of invalidArguments) {
assert_throws_js(TypeError,
() => new WebAssembly.Table(invalidArgument),
`new Table(${format_value(invalidArgument)})`);
}
}, "Invalid descriptor argument");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": undefined }));
}, "Undefined initial value in descriptor");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": undefined, "initial": 0 }));
}, "Undefined element value in descriptor");
const outOfRangeValues = [
NaN,
Infinity,
-Infinity,
-1,
0x100000000,
0x1000000000,
];
for (const value of outOfRangeValues) {
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": value }));
}, `Out-of-range initial value in descriptor: ${format_value(value)}`);
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": 0, "maximum": value }));
}, `Out-of-range maximum value in descriptor: ${format_value(value)}`);
}
test(() => {
assert_throws_js(RangeError, () => new WebAssembly.Table({ "element": "anyfunc", "initial": 10, "maximum": 9 }));
}, "Initial value exceeds maximum");
test(() => {
const argument = { "element": "anyfunc", "initial": 0 };
const table = new WebAssembly.Table(argument);
assert_Table(table, { "length": 0 });
}, "Basic (zero)");
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_Table(table, { "length": 5 });
}, "Basic (non-zero)");
test(() => {
const argument = { "element": "anyfunc", "initial": 0 };
const table = new WebAssembly.Table(argument, null, {});
assert_Table(table, { "length": 0 });
}, "Stray argument");
test(() => {
const proxy = new Proxy({}, {
has(o, x) {
assert_unreached(`Should not call [[HasProperty]] with ${x}`);
},
get(o, x) {
switch (x) {
case "element":
return "anyfunc";
case "initial":
case "maximum":
return 0;
default:
return undefined;
}
},
});
const table = new WebAssembly.Table(proxy);
assert_Table(table, { "length": 0 });
}, "Proxy descriptor");
test(() => {
const table = new WebAssembly.Table({
"element": {
toString() { return "anyfunc"; },
},
"initial": 1,
});
assert_Table(table, { "length": 1 });
}, "Type conversion for descriptor.element");
test(() => {
const order = [];
new WebAssembly.Table({
get maximum() {
order.push("maximum");
return {
valueOf() {
order.push("maximum valueOf");
return 1;
},
};
},
get initial() {
order.push("initial");
return {
valueOf() {
order.push("initial valueOf");
return 1;
},
};
},
get element() {
order.push("element");
return {
toString() {
order.push("element toString");
return "anyfunc";
},
};
},
});
assert_array_equals(order, [
"element",
"element toString",
"initial",
"initial valueOf",
"maximum",
"maximum valueOf",
]);
}, "Order of evaluation for descriptor");
test(() => {
const testObject = {};
const argument = { "element": "externref", "initial": 3 };
const table = new WebAssembly.Table(argument, testObject);
assert_equals(table.length, 3);
assert_equals(table.get(0), testObject);
assert_equals(table.get(1), testObject);
assert_equals(table.get(2), testObject);
}, "initialize externref table with default value");
test(() => {
const argument = { "element": "i32", "initial": 3 };
assert_throws_js(TypeError, () => new WebAssembly.Table(argument));
}, "initialize table with a wrong element value");
test(() => {
const builder = new WasmModuleBuilder();
builder
.addFunction("fn", kSig_v_v)
.addBody([])
.exportFunc();
const bin = builder.toBuffer();
const fn = new WebAssembly.Instance(new WebAssembly.Module(bin)).exports.fn;
const argument = { "element": "anyfunc", "initial": 3 };
const table = new WebAssembly.Table(argument, fn);
assert_equals(table.length, 3);
assert_equals(table.get(0), fn);
assert_equals(table.get(1), fn);
assert_equals(table.get(2), fn);
}, "initialize anyfunc table with default value");
test(() => {
const argument = { "element": "anyfunc", "initial": 3 };
assert_throws_js(TypeError, () => new WebAssembly.Table(argument, {}));
assert_throws_js(TypeError, () => new WebAssembly.Table(argument, "cannot be used as a wasm function"));
assert_throws_js(TypeError, () => new WebAssembly.Table(argument, 37));
}, "initialize anyfunc table with a bad default value");

View File

@ -0,0 +1,263 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=assertions.js
let functions = {};
setup(() => {
const builder = new WasmModuleBuilder();
builder
.addFunction("fn", kSig_v_d)
.addBody([])
.exportFunc();
builder
.addFunction("fn2", kSig_v_v)
.addBody([])
.exportFunc();
const buffer = builder.toBuffer()
const module = new WebAssembly.Module(buffer);
const instance = new WebAssembly.Instance(module, {});
functions = instance.exports;
});
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_throws_js(TypeError, () => table.get());
}, "Missing arguments: get");
test(t => {
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Table,
WebAssembly.Table.prototype,
];
const argument = {
valueOf: t.unreached_func("Should not touch the argument (valueOf)"),
toString: t.unreached_func("Should not touch the argument (toString)"),
};
const fn = WebAssembly.Table.prototype.get;
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`);
}
}, "Branding: get");
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_throws_js(TypeError, () => table.set());
}, "Missing arguments: set");
test(t => {
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Table,
WebAssembly.Table.prototype,
];
const argument = {
valueOf: t.unreached_func("Should not touch the argument (valueOf)"),
toString: t.unreached_func("Should not touch the argument (toString)"),
};
const fn = WebAssembly.Table.prototype.set;
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => fn.call(thisValue, argument, null), `this=${format_value(thisValue)}`);
}
}, "Branding: set");
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, [null, null, null, null, null]);
const {fn, fn2} = functions;
assert_equals(table.set(0, fn), undefined, "set() returns undefined.");
table.set(2, fn2);
table.set(4, fn);
assert_equal_to_array(table, [fn, null, fn2, null, fn]);
table.set(0, null);
assert_equal_to_array(table, [null, null, fn2, null, fn]);
}, "Basic");
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, [null, null, null, null, null]);
const {fn, fn2} = functions;
table.set(0, fn);
table.set(2, fn2);
table.set(4, fn);
assert_equal_to_array(table, [fn, null, fn2, null, fn]);
table.grow(4);
assert_equal_to_array(table, [fn, null, fn2, null, fn, null, null, null, null]);
}, "Growing");
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, [null, null, null, null, null]);
const {fn} = functions;
// -1 is the wrong type hence the type check on entry gets this
// before the range check does.
assert_throws_js(TypeError, () => table.set(-1, fn));
assert_throws_js(RangeError, () => table.set(5, fn));
assert_equal_to_array(table, [null, null, null, null, null]);
}, "Setting out-of-bounds");
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, [null]);
const invalidArguments = [
undefined,
true,
false,
"test",
Symbol(),
7,
NaN,
{},
];
for (const argument of invalidArguments) {
assert_throws_js(TypeError, () => table.set(0, argument),
`set(${format_value(argument)})`);
}
assert_equal_to_array(table, [null]);
}, "Setting non-function");
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, [null]);
const fn = function() {};
assert_throws_js(TypeError, () => table.set(0, fn));
assert_equal_to_array(table, [null]);
}, "Setting non-wasm function");
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, [null]);
const fn = () => {};
assert_throws_js(TypeError, () => table.set(0, fn));
assert_equal_to_array(table, [null]);
}, "Setting non-wasm arrow function");
const outOfRangeValues = [
undefined,
NaN,
Infinity,
-Infinity,
-1,
0x100000000,
0x1000000000,
"0x100000000",
{ valueOf() { return 0x100000000; } },
];
for (const value of outOfRangeValues) {
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_throws_js(TypeError, () => table.get(value));
}, `Getting out-of-range argument: ${format_value(value)}`);
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_throws_js(TypeError, () => table.set(value, null));
}, `Setting out-of-range argument: ${format_value(value)}`);
}
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
let called = 0;
const value = {
valueOf() {
called++;
return 0;
},
};
assert_throws_js(TypeError, () => table.set(value, {}));
assert_equals(called, 1);
}, "Order of argument conversion");
test(() => {
const {fn} = functions;
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_equals(table.get(0, {}), null);
assert_equals(table.set(0, fn, {}), undefined);
}, "Stray argument");
test(() => {
const builder = new WasmModuleBuilder();
builder
.addFunction("fn", kSig_v_v)
.addBody([])
.exportFunc();
const bin = builder.toBuffer();
const fn = new WebAssembly.Instance(new WebAssembly.Module(bin)).exports.fn;
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument, fn);
assert_equals(table.get(0), fn);
table.set(0);
assert_equals(table.get(0), null);
table.set(0, fn);
assert_equals(table.get(0), fn);
assert_throws_js(TypeError, () => table.set(0, {}));
assert_throws_js(TypeError, () => table.set(0, 37));
}, "Arguments for anyfunc table set");
test(() => {
const testObject = {};
const argument = { "element": "externref", "initial": 1 };
const table = new WebAssembly.Table(argument, testObject);
assert_equals(table.get(0), testObject);
table.set(0);
assert_equals(table.get(0), undefined);
table.set(0, testObject);
assert_equals(table.get(0), testObject);
table.set(0, 37);
assert_equals(table.get(0), 37);
}, "Arguments for externref table set");

View File

@ -0,0 +1,126 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/wasm-module-builder.js
// META: script=assertions.js
function nulls(n) {
return Array(n).fill(null);
}
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_throws_js(TypeError, () => table.grow());
}, "Missing arguments");
test(t => {
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Table,
WebAssembly.Table.prototype,
];
const argument = {
valueOf: t.unreached_func("Should not touch the argument (valueOf)"),
toString: t.unreached_func("Should not touch the argument (toString)"),
};
const fn = WebAssembly.Table.prototype.grow;
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`);
}
}, "Branding");
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, nulls(5), "before");
const result = table.grow(3);
assert_equals(result, 5);
assert_equal_to_array(table, nulls(8), "after");
}, "Basic");
test(() => {
const argument = { "element": "anyfunc", "initial": 3, "maximum": 5 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, nulls(3), "before");
const result = table.grow(2);
assert_equals(result, 3);
assert_equal_to_array(table, nulls(5), "after");
}, "Reached maximum");
test(() => {
const argument = { "element": "anyfunc", "initial": 2, "maximum": 5 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, nulls(2), "before");
assert_throws_js(RangeError, () => table.grow(4));
assert_equal_to_array(table, nulls(2), "after");
}, "Exceeded maximum");
const outOfRangeValues = [
undefined,
NaN,
Infinity,
-Infinity,
-1,
0x100000000,
0x1000000000,
"0x100000000",
{ valueOf() { return 0x100000000; } },
];
for (const value of outOfRangeValues) {
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_throws_js(TypeError, () => table.grow(value));
}, `Out-of-range argument: ${format_value(value)}`);
}
test(() => {
const argument = { "element": "anyfunc", "initial": 5 };
const table = new WebAssembly.Table(argument);
assert_equal_to_array(table, nulls(5), "before");
const result = table.grow(3, null, {});
assert_equals(result, 5);
assert_equal_to_array(table, nulls(8), "after");
}, "Stray argument");
test(() => {
const builder = new WasmModuleBuilder();
builder
.addFunction("fn", kSig_v_v)
.addBody([])
.exportFunc();
const bin = builder.toBuffer()
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
const fn = new WebAssembly.Instance(new WebAssembly.Module(bin)).exports.fn;
const result = table.grow(2, fn);
assert_equals(result, 1);
assert_equals(table.get(0), null);
assert_equals(table.get(1), fn);
assert_equals(table.get(2), fn);
}, "Grow with exported-function argument");
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_throws_js(TypeError, () => table.grow(2, {}));
}, "Grow with non-function argument");
test(() => {
const argument = { "element": "anyfunc", "initial": 1 };
const table = new WebAssembly.Table(argument);
assert_throws_js(TypeError, () => table.grow(2, () => true));
}, "Grow with JS-function argument");

View File

@ -0,0 +1,60 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const thisValues = [
undefined,
null,
true,
"",
Symbol(),
1,
{},
WebAssembly.Table,
WebAssembly.Table.prototype,
];
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length");
assert_equals(typeof desc, "object");
const getter = desc.get;
assert_equals(typeof getter, "function");
assert_equals(typeof desc.set, "undefined");
for (const thisValue of thisValues) {
assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`);
}
}, "Branding");
test(() => {
const argument = { "element": "anyfunc", "initial": 2 };
const table = new WebAssembly.Table(argument);
assert_equals(table.length, 2, "Initial length");
const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length");
assert_equals(typeof desc, "object");
const getter = desc.get;
assert_equals(typeof getter, "function");
assert_equals(getter.call(table, {}), 2);
}, "Stray argument");
test(() => {
const argument = { "element": "anyfunc", "initial": 2 };
const table = new WebAssembly.Table(argument);
assert_equals(table.length, 2, "Initial length");
table.length = 4;
assert_equals(table.length, 2, "Should not change the length");
}, "Setting (sloppy mode)");
test(() => {
const argument = { "element": "anyfunc", "initial": 2 };
const table = new WebAssembly.Table(argument);
assert_equals(table.length, 2, "Initial length");
assert_throws_js(TypeError, () => {
"use strict";
table.length = 4;
});
assert_equals(table.length, 2, "Should not change the length");
}, "Setting (strict mode)");

View File

@ -0,0 +1,17 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const argument = { "element": "anyfunc", "initial": 0 };
const table = new WebAssembly.Table(argument);
assert_class_string(table, "WebAssembly.Table");
}, "Object.prototype.toString on an Table");
test(() => {
assert_own_property(WebAssembly.Table.prototype, Symbol.toStringTag);
const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, Symbol.toStringTag);
assert_equals(propDesc.value, "WebAssembly.Table", "value");
assert_equals(propDesc.configurable, true, "configurable");
assert_equals(propDesc.enumerable, false, "enumerable");
assert_equals(propDesc.writable, false, "writable");
}, "@@toStringTag exists on the prototype with the appropriate descriptor");

View File

@ -0,0 +1,26 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function assert_type(argument) {
const mytable = new WebAssembly.Table(argument);
const tabletype = mytable.type()
assert_equals(tabletype.minimum, argument.minimum);
assert_equals(tabletype.maximum, argument.maximum);
assert_equals(tabletype.element, argument.element);
}
test(() => {
assert_type({ "minimum": 0, "element": "funcref"});
}, "Zero initial, no maximum");
test(() => {
assert_type({ "minimum": 5, "element": "funcref" });
}, "Non-zero initial, no maximum");
test(() => {
assert_type({ "minimum": 0, "maximum": 0, "element": "funcref" });
}, "Zero maximum");
test(() => {
assert_type({ "minimum": 0, "maximum": 5, "element": "funcref" });
}, "Non-zero maximum");

View File

@ -0,0 +1,49 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
test(() => {
assert_function_name(WebAssembly.Tag, "Tag", "WebAssembly.Tag");
}, "name");
test(() => {
assert_function_length(WebAssembly.Tag, 1, "WebAssembly.Tag");
}, "length");
test(() => {
assert_throws_js(TypeError, () => new WebAssembly.Tag());
}, "No arguments");
test(() => {
const argument = { parameters: [] };
assert_throws_js(TypeError, () => WebAssembly.Tag(argument));
}, "Calling");
test(() => {
const invalidArguments = [
undefined,
null,
false,
true,
"",
"test",
Symbol(),
1,
NaN,
{},
];
for (const invalidArgument of invalidArguments) {
assert_throws_js(
TypeError,
() => new WebAssembly.Tag(invalidArgument),
`new Tag(${format_value(invalidArgument)})`
);
}
}, "Invalid descriptor argument");
test(() => {
const invalidTypes = ["i16", "i128", "f16", "f128", "u32", "u64", "i32\0"];
for (const value of invalidTypes) {
const argument = { parameters: [value] };
assert_throws_js(TypeError, () => new WebAssembly.Tag(argument));
}
}, "Invalid type parameter");

View File

@ -0,0 +1,20 @@
// META: global=window,dedicatedworker,jsshell
test(() => {
const argument = { parameters: [] };
const tag = new WebAssembly.Tag(argument);
assert_class_string(tag, "WebAssembly.Tag");
}, "Object.prototype.toString on a Tag");
test(() => {
assert_own_property(WebAssembly.Tag.prototype, Symbol.toStringTag);
const propDesc = Object.getOwnPropertyDescriptor(
WebAssembly.Tag.prototype,
Symbol.toStringTag
);
assert_equals(propDesc.value, "WebAssembly.Tag", "value");
assert_equals(propDesc.configurable, true, "configurable");
assert_equals(propDesc.enumerable, false, "enumerable");
assert_equals(propDesc.writable, false, "writable");
}, "@@toStringTag exists on the prototype with the appropriate descriptor");

View File

@ -0,0 +1,21 @@
// META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js
function assert_type(argument) {
const tag = new WebAssembly.Tag(argument);
const tagtype = tag.type();
assert_array_equals(tagtype.parameters, argument.parameters);
}
test(() => {
assert_type({ parameters: [] });
}, "[]");
test(() => {
assert_type({ parameters: ["i32", "i64"] });
}, "[i32 i64]");
test(() => {
assert_type({ parameters: ["i32", "i64", "f32", "f64"] });
}, "[i32 i64 f32 f64]");

File diff suppressed because it is too large Load Diff