Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorTobias Nießen <tniessen@tnie.de>2022-04-23 06:09:15 +0300
committerGitHub <noreply@github.com>2022-04-23 06:09:15 +0300
commitc4781ea69ce164b1163a10f50d818be3d0a6bca4 (patch)
tree69743d339d2473f2978d75d76716cbbd06b8e409 /test
parenteeb27c2e0aecbf418635a8c4b7c4529385798c63 (diff)
lib,src: implement WebAssembly Web API
Refs: https://github.com/nodejs/node/pull/41749 Fixes: https://github.com/nodejs/node/issues/21130 PR-URL: https://github.com/nodejs/node/pull/42701 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Diffstat (limited to 'test')
-rw-r--r--test/fixtures/wpt/README.md2
-rw-r--r--test/fixtures/wpt/versions.json8
-rw-r--r--test/fixtures/wpt/wasm/jsapi/META.yml1
-rw-r--r--test/fixtures/wpt/wasm/jsapi/assertions.js100
-rw-r--r--test/fixtures/wpt/wasm/jsapi/bad-imports.js185
-rw-r--r--test/fixtures/wpt/wasm/jsapi/constructor/compile.any.js85
-rw-r--r--test/fixtures/wpt/wasm/jsapi/constructor/instantiate-bad-imports.any.js22
-rw-r--r--test/fixtures/wpt/wasm/jsapi/constructor/instantiate.any.js152
-rw-r--r--test/fixtures/wpt/wasm/jsapi/constructor/multi-value.any.js149
-rw-r--r--test/fixtures/wpt/wasm/jsapi/constructor/toStringTag.any.js42
-rw-r--r--test/fixtures/wpt/wasm/jsapi/constructor/validate.any.js99
-rw-r--r--test/fixtures/wpt/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js13
-rw-r--r--test/fixtures/wpt/wasm/jsapi/exception/basic.tentative.any.js121
-rw-r--r--test/fixtures/wpt/wasm/jsapi/exception/constructor.tentative.any.js62
-rw-r--r--test/fixtures/wpt/wasm/jsapi/exception/getArg.tentative.any.js54
-rw-r--r--test/fixtures/wpt/wasm/jsapi/exception/is.tentative.any.js25
-rw-r--r--test/fixtures/wpt/wasm/jsapi/exception/toString.tentative.any.js21
-rw-r--r--test/fixtures/wpt/wasm/jsapi/function/call.tentative.any.js16
-rw-r--r--test/fixtures/wpt/wasm/jsapi/function/constructor.tentative.any.js65
-rw-r--r--test/fixtures/wpt/wasm/jsapi/function/table.tentative.any.js30
-rw-r--r--test/fixtures/wpt/wasm/jsapi/function/type.tentative.any.js28
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/entry-different-function-realm.html45
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/entry.html43
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/helper.js12
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/incumbent.html54
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/README.md5
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/current/current.html4
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/current/resources/window-to-open.html3
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/entry-incumbent.html15
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/function/function.html3
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/function/resources/window-to-open.html3
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/incumbent-incumbent.html24
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/relevant.html14
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html3
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/resources/window-to-open.html3
-rw-r--r--test/fixtures/wpt/wasm/jsapi/functions/resources/window-to-open.html3
-rw-r--r--test/fixtures/wpt/wasm/jsapi/global/constructor.any.js166
-rw-r--r--test/fixtures/wpt/wasm/jsapi/global/toString.any.js17
-rw-r--r--test/fixtures/wpt/wasm/jsapi/global/type.tentative.any.js65
-rw-r--r--test/fixtures/wpt/wasm/jsapi/global/value-get-set.any.js152
-rw-r--r--test/fixtures/wpt/wasm/jsapi/global/valueOf.any.js28
-rw-r--r--test/fixtures/wpt/wasm/jsapi/idlharness.any.js22
-rw-r--r--test/fixtures/wpt/wasm/jsapi/instance/constructor-bad-imports.any.js13
-rw-r--r--test/fixtures/wpt/wasm/jsapi/instance/constructor-caching.any.js54
-rw-r--r--test/fixtures/wpt/wasm/jsapi/instance/constructor.any.js54
-rw-r--r--test/fixtures/wpt/wasm/jsapi/instance/exports.any.js66
-rw-r--r--test/fixtures/wpt/wasm/jsapi/instance/toString.any.js19
-rw-r--r--test/fixtures/wpt/wasm/jsapi/instanceTestFactory.js761
-rw-r--r--test/fixtures/wpt/wasm/jsapi/interface.any.js160
-rw-r--r--test/fixtures/wpt/wasm/jsapi/memory/assertions.js38
-rw-r--r--test/fixtures/wpt/wasm/jsapi/memory/buffer.any.js64
-rw-r--r--test/fixtures/wpt/wasm/jsapi/memory/constructor-shared.tentative.any.js54
-rw-r--r--test/fixtures/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js20
-rw-r--r--test/fixtures/wpt/wasm/jsapi/memory/constructor.any.js139
-rw-r--r--test/fixtures/wpt/wasm/jsapi/memory/grow.any.js189
-rw-r--r--test/fixtures/wpt/wasm/jsapi/memory/toString.any.js17
-rw-r--r--test/fixtures/wpt/wasm/jsapi/memory/type.tentative.any.js37
-rw-r--r--test/fixtures/wpt/wasm/jsapi/module/constructor.any.js69
-rw-r--r--test/fixtures/wpt/wasm/jsapi/module/customSections.any.js140
-rw-r--r--test/fixtures/wpt/wasm/jsapi/module/exports.any.js185
-rw-r--r--test/fixtures/wpt/wasm/jsapi/module/imports.any.js185
-rw-r--r--test/fixtures/wpt/wasm/jsapi/module/toString.any.js18
-rw-r--r--test/fixtures/wpt/wasm/jsapi/proto-from-ctor-realm.html95
-rw-r--r--test/fixtures/wpt/wasm/jsapi/prototypes.any.js43
-rw-r--r--test/fixtures/wpt/wasm/jsapi/table/assertions.js24
-rw-r--r--test/fixtures/wpt/wasm/jsapi/table/constructor-types.tentative.any.js20
-rw-r--r--test/fixtures/wpt/wasm/jsapi/table/constructor.any.js208
-rw-r--r--test/fixtures/wpt/wasm/jsapi/table/get-set.any.js263
-rw-r--r--test/fixtures/wpt/wasm/jsapi/table/grow.any.js126
-rw-r--r--test/fixtures/wpt/wasm/jsapi/table/length.any.js60
-rw-r--r--test/fixtures/wpt/wasm/jsapi/table/toString.any.js17
-rw-r--r--test/fixtures/wpt/wasm/jsapi/table/type.tentative.any.js26
-rw-r--r--test/fixtures/wpt/wasm/jsapi/tag/constructor.tentative.any.js49
-rw-r--r--test/fixtures/wpt/wasm/jsapi/tag/toString.tentative.any.js20
-rw-r--r--test/fixtures/wpt/wasm/jsapi/tag/type.tentative.any.js21
-rw-r--r--test/fixtures/wpt/wasm/jsapi/wasm-module-builder.js1323
-rw-r--r--test/fixtures/wpt/wasm/webapi/META.yml1
-rw-r--r--test/fixtures/wpt/wasm/webapi/abort.any.js37
-rw-r--r--test/fixtures/wpt/wasm/webapi/body.any.js19
-rw-r--r--test/fixtures/wpt/wasm/webapi/contenttype.any.js64
-rw-r--r--test/fixtures/wpt/wasm/webapi/empty-body.any.js20
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/execute-start.tentative.html23
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/exported-names.tentative.html17
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/invalid-bytecode.tentative.html24
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html38
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html11
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/module-parse-error.tentative.html24
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.js1
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.tentative.html25
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasmbin0 -> 51 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasmbin0 -> 73 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasmbin0 -> 14 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasmbin0 -> 27 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js2
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasmbin0 -> 75 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js2
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasmbin0 -> 68 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js2
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasmbin0 -> 67 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js2
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasmbin0 -> 67 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js2
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasmbin0 -> 66 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.js13
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasmbin0 -> 101 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/log.js1
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasmbin0 -> 8 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasmbin0 -> 23 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasmbin0 -> 45 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasmbin0 -> 46 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasmbin0 -> 75 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.js1
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasmbin0 -> 45 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.js1
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasmbin0 -> 40 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.js1
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasmbin0 -> 41 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.js1
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasmbin0 -> 40 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.js15
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasmbin0 -> 294 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker-helper.js1
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.js1
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasmbin0 -> 62 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html14
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import.tentative.html34
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html32
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html26
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/worker-import.tentative.html13
-rw-r--r--test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html13
-rw-r--r--test/fixtures/wpt/wasm/webapi/historical.any.js29
-rw-r--r--test/fixtures/wpt/wasm/webapi/idlharness.any.js10
-rw-r--r--test/fixtures/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.js13
-rw-r--r--test/fixtures/wpt/wasm/webapi/instantiateStreaming.any.js49
-rw-r--r--test/fixtures/wpt/wasm/webapi/invalid-args.any.js28
-rw-r--r--test/fixtures/wpt/wasm/webapi/invalid-code.any.js21
-rw-r--r--test/fixtures/wpt/wasm/webapi/modified-contenttype.any.js24
-rw-r--r--test/fixtures/wpt/wasm/webapi/origin.sub.any.js15
-rw-r--r--test/fixtures/wpt/wasm/webapi/rejected-arg.any.js9
-rw-r--r--test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasmbin0 -> 46 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/resources/incrementer.wasmbin0 -> 46 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm.headers2
-rw-r--r--test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasmbin0 -> 46 bytes
-rw-r--r--test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers2
-rw-r--r--test/fixtures/wpt/wasm/webapi/status.any.js21
-rw-r--r--test/fixtures/wpt/wasm/webapi/wasm_stream_compile_test.html115
-rw-r--r--test/fixtures/wpt/wasm/webapi/wasm_stream_instantiate_test.html115
-rw-r--r--test/parallel/test-bootstrap-modules.js1
-rw-r--r--test/parallel/test-fetch-disabled.mjs3
-rw-r--r--test/parallel/test-wasm-web-api.js226
-rw-r--r--test/wpt/status/wasm/webapi.json24
-rw-r--r--test/wpt/test-wasm-webapi.js7
152 files changed, 7736 insertions, 0 deletions
diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md
index f5d946eff11..927a8a6f80f 100644
--- a/test/fixtures/wpt/README.md
+++ b/test/fixtures/wpt/README.md
@@ -26,6 +26,8 @@ Last update:
- streams: https://github.com/web-platform-tests/wpt/tree/8f60d94439/streams
- url: https://github.com/web-platform-tests/wpt/tree/77d54aa9e0/url
- user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing
+- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/1dd414c796/wasm/jsapi
+- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
- WebCryptoAPI: https://github.com/web-platform-tests/wpt/tree/cdd0f03df4/WebCryptoAPI
- webidl/ecmascript-binding/es-exceptions: https://github.com/web-platform-tests/wpt/tree/a370aad338/webidl/ecmascript-binding/es-exceptions
diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json
index 9f81c28c198..bde6cf862f6 100644
--- a/test/fixtures/wpt/versions.json
+++ b/test/fixtures/wpt/versions.json
@@ -63,6 +63,14 @@
"commit": "df24fb604e2d40528ac1d1b5dd970e32fc5c2978",
"path": "user-timing"
},
+ "wasm/jsapi": {
+ "commit": "1dd414c79616489ea021c800eb0375a709e8114e",
+ "path": "wasm/jsapi"
+ },
+ "wasm/webapi": {
+ "commit": "fd1b23eeaaf9a01555d4fa29cf79ed11a4c44a50",
+ "path": "wasm/webapi"
+ },
"WebCryptoAPI": {
"commit": "cdd0f03df41b222aed098fbbb11c6a3cc500a86b",
"path": "WebCryptoAPI"
diff --git a/test/fixtures/wpt/wasm/jsapi/META.yml b/test/fixtures/wpt/wasm/jsapi/META.yml
new file mode 100644
index 00000000000..cf5525ae115
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/META.yml
@@ -0,0 +1 @@
+spec: https://webassembly.github.io/spec/js-api/
diff --git a/test/fixtures/wpt/wasm/jsapi/assertions.js b/test/fixtures/wpt/wasm/jsapi/assertions.js
new file mode 100644
index 00000000000..162f5a9a6b8
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/assertions.js
@@ -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);
+}
diff --git a/test/fixtures/wpt/wasm/jsapi/bad-imports.js b/test/fixtures/wpt/wasm/jsapi/bad-imports.js
new file mode 100644
index 00000000000..786fc650e32
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/bad-imports.js
@@ -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,
+ },
+ });
+ }
+}
diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/compile.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/compile.any.js
new file mode 100644
index 00000000000..e94ce117173
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/constructor/compile.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/instantiate-bad-imports.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/instantiate-bad-imports.any.js
new file mode 100644
index 00000000000..30252bd6eeb
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/constructor/instantiate-bad-imports.any.js
@@ -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}`);
+});
diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/instantiate.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/instantiate.any.js
new file mode 100644
index 00000000000..8152f3a56f3
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/constructor/instantiate.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/multi-value.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/multi-value.any.js
new file mode 100644
index 00000000000..4b06d1da3c4
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/constructor/multi-value.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/toStringTag.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/toStringTag.any.js
new file mode 100644
index 00000000000..c6d2cdaf662
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/constructor/toStringTag.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/constructor/validate.any.js b/test/fixtures/wpt/wasm/jsapi/constructor/validate.any.js
new file mode 100644
index 00000000000..8b4f4582ab2
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/constructor/validate.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js b/test/fixtures/wpt/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js
new file mode 100644
index 00000000000..572db0c01b6
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/exception/basic.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/basic.tentative.any.js
new file mode 100644
index 00000000000..9ddebae0e96
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/exception/basic.tentative.any.js
@@ -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 except = builder.addException(kSig_v_r);
+ builder.addFunction("throw_param", kSig_v_r)
+ .addBody([
+ kExprLocalGet, 0,
+ kExprThrow, except,
+ ])
+ .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 except = builder.addException(kSig_v_a);
+ builder.addFunction("throw_null", kSig_v_v)
+ .addBody([
+ kExprRefNull, kWasmAnyFunc,
+ kExprThrow, except,
+ ])
+ .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 except = builder.addException(kSig_v_i);
+ builder.addFunction("throw_int", kSig_v_v)
+ .addBody([
+ ...wasmI32Const(7),
+ kExprThrow, except,
+ ])
+ .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 except = builder.addException(kSig_v_r);
+ builder.addFunction("catch_exception", kSig_r_v)
+ .addBody([
+ kExprTry, kWasmStmt,
+ kExprCallFunction, fnIndex,
+ kExprCatch, except,
+ 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");
diff --git a/test/fixtures/wpt/wasm/jsapi/exception/constructor.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/constructor.tentative.any.js
new file mode 100644
index 00000000000..0fd47b455e0
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/exception/constructor.tentative.any.js
@@ -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 argument = new WebAssembly.Tag({ parameters: [] });
+ assert_throws_js(TypeError, () => WebAssembly.Exception(argument));
+}, "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 exn = new WebAssembly.Tag({ parameters: [typeAndArg[0]] });
+ assert_throws_js(
+ TypeError,
+ () => new WebAssembly.Exception(exn, typeAndArg[1])
+ );
+ }
+}, "Invalid exception argument");
diff --git a/test/fixtures/wpt/wasm/jsapi/exception/getArg.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/getArg.tentative.any.js
new file mode 100644
index 00000000000..ecd2fbd42fd
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/exception/getArg.tentative.any.js
@@ -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(TypeError, () => 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(TypeError, () => 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");
diff --git a/test/fixtures/wpt/wasm/jsapi/exception/is.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/is.tentative.any.js
new file mode 100644
index 00000000000..e28a88a3c5f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/exception/is.tentative.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/exception/toString.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/exception/toString.tentative.any.js
new file mode 100644
index 00000000000..52635186c76
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/exception/toString.tentative.any.js
@@ -0,0 +1,21 @@
+// META: global=window,dedicatedworker,jsshell
+
+test(() => {
+ const argument = { parameters: [] };
+ const tag = new WebAssembly.Tag(argument);
+ const exception = new WebAssembly.Exception(tag, []);
+ assert_class_string(exception, "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");
diff --git a/test/fixtures/wpt/wasm/jsapi/function/call.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/function/call.tentative.any.js
new file mode 100644
index 00000000000..626cd13c9f0
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/function/call.tentative.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/function/constructor.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/function/constructor.tentative.any.js
new file mode 100644
index 00000000000..636aeca4dc1
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/function/constructor.tentative.any.js
@@ -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")
diff --git a/test/fixtures/wpt/wasm/jsapi/function/table.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/function/table.tentative.any.js
new file mode 100644
index 00000000000..d7d0d86e3b6
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/function/table.tentative.any.js
@@ -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")
diff --git a/test/fixtures/wpt/wasm/jsapi/function/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/function/type.tentative.any.js
new file mode 100644
index 00000000000..e01a23a9e43
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/function/type.tentative.any.js
@@ -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")
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/entry-different-function-realm.html b/test/fixtures/wpt/wasm/jsapi/functions/entry-different-function-realm.html
new file mode 100644
index 00000000000..3af3dd924fb
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/entry-different-function-realm.html
@@ -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>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/entry.html b/test/fixtures/wpt/wasm/jsapi/functions/entry.html
new file mode 100644
index 00000000000..15018074491
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/entry.html
@@ -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>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/helper.js b/test/fixtures/wpt/wasm/jsapi/functions/helper.js
new file mode 100644
index 00000000000..487791c69ad
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/helper.js
@@ -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,
+ }
+ });
+}
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/incumbent.html b/test/fixtures/wpt/wasm/jsapi/functions/incumbent.html
new file mode 100644
index 00000000000..cb276329770
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/incumbent.html
@@ -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>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/README.md b/test/fixtures/wpt/wasm/jsapi/functions/resources/README.md
new file mode 100644
index 00000000000..a89258a4e01
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/README.md
@@ -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.
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/current/current.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/current/current.html
new file mode 100644
index 00000000000..63d9c437fc5
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/current/current.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Current page used as a test helper</title>
+
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/current/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/current/resources/window-to-open.html
new file mode 100644
index 00000000000..1bc4cca9a39
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/current/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the current settings object is used this page will be opened</title>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/entry-incumbent.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/entry-incumbent.html
new file mode 100644
index 00000000000..6b210563e99
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/entry-incumbent.html
@@ -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>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/function/function.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/function/function.html
new file mode 100644
index 00000000000..979b902eaa0
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/function/function.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Realm for a host function used as a test helper</title>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/function/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/function/resources/window-to-open.html
new file mode 100644
index 00000000000..3928c1f8aa9
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/function/resources/window-to-open.html
@@ -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>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/incumbent-incumbent.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/incumbent-incumbent.html
new file mode 100644
index 00000000000..5e84f65a084
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/incumbent-incumbent.html
@@ -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>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/relevant.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/relevant.html
new file mode 100644
index 00000000000..06df91c2374
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/relevant.html
@@ -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>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html
new file mode 100644
index 00000000000..4138b5a0844
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the relevant settings object is used this page will be opened</title>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/resources/window-to-open.html
new file mode 100644
index 00000000000..7743b9b5782
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the incumbent settings object is used this page will be opened</title>
diff --git a/test/fixtures/wpt/wasm/jsapi/functions/resources/window-to-open.html b/test/fixtures/wpt/wasm/jsapi/functions/resources/window-to-open.html
new file mode 100644
index 00000000000..ce357937f5e
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/functions/resources/window-to-open.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>If the entry settings object is used this page will be opened</title>
diff --git a/test/fixtures/wpt/wasm/jsapi/global/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/global/constructor.any.js
new file mode 100644
index 00000000000..f536f5d7b5d
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/global/constructor.any.js
@@ -0,0 +1,166 @@
+// 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": "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");
diff --git a/test/fixtures/wpt/wasm/jsapi/global/toString.any.js b/test/fixtures/wpt/wasm/jsapi/global/toString.any.js
new file mode 100644
index 00000000000..359c4273b5b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/global/toString.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/global/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/global/type.tentative.any.js
new file mode 100644
index 00000000000..173af647f27
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/global/type.tentative.any.js
@@ -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": "anyfunc", "mutable": true})
+}, "anyfunc, mutable")
+
+test(() => {
+ assert_type({"value": "anyfunc", "mutable": false})
+}, "anyfunc, 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");
diff --git a/test/fixtures/wpt/wasm/jsapi/global/value-get-set.any.js b/test/fixtures/wpt/wasm/jsapi/global/value-get-set.any.js
new file mode 100644
index 00000000000..f95b7ca9e3f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/global/value-get-set.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/global/valueOf.any.js b/test/fixtures/wpt/wasm/jsapi/global/valueOf.any.js
new file mode 100644
index 00000000000..0695a5a61fb
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/global/valueOf.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/idlharness.any.js b/test/fixtures/wpt/wasm/jsapi/idlharness.any.js
new file mode 100644
index 00000000000..98713d4bf6e
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/idlharness.any.js
@@ -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],
+ });
+ }
+);
diff --git a/test/fixtures/wpt/wasm/jsapi/instance/constructor-bad-imports.any.js b/test/fixtures/wpt/wasm/jsapi/instance/constructor-bad-imports.any.js
new file mode 100644
index 00000000000..e4a5abb8eb2
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/instance/constructor-bad-imports.any.js
@@ -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}`);
+});
diff --git a/test/fixtures/wpt/wasm/jsapi/instance/constructor-caching.any.js b/test/fixtures/wpt/wasm/jsapi/instance/constructor-caching.any.js
new file mode 100644
index 00000000000..1aa4739b629
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/instance/constructor-caching.any.js
@@ -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);
+});
diff --git a/test/fixtures/wpt/wasm/jsapi/instance/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/instance/constructor.any.js
new file mode 100644
index 00000000000..26390ebd2cd
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/instance/constructor.any.js
@@ -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);
+}
diff --git a/test/fixtures/wpt/wasm/jsapi/instance/exports.any.js b/test/fixtures/wpt/wasm/jsapi/instance/exports.any.js
new file mode 100644
index 00000000000..6dcfbcee950
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/instance/exports.any.js
@@ -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)");
diff --git a/test/fixtures/wpt/wasm/jsapi/instance/toString.any.js b/test/fixtures/wpt/wasm/jsapi/instance/toString.any.js
new file mode 100644
index 00000000000..547a9ca8295
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/instance/toString.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/instanceTestFactory.js b/test/fixtures/wpt/wasm/jsapi/instanceTestFactory.js
new file mode 100644
index 00000000000..ac468947ec2
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/instanceTestFactory.js
@@ -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: () => {}
+ };
+ }
+ ],
+];
diff --git a/test/fixtures/wpt/wasm/jsapi/interface.any.js b/test/fixtures/wpt/wasm/jsapi/interface.any.js
new file mode 100644
index 00000000000..19d29ead0a7
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/interface.any.js
@@ -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],
+]);
diff --git a/test/fixtures/wpt/wasm/jsapi/memory/assertions.js b/test/fixtures/wpt/wasm/jsapi/memory/assertions.js
new file mode 100644
index 00000000000..b539513adca
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/memory/assertions.js
@@ -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 });
+}
diff --git a/test/fixtures/wpt/wasm/jsapi/memory/buffer.any.js b/test/fixtures/wpt/wasm/jsapi/memory/buffer.any.js
new file mode 100644
index 00000000000..fb1d1200b89
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/memory/buffer.any.js
@@ -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)");
diff --git a/test/fixtures/wpt/wasm/jsapi/memory/constructor-shared.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/memory/constructor-shared.tentative.any.js
new file mode 100644
index 00000000000..216fc4ca555
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/memory/constructor-shared.tentative.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js
new file mode 100644
index 00000000000..d5378dbe82b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/memory/constructor-types.tentative.any.js
@@ -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"); \ No newline at end of file
diff --git a/test/fixtures/wpt/wasm/jsapi/memory/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/memory/constructor.any.js
new file mode 100644
index 00000000000..0a0be11e370
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/memory/constructor.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/memory/grow.any.js b/test/fixtures/wpt/wasm/jsapi/memory/grow.any.js
new file mode 100644
index 00000000000..c511129491f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/memory/grow.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/memory/toString.any.js b/test/fixtures/wpt/wasm/jsapi/memory/toString.any.js
new file mode 100644
index 00000000000..f4059f76577
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/memory/toString.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/memory/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/memory/type.tentative.any.js
new file mode 100644
index 00000000000..a96a3227adc
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/memory/type.tentative.any.js
@@ -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"); \ No newline at end of file
diff --git a/test/fixtures/wpt/wasm/jsapi/module/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/module/constructor.any.js
new file mode 100644
index 00000000000..9978f7e6ac8
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/module/constructor.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/module/customSections.any.js b/test/fixtures/wpt/wasm/jsapi/module/customSections.any.js
new file mode 100644
index 00000000000..4029877e92c
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/module/customSections.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/module/exports.any.js b/test/fixtures/wpt/wasm/jsapi/module/exports.any.js
new file mode 100644
index 00000000000..40a3935a4a2
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/module/exports.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/module/imports.any.js b/test/fixtures/wpt/wasm/jsapi/module/imports.any.js
new file mode 100644
index 00000000000..ec550ce6c41
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/module/imports.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/module/toString.any.js b/test/fixtures/wpt/wasm/jsapi/module/toString.any.js
new file mode 100644
index 00000000000..1c20cd6108c
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/module/toString.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/proto-from-ctor-realm.html b/test/fixtures/wpt/wasm/jsapi/proto-from-ctor-realm.html
new file mode 100644
index 00000000000..45405b52900
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/proto-from-ctor-realm.html
@@ -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>
diff --git a/test/fixtures/wpt/wasm/jsapi/prototypes.any.js b/test/fixtures/wpt/wasm/jsapi/prototypes.any.js
new file mode 100644
index 00000000000..714f4f8430e
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/prototypes.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/table/assertions.js b/test/fixtures/wpt/wasm/jsapi/table/assertions.js
new file mode 100644
index 00000000000..19cc5c3b92d
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/table/assertions.js
@@ -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})`);
+ }
+}
diff --git a/test/fixtures/wpt/wasm/jsapi/table/constructor-types.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/table/constructor-types.tentative.any.js
new file mode 100644
index 00000000000..99ca41b55a9
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/table/constructor-types.tentative.any.js
@@ -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"); \ No newline at end of file
diff --git a/test/fixtures/wpt/wasm/jsapi/table/constructor.any.js b/test/fixtures/wpt/wasm/jsapi/table/constructor.any.js
new file mode 100644
index 00000000000..6d38d04e4f5
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/table/constructor.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/table/get-set.any.js b/test/fixtures/wpt/wasm/jsapi/table/get-set.any.js
new file mode 100644
index 00000000000..9301057a533
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/table/get-set.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/table/grow.any.js b/test/fixtures/wpt/wasm/jsapi/table/grow.any.js
new file mode 100644
index 00000000000..520d24bf4ba
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/table/grow.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/table/length.any.js b/test/fixtures/wpt/wasm/jsapi/table/length.any.js
new file mode 100644
index 00000000000..a9ef095ded4
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/table/length.any.js
@@ -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)");
diff --git a/test/fixtures/wpt/wasm/jsapi/table/toString.any.js b/test/fixtures/wpt/wasm/jsapi/table/toString.any.js
new file mode 100644
index 00000000000..8a09f2832c1
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/table/toString.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/table/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/table/type.tentative.any.js
new file mode 100644
index 00000000000..596e10b6bf5
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/table/type.tentative.any.js
@@ -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": "anyfunc"});
+}, "Zero initial, no maximum");
+
+test(() => {
+ assert_type({ "minimum": 5, "element": "anyfunc" });
+}, "Non-zero initial, no maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "maximum": 0, "element": "anyfunc" });
+}, "Zero maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "maximum": 5, "element": "anyfunc" });
+}, "Non-zero maximum");
diff --git a/test/fixtures/wpt/wasm/jsapi/tag/constructor.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/tag/constructor.tentative.any.js
new file mode 100644
index 00000000000..de63e7bf46d
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/tag/constructor.tentative.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/tag/toString.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/tag/toString.tentative.any.js
new file mode 100644
index 00000000000..ad9a4ba152f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/tag/toString.tentative.any.js
@@ -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");
diff --git a/test/fixtures/wpt/wasm/jsapi/tag/type.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/tag/type.tentative.any.js
new file mode 100644
index 00000000000..9d2f0de1a00
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/tag/type.tentative.any.js
@@ -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]");
diff --git a/test/fixtures/wpt/wasm/jsapi/wasm-module-builder.js b/test/fixtures/wpt/wasm/jsapi/wasm-module-builder.js
new file mode 100644
index 00000000000..7be72f86dae
--- /dev/null
+++ b/test/fixtures/wpt/wasm/jsapi/wasm-module-builder.js
@@ -0,0 +1,1323 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Used for encoding f32 and double constants to bits.
+let byte_view = new Uint8Array(8);
+let data_view = new DataView(byte_view.buffer);
+
+// The bytes function receives one of
+// - several arguments, each of which is either a number or a string of length
+// 1; if it's a string, the charcode of the contained character is used.
+// - a single array argument containing the actual arguments
+// - a single string; the returned buffer will contain the char codes of all
+// contained characters.
+function bytes(...input) {
+ if (input.length == 1 && typeof input[0] == 'array') input = input[0];
+ if (input.length == 1 && typeof input[0] == 'string') {
+ let len = input[0].length;
+ let view = new Uint8Array(len);
+ for (let i = 0; i < len; i++) view[i] = input[0].charCodeAt(i);
+ return view.buffer;
+ }
+ let view = new Uint8Array(input.length);
+ for (let i = 0; i < input.length; i++) {
+ let val = input[i];
+ if (typeof val == 'string') {
+ assertEquals(1, val.length, 'string inputs must have length 1');
+ val = val.charCodeAt(0);
+ }
+ view[i] = val | 0;
+ }
+ return view.buffer;
+}
+
+// Header declaration constants
+var kWasmH0 = 0;
+var kWasmH1 = 0x61;
+var kWasmH2 = 0x73;
+var kWasmH3 = 0x6d;
+
+var kWasmV0 = 0x1;
+var kWasmV1 = 0;
+var kWasmV2 = 0;
+var kWasmV3 = 0;
+
+var kHeaderSize = 8;
+var kPageSize = 65536;
+var kSpecMaxPages = 65535;
+var kMaxVarInt32Size = 5;
+var kMaxVarInt64Size = 10;
+
+let kDeclNoLocals = 0;
+
+// Section declaration constants
+let kUnknownSectionCode = 0;
+let kTypeSectionCode = 1; // Function signature declarations
+let kImportSectionCode = 2; // Import declarations
+let kFunctionSectionCode = 3; // Function declarations
+let kTableSectionCode = 4; // Indirect function table and other tables
+let kMemorySectionCode = 5; // Memory attributes
+let kGlobalSectionCode = 6; // Global declarations
+let kExportSectionCode = 7; // Exports
+let kStartSectionCode = 8; // Start function declaration
+let kElementSectionCode = 9; // Elements section
+let kCodeSectionCode = 10; // Function code
+let kDataSectionCode = 11; // Data segments
+let kDataCountSectionCode = 12; // Data segment count (between Element & Code)
+let kExceptionSectionCode = 13; // Exception section (between Global & Export)
+
+// Name section types
+let kModuleNameCode = 0;
+let kFunctionNamesCode = 1;
+let kLocalNamesCode = 2;
+
+let kWasmFunctionTypeForm = 0x60;
+let kWasmAnyFunctionTypeForm = 0x70;
+
+let kHasMaximumFlag = 1;
+let kSharedHasMaximumFlag = 3;
+
+// Segment flags
+let kActiveNoIndex = 0;
+let kPassive = 1;
+let kActiveWithIndex = 2;
+let kPassiveWithElements = 5;
+
+// Function declaration flags
+let kDeclFunctionName = 0x01;
+let kDeclFunctionImport = 0x02;
+let kDeclFunctionLocals = 0x04;
+let kDeclFunctionExport = 0x08;
+
+// Local types
+let kWasmStmt = 0x40;
+let kWasmI32 = 0x7f;
+let kWasmI64 = 0x7e;
+let kWasmF32 = 0x7d;
+let kWasmF64 = 0x7c;
+let kWasmS128 = 0x7b;
+let kWasmAnyRef = 0x6f;
+let kWasmAnyFunc = 0x70;
+
+let kExternalFunction = 0;
+let kExternalTable = 1;
+let kExternalMemory = 2;
+let kExternalGlobal = 3;
+let kExternalException = 4;
+
+let kTableZero = 0;
+let kMemoryZero = 0;
+let kSegmentZero = 0;
+
+let kExceptionAttribute = 0;
+
+// Useful signatures
+let kSig_i_i = makeSig([kWasmI32], [kWasmI32]);
+let kSig_l_l = makeSig([kWasmI64], [kWasmI64]);
+let kSig_i_l = makeSig([kWasmI64], [kWasmI32]);
+let kSig_i_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32]);
+let kSig_i_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], [kWasmI32]);
+let kSig_v_iiii = makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmI32], []);
+let kSig_f_ff = makeSig([kWasmF32, kWasmF32], [kWasmF32]);
+let kSig_d_dd = makeSig([kWasmF64, kWasmF64], [kWasmF64]);
+let kSig_l_ll = makeSig([kWasmI64, kWasmI64], [kWasmI64]);
+let kSig_i_dd = makeSig([kWasmF64, kWasmF64], [kWasmI32]);
+let kSig_v_v = makeSig([], []);
+let kSig_i_v = makeSig([], [kWasmI32]);
+let kSig_l_v = makeSig([], [kWasmI64]);
+let kSig_f_v = makeSig([], [kWasmF32]);
+let kSig_d_v = makeSig([], [kWasmF64]);
+let kSig_v_i = makeSig([kWasmI32], []);
+let kSig_v_ii = makeSig([kWasmI32, kWasmI32], []);
+let kSig_v_iii = makeSig([kWasmI32, kWasmI32, kWasmI32], []);
+let kSig_v_l = makeSig([kWasmI64], []);
+let kSig_v_d = makeSig([kWasmF64], []);
+let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
+let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
+let kSig_ii_v = makeSig([], [kWasmI32, kWasmI32]);
+let kSig_iii_v = makeSig([], [kWasmI32, kWasmI32, kWasmI32]);
+let kSig_ii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32]);
+let kSig_iii_i = makeSig([kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
+let kSig_ii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32]);
+let kSig_iii_ii = makeSig([kWasmI32, kWasmI32], [kWasmI32, kWasmI32, kWasmI32]);
+
+let kSig_v_f = makeSig([kWasmF32], []);
+let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
+let kSig_f_d = makeSig([kWasmF64], [kWasmF32]);
+let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
+let kSig_r_r = makeSig([kWasmAnyRef], [kWasmAnyRef]);
+let kSig_a_a = makeSig([kWasmAnyFunc], [kWasmAnyFunc]);
+let kSig_i_r = makeSig([kWasmAnyRef], [kWasmI32]);
+let kSig_v_r = makeSig([kWasmAnyRef], []);
+let kSig_v_a = makeSig([kWasmAnyFunc], []);
+let kSig_v_rr = makeSig([kWasmAnyRef, kWasmAnyRef], []);
+let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []);
+let kSig_r_v = makeSig([], [kWasmAnyRef]);
+let kSig_a_v = makeSig([], [kWasmAnyFunc]);
+let kSig_a_i = makeSig([kWasmI32], [kWasmAnyFunc]);
+
+function makeSig(params, results) {
+ return {params: params, results: results};
+}
+
+function makeSig_v_x(x) {
+ return makeSig([x], []);
+}
+
+function makeSig_v_xx(x) {
+ return makeSig([x, x], []);
+}
+
+function makeSig_r_v(r) {
+ return makeSig([], [r]);
+}
+
+function makeSig_r_x(r, x) {
+ return makeSig([x], [r]);
+}
+
+function makeSig_r_xx(r, x) {
+ return makeSig([x, x], [r]);
+}
+
+// Opcodes
+let kExprUnreachable = 0x00;
+let kExprNop = 0x01;
+let kExprBlock = 0x02;
+let kExprLoop = 0x03;
+let kExprIf = 0x04;
+let kExprElse = 0x05;
+let kExprTry = 0x06;
+let kExprCatch = 0x07;
+let kExprCatchAll = 0x19;
+let kExprThrow = 0x08;
+let kExprRethrow = 0x09;
+let kExprBrOnExn = 0x0a;
+let kExprEnd = 0x0b;
+let kExprBr = 0x0c;
+let kExprBrIf = 0x0d;
+let kExprBrTable = 0x0e;
+let kExprReturn = 0x0f;
+let kExprCallFunction = 0x10;
+let kExprCallIndirect = 0x11;
+let kExprReturnCall = 0x12;
+let kExprReturnCallIndirect = 0x13;
+let kExprDrop = 0x1a;
+let kExprSelect = 0x1b;
+let kExprLocalGet = 0x20;
+let kExprLocalSet = 0x21;
+let kExprLocalTee = 0x22;
+let kExprGlobalGet = 0x23;
+let kExprGlobalSet = 0x24;
+let kExprTableGet = 0x25;
+let kExprTableSet = 0x26;
+let kExprI32LoadMem = 0x28;
+let kExprI64LoadMem = 0x29;
+let kExprF32LoadMem = 0x2a;
+let kExprF64LoadMem = 0x2b;
+let kExprI32LoadMem8S = 0x2c;
+let kExprI32LoadMem8U = 0x2d;
+let kExprI32LoadMem16S = 0x2e;
+let kExprI32LoadMem16U = 0x2f;
+let kExprI64LoadMem8S = 0x30;
+let kExprI64LoadMem8U = 0x31;
+let kExprI64LoadMem16S = 0x32;
+let kExprI64LoadMem16U = 0x33;
+let kExprI64LoadMem32S = 0x34;
+let kExprI64LoadMem32U = 0x35;
+let kExprI32StoreMem = 0x36;
+let kExprI64StoreMem = 0x37;
+let kExprF32StoreMem = 0x38;
+let kExprF64StoreMem = 0x39;
+let kExprI32StoreMem8 = 0x3a;
+let kExprI32StoreMem16 = 0x3b;
+let kExprI64StoreMem8 = 0x3c;
+let kExprI64StoreMem16 = 0x3d;
+let kExprI64StoreMem32 = 0x3e;
+let kExprMemorySize = 0x3f;
+let kExprMemoryGrow = 0x40;
+let kExprI32Const = 0x41;
+let kExprI64Const = 0x42;
+let kExprF32Const = 0x43;
+let kExprF64Const = 0x44;
+let kExprI32Eqz = 0x45;
+let kExprI32Eq = 0x46;
+let kExprI32Ne = 0x47;
+let kExprI32LtS = 0x48;
+let kExprI32LtU = 0x49;
+let kExprI32GtS = 0x4a;
+let kExprI32GtU = 0x4b;
+let kExprI32LeS = 0x4c;
+let kExprI32LeU = 0x4d;
+let kExprI32GeS = 0x4e;
+let kExprI32GeU = 0x4f;
+let kExprI64Eqz = 0x50;
+let kExprI64Eq = 0x51;
+let kExprI64Ne = 0x52;
+let kExprI64LtS = 0x53;
+let kExprI64LtU = 0x54;
+let kExprI64GtS = 0x55;
+let kExprI64GtU = 0x56;
+let kExprI64LeS = 0x57;
+let kExprI64LeU = 0x58;
+let kExprI64GeS = 0x59;
+let kExprI64GeU = 0x5a;
+let kExprF32Eq = 0x5b;
+let kExprF32Ne = 0x5c;
+let kExprF32Lt = 0x5d;
+let kExprF32Gt = 0x5e;
+let kExprF32Le = 0x5f;
+let kExprF32Ge = 0x60;
+let kExprF64Eq = 0x61;
+let kExprF64Ne = 0x62;
+let kExprF64Lt = 0x63;
+let kExprF64Gt = 0x64;
+let kExprF64Le = 0x65;
+let kExprF64Ge = 0x66;
+let kExprI32Clz = 0x67;
+let kExprI32Ctz = 0x68;
+let kExprI32Popcnt = 0x69;
+let kExprI32Add = 0x6a;
+let kExprI32Sub = 0x6b;
+let kExprI32Mul = 0x6c;
+let kExprI32DivS = 0x6d;
+let kExprI32DivU = 0x6e;
+let kExprI32RemS = 0x6f;
+let kExprI32RemU = 0x70;
+let kExprI32And = 0x71;
+let kExprI32Ior = 0x72;
+let kExprI32Xor = 0x73;
+let kExprI32Shl = 0x74;
+let kExprI32ShrS = 0x75;
+let kExprI32ShrU = 0x76;
+let kExprI32Rol = 0x77;
+let kExprI32Ror = 0x78;
+let kExprI64Clz = 0x79;
+let kExprI64Ctz = 0x7a;
+let kExprI64Popcnt = 0x7b;
+let kExprI64Add = 0x7c;
+let kExprI64Sub = 0x7d;
+let kExprI64Mul = 0x7e;
+let kExprI64DivS = 0x7f;
+let kExprI64DivU = 0x80;
+let kExprI64RemS = 0x81;
+let kExprI64RemU = 0x82;
+let kExprI64And = 0x83;
+let kExprI64Ior = 0x84;
+let kExprI64Xor = 0x85;
+let kExprI64Shl = 0x86;
+let kExprI64ShrS = 0x87;
+let kExprI64ShrU = 0x88;
+let kExprI64Rol = 0x89;
+let kExprI64Ror = 0x8a;
+let kExprF32Abs = 0x8b;
+let kExprF32Neg = 0x8c;
+let kExprF32Ceil = 0x8d;
+let kExprF32Floor = 0x8e;
+let kExprF32Trunc = 0x8f;
+let kExprF32NearestInt = 0x90;
+let kExprF32Sqrt = 0x91;
+let kExprF32Add = 0x92;
+let kExprF32Sub = 0x93;
+let kExprF32Mul = 0x94;
+let kExprF32Div = 0x95;
+let kExprF32Min = 0x96;
+let kExprF32Max = 0x97;
+let kExprF32CopySign = 0x98;
+let kExprF64Abs = 0x99;
+let kExprF64Neg = 0x9a;
+let kExprF64Ceil = 0x9b;
+let kExprF64Floor = 0x9c;
+let kExprF64Trunc = 0x9d;
+let kExprF64NearestInt = 0x9e;
+let kExprF64Sqrt = 0x9f;
+let kExprF64Add = 0xa0;
+let kExprF64Sub = 0xa1;
+let kExprF64Mul = 0xa2;
+let kExprF64Div = 0xa3;
+let kExprF64Min = 0xa4;
+let kExprF64Max = 0xa5;
+let kExprF64CopySign = 0xa6;
+let kExprI32ConvertI64 = 0xa7;
+let kExprI32SConvertF32 = 0xa8;
+let kExprI32UConvertF32 = 0xa9;
+let kExprI32SConvertF64 = 0xaa;
+let kExprI32UConvertF64 = 0xab;
+let kExprI64SConvertI32 = 0xac;
+let kExprI64UConvertI32 = 0xad;
+let kExprI64SConvertF32 = 0xae;
+let kExprI64UConvertF32 = 0xaf;
+let kExprI64SConvertF64 = 0xb0;
+let kExprI64UConvertF64 = 0xb1;
+let kExprF32SConvertI32 = 0xb2;
+let kExprF32UConvertI32 = 0xb3;
+let kExprF32SConvertI64 = 0xb4;
+let kExprF32UConvertI64 = 0xb5;
+let kExprF32ConvertF64 = 0xb6;
+let kExprF64SConvertI32 = 0xb7;
+let kExprF64UConvertI32 = 0xb8;
+let kExprF64SConvertI64 = 0xb9;
+let kExprF64UConvertI64 = 0xba;
+let kExprF64ConvertF32 = 0xbb;
+let kExprI32ReinterpretF32 = 0xbc;
+let kExprI64ReinterpretF64 = 0xbd;
+let kExprF32ReinterpretI32 = 0xbe;
+let kExprF64ReinterpretI64 = 0xbf;
+let kExprI32SExtendI8 = 0xc0;
+let kExprI32SExtendI16 = 0xc1;
+let kExprI64SExtendI8 = 0xc2;
+let kExprI64SExtendI16 = 0xc3;
+let kExprI64SExtendI32 = 0xc4;
+let kExprRefNull = 0xd0;
+let kExprRefIsNull = 0xd1;
+let kExprRefFunc = 0xd2;
+
+// Prefix opcodes
+let kNumericPrefix = 0xfc;
+let kSimdPrefix = 0xfd;
+let kAtomicPrefix = 0xfe;
+
+// Numeric opcodes.
+let kExprMemoryInit = 0x08;
+let kExprDataDrop = 0x09;
+let kExprMemoryCopy = 0x0a;
+let kExprMemoryFill = 0x0b;
+let kExprTableInit = 0x0c;
+let kExprElemDrop = 0x0d;
+let kExprTableCopy = 0x0e;
+let kExprTableGrow = 0x0f;
+let kExprTableSize = 0x10;
+let kExprTableFill = 0x11;
+
+// Atomic opcodes.
+let kExprAtomicNotify = 0x00;
+let kExprI32AtomicWait = 0x01;
+let kExprI64AtomicWait = 0x02;
+let kExprI32AtomicLoad = 0x10;
+let kExprI32AtomicLoad8U = 0x12;
+let kExprI32AtomicLoad16U = 0x13;
+let kExprI32AtomicStore = 0x17;
+let kExprI32AtomicStore8U = 0x19;
+let kExprI32AtomicStore16U = 0x1a;
+let kExprI32AtomicAdd = 0x1e;
+let kExprI32AtomicAdd8U = 0x20;
+let kExprI32AtomicAdd16U = 0x21;
+let kExprI32AtomicSub = 0x25;
+let kExprI32AtomicSub8U = 0x27;
+let kExprI32AtomicSub16U = 0x28;
+let kExprI32AtomicAnd = 0x2c;
+let kExprI32AtomicAnd8U = 0x2e;
+let kExprI32AtomicAnd16U = 0x2f;
+let kExprI32AtomicOr = 0x33;
+let kExprI32AtomicOr8U = 0x35;
+let kExprI32AtomicOr16U = 0x36;
+let kExprI32AtomicXor = 0x3a;
+let kExprI32AtomicXor8U = 0x3c;
+let kExprI32AtomicXor16U = 0x3d;
+let kExprI32AtomicExchange = 0x41;
+let kExprI32AtomicExchange8U = 0x43;
+let kExprI32AtomicExchange16U = 0x44;
+let kExprI32AtomicCompareExchange = 0x48;
+let kExprI32AtomicCompareExchange8U = 0x4a;
+let kExprI32AtomicCompareExchange16U = 0x4b;
+
+let kExprI64AtomicLoad = 0x11;
+let kExprI64AtomicLoad8U = 0x14;
+let kExprI64AtomicLoad16U = 0x15;
+let kExprI64AtomicLoad32U = 0x16;
+let kExprI64AtomicStore = 0x18;
+let kExprI64AtomicStore8U = 0x1b;
+let kExprI64AtomicStore16U = 0x1c;
+let kExprI64AtomicStore32U = 0x1d;
+let kExprI64AtomicAdd = 0x1f;
+let kExprI64AtomicAdd8U = 0x22;
+let kExprI64AtomicAdd16U = 0x23;
+let kExprI64AtomicAdd32U = 0x24;
+let kExprI64AtomicSub = 0x26;
+let kExprI64AtomicSub8U = 0x29;
+let kExprI64AtomicSub16U = 0x2a;
+let kExprI64AtomicSub32U = 0x2b;
+let kExprI64AtomicAnd = 0x2d;
+let kExprI64AtomicAnd8U = 0x30;
+let kExprI64AtomicAnd16U = 0x31;
+let kExprI64AtomicAnd32U = 0x32;
+let kExprI64AtomicOr = 0x34;
+let kExprI64AtomicOr8U = 0x37;
+let kExprI64AtomicOr16U = 0x38;
+let kExprI64AtomicOr32U = 0x39;
+let kExprI64AtomicXor = 0x3b;
+let kExprI64AtomicXor8U = 0x3e;
+let kExprI64AtomicXor16U = 0x3f;
+let kExprI64AtomicXor32U = 0x40;
+let kExprI64AtomicExchange = 0x42;
+let kExprI64AtomicExchange8U = 0x45;
+let kExprI64AtomicExchange16U = 0x46;
+let kExprI64AtomicExchange32U = 0x47;
+let kExprI64AtomicCompareExchange = 0x49
+let kExprI64AtomicCompareExchange8U = 0x4c;
+let kExprI64AtomicCompareExchange16U = 0x4d;
+let kExprI64AtomicCompareExchange32U = 0x4e;
+
+// Simd opcodes.
+let kExprS128LoadMem = 0x00;
+let kExprS128StoreMem = 0x01;
+let kExprI32x4Splat = 0x0c;
+let kExprI32x4Eq = 0x2c;
+let kExprS1x4AllTrue = 0x75;
+let kExprF32x4Min = 0x9e;
+
+class Binary {
+ constructor() {
+ this.length = 0;
+ this.buffer = new Uint8Array(8192);
+ }
+
+ ensure_space(needed) {
+ if (this.buffer.length - this.length >= needed) return;
+ let new_capacity = this.buffer.length * 2;
+ while (new_capacity - this.length < needed) new_capacity *= 2;
+ let new_buffer = new Uint8Array(new_capacity);
+ new_buffer.set(this.buffer);
+ this.buffer = new_buffer;
+ }
+
+ trunc_buffer() {
+ return new Uint8Array(this.buffer.buffer, 0, this.length);
+ }
+
+ reset() {
+ this.length = 0;
+ }
+
+ emit_u8(val) {
+ this.ensure_space(1);
+ this.buffer[this.length++] = val;
+ }
+
+ emit_u16(val) {
+ this.ensure_space(2);
+ this.buffer[this.length++] = val;
+ this.buffer[this.length++] = val >> 8;
+ }
+
+ emit_u32(val) {
+ this.ensure_space(4);
+ this.buffer[this.length++] = val;
+ this.buffer[this.length++] = val >> 8;
+ this.buffer[this.length++] = val >> 16;
+ this.buffer[this.length++] = val >> 24;
+ }
+
+ emit_leb_u(val, max_len) {
+ this.ensure_space(max_len);
+ for (let i = 0; i < max_len; ++i) {
+ let v = val & 0xff;
+ val = val >>> 7;
+ if (val == 0) {
+ this.buffer[this.length++] = v;
+ return;
+ }
+ this.buffer[this.length++] = v | 0x80;
+ }
+ throw new Error("Leb value exceeds maximum length of " + max_len);
+ }
+
+ emit_u32v(val) {
+ this.emit_leb_u(val, kMaxVarInt32Size);
+ }
+
+ emit_u64v(val) {
+ this.emit_leb_u(val, kMaxVarInt64Size);
+ }
+
+ emit_bytes(data) {
+ this.ensure_space(data.length);
+ this.buffer.set(data, this.length);
+ this.length += data.length;
+ }
+
+ emit_string(string) {
+ // When testing illegal names, we pass a byte array directly.
+ if (string instanceof Array) {
+ this.emit_u32v(string.length);
+ this.emit_bytes(string);
+ return;
+ }
+
+ // This is the hacky way to convert a JavaScript string to a UTF8 encoded
+ // string only containing single-byte characters.
+ let string_utf8 = unescape(encodeURIComponent(string));
+ this.emit_u32v(string_utf8.length);
+ for (let i = 0; i < string_utf8.length; i++) {
+ this.emit_u8(string_utf8.charCodeAt(i));
+ }
+ }
+
+ emit_header() {
+ this.emit_bytes([
+ kWasmH0, kWasmH1, kWasmH2, kWasmH3, kWasmV0, kWasmV1, kWasmV2, kWasmV3
+ ]);
+ }
+
+ emit_section(section_code, content_generator) {
+ // Emit section name.
+ this.emit_u8(section_code);
+ // Emit the section to a temporary buffer: its full length isn't know yet.
+ const section = new Binary;
+ content_generator(section);
+ // Emit section length.
+ this.emit_u32v(section.length);
+ // Copy the temporary buffer.
+ // Avoid spread because {section} can be huge.
+ this.emit_bytes(section.trunc_buffer());
+ }
+}
+
+class WasmFunctionBuilder {
+ constructor(module, name, type_index) {
+ this.module = module;
+ this.name = name;
+ this.type_index = type_index;
+ this.body = [];
+ this.locals = [];
+ this.local_names = [];
+ }
+
+ numLocalNames() {
+ let num_local_names = 0;
+ for (let loc_name of this.local_names) {
+ if (loc_name !== undefined) ++num_local_names;
+ }
+ return num_local_names;
+ }
+
+ exportAs(name) {
+ this.module.addExport(name, this.index);
+ return this;
+ }
+
+ exportFunc() {
+ this.exportAs(this.name);
+ return this;
+ }
+
+ addBody(body) {
+ for (let b of body) {
+ if (typeof b !== 'number' || (b & (~0xFF)) !== 0 )
+ throw new Error('invalid body (entries must be 8 bit numbers): ' + body);
+ }
+ this.body = body.slice();
+ // Automatically add the end for the function block to the body.
+ this.body.push(kExprEnd);
+ return this;
+ }
+
+ addBodyWithEnd(body) {
+ this.body = body;
+ return this;
+ }
+
+ getNumLocals() {
+ let total_locals = 0;
+ for (let l of this.locals) {
+ for (let type of ["i32", "i64", "f32", "f64", "s128"]) {
+ total_locals += l[type + "_count"] || 0;
+ }
+ }
+ return total_locals;
+ }
+
+ addLocals(locals, names) {
+ const old_num_locals = this.getNumLocals();
+ this.locals.push(locals);
+ if (names) {
+ const missing_names = old_num_locals - this.local_names.length;
+ this.local_names.push(...new Array(missing_names), ...names);
+ }
+ return this;
+ }
+
+ end() {
+ return this.module;
+ }
+}
+
+class WasmGlobalBuilder {
+ constructor(module, type, mutable) {
+ this.module = module;
+ this.type = type;
+ this.mutable = mutable;
+ this.init = 0;
+ }
+
+ exportAs(name) {
+ this.module.exports.push({name: name, kind: kExternalGlobal,
+ index: this.index});
+ return this;
+ }
+}
+
+class WasmTableBuilder {
+ constructor(module, type, initial_size, max_size) {
+ this.module = module;
+ this.type = type;
+ this.initial_size = initial_size;
+ this.has_max = max_size != undefined;
+ this.max_size = max_size;
+ }
+
+ exportAs(name) {
+ this.module.exports.push({name: name, kind: kExternalTable,
+ index: this.index});
+ return this;
+ }
+}
+
+class WasmModuleBuilder {
+ constructor() {
+ this.types = [];
+ this.imports = [];
+ this.exports = [];
+ this.globals = [];
+ this.tables = [];
+ this.exceptions = [];
+ this.functions = [];
+ this.element_segments = [];
+ this.data_segments = [];
+ this.explicit = [];
+ this.num_imported_funcs = 0;
+ this.num_imported_globals = 0;
+ this.num_imported_tables = 0;
+ this.num_imported_exceptions = 0;
+ return this;
+ }
+
+ addStart(start_index) {
+ this.start_index = start_index;
+ return this;
+ }
+
+ addMemory(min, max, exp, shared) {
+ this.memory = {min: min, max: max, exp: exp, shared: shared};
+ return this;
+ }
+
+ addExplicitSection(bytes) {
+ this.explicit.push(bytes);
+ return this;
+ }
+
+ stringToBytes(name) {
+ var result = new Binary();
+ result.emit_string(name);
+ return result.trunc_buffer()
+ }
+
+ createCustomSection(name, bytes) {
+ name = this.stringToBytes(name);
+ var section = new Binary();
+ section.emit_u8(kUnknownSectionCode);
+ section.emit_u32v(name.length + bytes.length);
+ section.emit_bytes(name);
+ section.emit_bytes(bytes);
+ return section.trunc_buffer();
+ }
+
+ addCustomSection(name, bytes) {
+ this.explicit.push(this.createCustomSection(name, bytes));
+ }
+
+ addType(type) {
+ this.types.push(type);
+ var pl = type.params.length; // should have params
+ var rl = type.results.length; // should have results
+ return this.types.length - 1;
+ }
+
+ addGlobal(local_type, mutable) {
+ let glob = new WasmGlobalBuilder(this, local_type, mutable);
+ glob.index = this.globals.length + this.num_imported_globals;
+ this.globals.push(glob);
+ return glob;
+ }
+
+ addTable(type, initial_size, max_size = undefined) {
+ if (type != kWasmAnyRef && type != kWasmAnyFunc) {
+ throw new Error('Tables must be of type kWasmAnyRef or kWasmAnyFunc');
+ }
+ let table = new WasmTableBuilder(this, type, initial_size, max_size);
+ table.index = this.tables.length + this.num_imported_tables;
+ this.tables.push(table);
+ return table;
+ }
+
+ addException(type) {
+ let type_index = (typeof type) == "number" ? type : this.addType(type);
+ let except_index = this.exceptions.length + this.num_imported_exceptions;
+ this.exceptions.push(type_index);
+ return except_index;
+ }
+
+ addFunction(name, type) {
+ let type_index = (typeof type) == "number" ? type : this.addType(type);
+ let func = new WasmFunctionBuilder(this, name, type_index);
+ func.index = this.functions.length + this.num_imported_funcs;
+ this.functions.push(func);
+ return func;
+ }
+
+ addImport(module, name, type) {
+ if (this.functions.length != 0) {
+ throw new Error('Imported functions must be declared before local ones');
+ }
+ let type_index = (typeof type) == "number" ? type : this.addType(type);
+ this.imports.push({module: module, name: name, kind: kExternalFunction,
+ type: type_index});
+ return this.num_imported_funcs++;
+ }
+
+ addImportedGlobal(module, name, type, mutable = false) {
+ if (this.globals.length != 0) {
+ throw new Error('Imported globals must be declared before local ones');
+ }
+ let o = {module: module, name: name, kind: kExternalGlobal, type: type,
+ mutable: mutable};
+ this.imports.push(o);
+ return this.num_imported_globals++;
+ }
+
+ addImportedMemory(module, name, initial = 0, maximum, shared) {
+ let o = {module: module, name: name, kind: kExternalMemory,
+ initial: initial, maximum: maximum, shared: shared};
+ this.imports.push(o);
+ return this;
+ }
+
+ addImportedTable(module, name, initial, maximum, type) {
+ if (this.tables.length != 0) {
+ throw new Error('Imported tables must be declared before local ones');
+ }
+ let o = {module: module, name: name, kind: kExternalTable, initial: initial,
+ maximum: maximum, type: type || kWasmAnyFunctionTypeForm};
+ this.imports.push(o);
+ return this.num_imported_tables++;
+ }
+
+ addImportedException(module, name, type) {
+ if (this.exceptions.length != 0) {
+ throw new Error('Imported exceptions must be declared before local ones');
+ }
+ let type_index = (typeof type) == "number" ? type : this.addType(type);
+ let o = {module: module, name: name, kind: kExternalException, type: type_index};
+ this.imports.push(o);
+ return this.num_imported_exceptions++;
+ }
+
+ addExport(name, index) {
+ this.exports.push({name: name, kind: kExternalFunction, index: index});
+ return this;
+ }
+
+ addExportOfKind(name, kind, index) {
+ this.exports.push({name: name, kind: kind, index: index});
+ return this;
+ }
+
+ addDataSegment(addr, data, is_global = false) {
+ this.data_segments.push(
+ {addr: addr, data: data, is_global: is_global, is_active: true});
+ return this.data_segments.length - 1;
+ }
+
+ addPassiveDataSegment(data) {
+ this.data_segments.push({data: data, is_active: false});
+ return this.data_segments.length - 1;
+ }
+
+ exportMemoryAs(name) {
+ this.exports.push({name: name, kind: kExternalMemory, index: 0});
+ }
+
+ addElementSegment(table, base, is_global, array) {
+ this.element_segments.push({table: table, base: base, is_global: is_global,
+ array: array, is_active: true});
+ return this;
+ }
+
+ addPassiveElementSegment(array, is_import = false) {
+ this.element_segments.push({array: array, is_active: false});
+ return this;
+ }
+
+ appendToTable(array) {
+ for (let n of array) {
+ if (typeof n != 'number')
+ throw new Error('invalid table (entries have to be numbers): ' + array);
+ }
+ if (this.tables.length == 0) {
+ this.addTable(kWasmAnyFunc, 0);
+ }
+ // Adjust the table to the correct size.
+ let table = this.tables[0];
+ const base = table.initial_size;
+ const table_size = base + array.length;
+ table.initial_size = table_size;
+ if (table.has_max && table_size > table.max_size) {
+ table.max_size = table_size;
+ }
+ return this.addElementSegment(0, base, false, array);
+ }
+
+ setTableBounds(min, max = undefined) {
+ if (this.tables.length != 0) {
+ throw new Error("The table bounds of table '0' have already been set.");
+ }
+ this.addTable(kWasmAnyFunc, min, max);
+ return this;
+ }
+
+ setName(name) {
+ this.name = name;
+ return this;
+ }
+
+ toBuffer(debug = false) {
+ let binary = new Binary;
+ let wasm = this;
+
+ // Add header
+ binary.emit_header();
+
+ // Add type section
+ if (wasm.types.length > 0) {
+ if (debug) print("emitting types @ " + binary.length);
+ binary.emit_section(kTypeSectionCode, section => {
+ section.emit_u32v(wasm.types.length);
+ for (let type of wasm.types) {
+ section.emit_u8(kWasmFunctionTypeForm);
+ section.emit_u32v(type.params.length);
+ for (let param of type.params) {
+ section.emit_u8(param);
+ }
+ section.emit_u32v(type.results.length);
+ for (let result of type.results) {
+ section.emit_u8(result);
+ }
+ }
+ });
+ }
+
+ // Add imports section
+ if (wasm.imports.length > 0) {
+ if (debug) print("emitting imports @ " + binary.length);
+ binary.emit_section(kImportSectionCode, section => {
+ section.emit_u32v(wasm.imports.length);
+ for (let imp of wasm.imports) {
+ section.emit_string(imp.module);
+ section.emit_string(imp.name || '');
+ section.emit_u8(imp.kind);
+ if (imp.kind == kExternalFunction) {
+ section.emit_u32v(imp.type);
+ } else if (imp.kind == kExternalGlobal) {
+ section.emit_u32v(imp.type);
+ section.emit_u8(imp.mutable);
+ } else if (imp.kind == kExternalMemory) {
+ var has_max = (typeof imp.maximum) != "undefined";
+ var is_shared = (typeof imp.shared) != "undefined";
+ if (is_shared) {
+ section.emit_u8(has_max ? 3 : 2); // flags
+ } else {
+ section.emit_u8(has_max ? 1 : 0); // flags
+ }
+ section.emit_u32v(imp.initial); // initial
+ if (has_max) section.emit_u32v(imp.maximum); // maximum
+ } else if (imp.kind == kExternalTable) {
+ section.emit_u8(imp.type);
+ var has_max = (typeof imp.maximum) != "undefined";
+ section.emit_u8(has_max ? 1 : 0); // flags
+ section.emit_u32v(imp.initial); // initial
+ if (has_max) section.emit_u32v(imp.maximum); // maximum
+ } else if (imp.kind == kExternalException) {
+ section.emit_u32v(kExceptionAttribute);
+ section.emit_u32v(imp.type);
+ } else {
+ throw new Error("unknown/unsupported import kind " + imp.kind);
+ }
+ }
+ });
+ }
+
+ // Add functions declarations
+ if (wasm.functions.length > 0) {
+ if (debug) print("emitting function decls @ " + binary.length);
+ binary.emit_section(kFunctionSectionCode, section => {
+ section.emit_u32v(wasm.functions.length);
+ for (let func of wasm.functions) {
+ section.emit_u32v(func.type_index);
+ }
+ });
+ }
+
+ // Add table section
+ if (wasm.tables.length > 0) {
+ if (debug) print ("emitting tables @ " + binary.length);
+ binary.emit_section(kTableSectionCode, section => {
+ section.emit_u32v(wasm.tables.length);
+ for (let table of wasm.tables) {
+ section.emit_u8(table.type);
+ section.emit_u8(table.has_max);
+ section.emit_u32v(table.initial_size);
+ if (table.has_max) section.emit_u32v(table.max_size);
+ }
+ });
+ }
+
+ // Add memory section
+ if (wasm.memory !== undefined) {
+ if (debug) print("emitting memory @ " + binary.length);
+ binary.emit_section(kMemorySectionCode, section => {
+ section.emit_u8(1); // one memory entry
+ const has_max = wasm.memory.max !== undefined;
+ const is_shared = wasm.memory.shared !== undefined;
+ // Emit flags (bit 0: reszeable max, bit 1: shared memory)
+ if (is_shared) {
+ section.emit_u8(has_max ? kSharedHasMaximumFlag : 2);
+ } else {
+ section.emit_u8(has_max ? kHasMaximumFlag : 0);
+ }
+ section.emit_u32v(wasm.memory.min);
+ if (has_max) section.emit_u32v(wasm.memory.max);
+ });
+ }
+
+ // Add global section.
+ if (wasm.globals.length > 0) {
+ if (debug) print ("emitting globals @ " + binary.length);
+ binary.emit_section(kGlobalSectionCode, section => {
+ section.emit_u32v(wasm.globals.length);
+ for (let global of wasm.globals) {
+ section.emit_u8(global.type);
+ section.emit_u8(global.mutable);
+ if ((typeof global.init_index) == "undefined") {
+ // Emit a constant initializer.
+ switch (global.type) {
+ case kWasmI32:
+ section.emit_u8(kExprI32Const);
+ section.emit_u32v(global.init);
+ break;
+ case kWasmI64:
+ section.emit_u8(kExprI64Const);
+ section.emit_u64v(global.init);
+ break;
+ case kWasmF32:
+ section.emit_bytes(wasmF32Const(global.init));
+ break;
+ case kWasmF64:
+ section.emit_bytes(wasmF64Const(global.init));
+ break;
+ case kWasmAnyFunc:
+ case kWasmAnyRef:
+ if (global.function_index !== undefined) {
+ section.emit_u8(kExprRefFunc);
+ section.emit_u32v(global.function_index);
+ } else {
+ section.emit_u8(kExprRefNull);
+ }
+ break;
+ }
+ } else {
+ // Emit a global-index initializer.
+ section.emit_u8(kExprGlobalGet);
+ section.emit_u32v(global.init_index);
+ }
+ section.emit_u8(kExprEnd); // end of init expression
+ }
+ });
+ }
+
+ // Add exceptions.
+ if (wasm.exceptions.length > 0) {
+ if (debug) print("emitting exceptions @ " + binary.length);
+ binary.emit_section(kExceptionSectionCode, section => {
+ section.emit_u32v(wasm.exceptions.length);
+ for (let type of wasm.exceptions) {
+ section.emit_u32v(kExceptionAttribute);
+ section.emit_u32v(type);
+ }
+ });
+ }
+
+ // Add export table.
+ var mem_export = (wasm.memory !== undefined && wasm.memory.exp);
+ var exports_count = wasm.exports.length + (mem_export ? 1 : 0);
+ if (exports_count > 0) {
+ if (debug) print("emitting exports @ " + binary.length);
+ binary.emit_section(kExportSectionCode, section => {
+ section.emit_u32v(exports_count);
+ for (let exp of wasm.exports) {
+ section.emit_string(exp.name);
+ section.emit_u8(exp.kind);
+ section.emit_u32v(exp.index);
+ }
+ if (mem_export) {
+ section.emit_string("memory");
+ section.emit_u8(kExternalMemory);
+ section.emit_u8(0);
+ }
+ });
+ }
+
+ // Add start function section.
+ if (wasm.start_index !== undefined) {
+ if (debug) print("emitting start function @ " + binary.length);
+ binary.emit_section(kStartSectionCode, section => {
+ section.emit_u32v(wasm.start_index);
+ });
+ }
+
+ // Add element segments
+ if (wasm.element_segments.length > 0) {
+ if (debug) print("emitting element segments @ " + binary.length);
+ binary.emit_section(kElementSectionCode, section => {
+ var inits = wasm.element_segments;
+ section.emit_u32v(inits.length);
+
+ for (let init of inits) {
+ if (init.is_active) {
+ // Active segment.
+ if (init.table == 0) {
+ section.emit_u32v(kActiveNoIndex);
+ } else {
+ section.emit_u32v(kActiveWithIndex);
+ section.emit_u32v(init.table);
+ }
+ if (init.is_global) {
+ section.emit_u8(kExprGlobalGet);
+ } else {
+ section.emit_u8(kExprI32Const);
+ }
+ section.emit_u32v(init.base);
+ section.emit_u8(kExprEnd);
+ if (init.table != 0) {
+ section.emit_u8(kExternalFunction);
+ }
+ section.emit_u32v(init.array.length);
+ for (let index of init.array) {
+ section.emit_u32v(index);
+ }
+ } else {
+ // Passive segment.
+ section.emit_u8(kPassiveWithElements); // flags
+ section.emit_u8(kWasmAnyFunc);
+ section.emit_u32v(init.array.length);
+ for (let index of init.array) {
+ if (index === null) {
+ section.emit_u8(kExprRefNull);
+ section.emit_u8(kExprEnd);
+ } else {
+ section.emit_u8(kExprRefFunc);
+ section.emit_u32v(index);
+ section.emit_u8(kExprEnd);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ // If there are any passive data segments, add the DataCount section.
+ if (wasm.data_segments.some(seg => !seg.is_active)) {
+ binary.emit_section(kDataCountSectionCode, section => {
+ section.emit_u32v(wasm.data_segments.length);
+ });
+ }
+
+ // Add function bodies.
+ if (wasm.functions.length > 0) {
+ // emit function bodies
+ if (debug) print("emitting code @ " + binary.length);
+ binary.emit_section(kCodeSectionCode, section => {
+ section.emit_u32v(wasm.functions.length);
+ let header = new Binary;
+ for (let func of wasm.functions) {
+ header.reset();
+ // Function body length will be patched later.
+ let local_decls = [];
+ for (let l of func.locals || []) {
+ if (l.i32_count > 0) {
+ local_decls.push({count: l.i32_count, type: kWasmI32});
+ }
+ if (l.i64_count > 0) {
+ local_decls.push({count: l.i64_count, type: kWasmI64});
+ }
+ if (l.f32_count > 0) {
+ local_decls.push({count: l.f32_count, type: kWasmF32});
+ }
+ if (l.f64_count > 0) {
+ local_decls.push({count: l.f64_count, type: kWasmF64});
+ }
+ if (l.s128_count > 0) {
+ local_decls.push({count: l.s128_count, type: kWasmS128});
+ }
+ if (l.anyref_count > 0) {
+ local_decls.push({count: l.anyref_count, type: kWasmAnyRef});
+ }
+ if (l.anyfunc_count > 0) {
+ local_decls.push({count: l.anyfunc_count, type: kWasmAnyFunc});
+ }
+ }
+
+ header.emit_u32v(local_decls.length);
+ for (let decl of local_decls) {
+ header.emit_u32v(decl.count);
+ header.emit_u8(decl.type);
+ }
+
+ section.emit_u32v(header.length + func.body.length);
+ section.emit_bytes(header.trunc_buffer());
+ section.emit_bytes(func.body);
+ }
+ });
+ }
+
+ // Add data segments.
+ if (wasm.data_segments.length > 0) {
+ if (debug) print("emitting data segments @ " + binary.length);
+ binary.emit_section(kDataSectionCode, section => {
+ section.emit_u32v(wasm.data_segments.length);
+ for (let seg of wasm.data_segments) {
+ if (seg.is_active) {
+ section.emit_u8(0); // linear memory index 0 / flags
+ if (seg.is_global) {
+ // initializer is a global variable
+ section.emit_u8(kExprGlobalGet);
+ section.emit_u32v(seg.addr);
+ } else {
+ // initializer is a constant
+ section.emit_u8(kExprI32Const);
+ section.emit_u32v(seg.addr);
+ }
+ section.emit_u8(kExprEnd);
+ } else {
+ section.emit_u8(kPassive); // flags
+ }
+ section.emit_u32v(seg.data.length);
+ section.emit_bytes(seg.data);
+ }
+ });
+ }
+
+ // Add any explicitly added sections
+ for (let exp of wasm.explicit) {
+ if (debug) print("emitting explicit @ " + binary.length);
+ binary.emit_bytes(exp);
+ }
+
+ // Add names.
+ let num_function_names = 0;
+ let num_functions_with_local_names = 0;
+ for (let func of wasm.functions) {
+ if (func.name !== undefined) ++num_function_names;
+ if (func.numLocalNames() > 0) ++num_functions_with_local_names;
+ }
+ if (num_function_names > 0 || num_functions_with_local_names > 0 ||
+ wasm.name !== undefined) {
+ if (debug) print('emitting names @ ' + binary.length);
+ binary.emit_section(kUnknownSectionCode, section => {
+ section.emit_string('name');
+ // Emit module name.
+ if (wasm.name !== undefined) {
+ section.emit_section(kModuleNameCode, name_section => {
+ name_section.emit_string(wasm.name);
+ });
+ }
+ // Emit function names.
+ if (num_function_names > 0) {
+ section.emit_section(kFunctionNamesCode, name_section => {
+ name_section.emit_u32v(num_function_names);
+ for (let func of wasm.functions) {
+ if (func.name === undefined) continue;
+ name_section.emit_u32v(func.index);
+ name_section.emit_string(func.name);
+ }
+ });
+ }
+ // Emit local names.
+ if (num_functions_with_local_names > 0) {
+ section.emit_section(kLocalNamesCode, name_section => {
+ name_section.emit_u32v(num_functions_with_local_names);
+ for (let func of wasm.functions) {
+ if (func.numLocalNames() == 0) continue;
+ name_section.emit_u32v(func.index);
+ name_section.emit_u32v(func.numLocalNames());
+ for (let i = 0; i < func.local_names.length; ++i) {
+ if (func.local_names[i] === undefined) continue;
+ name_section.emit_u32v(i);
+ name_section.emit_string(func.local_names[i]);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ return binary.trunc_buffer();
+ }
+
+ toArray(debug = false) {
+ return Array.from(this.toBuffer(debug));
+ }
+
+ instantiate(ffi) {
+ let module = this.toModule();
+ let instance = new WebAssembly.Instance(module, ffi);
+ return instance;
+ }
+
+ asyncInstantiate(ffi) {
+ return WebAssembly.instantiate(this.toBuffer(), ffi)
+ .then(({module, instance}) => instance);
+ }
+
+ toModule(debug = false) {
+ return new WebAssembly.Module(this.toBuffer(debug));
+ }
+}
+
+function wasmSignedLeb(val, max_len = 5) {
+ let res = [];
+ for (let i = 0; i < max_len; ++i) {
+ let v = val & 0x7f;
+ // If {v} sign-extended from 7 to 32 bits is equal to val, we are done.
+ if (((v << 25) >> 25) == val) {
+ res.push(v);
+ return res;
+ }
+ res.push(v | 0x80);
+ val = val >> 7;
+ }
+ throw new Error(
+ 'Leb value <' + val + '> exceeds maximum length of ' + max_len);
+}
+
+function wasmI32Const(val) {
+ return [kExprI32Const, ...wasmSignedLeb(val, 5)];
+}
+
+function wasmF32Const(f) {
+ // Write in little-endian order at offset 0.
+ data_view.setFloat32(0, f, true);
+ return [
+ kExprF32Const, byte_view[0], byte_view[1], byte_view[2], byte_view[3]
+ ];
+}
+
+function wasmF64Const(f) {
+ // Write in little-endian order at offset 0.
+ data_view.setFloat64(0, f, true);
+ return [
+ kExprF64Const, byte_view[0], byte_view[1], byte_view[2],
+ byte_view[3], byte_view[4], byte_view[5], byte_view[6], byte_view[7]
+ ];
+}
diff --git a/test/fixtures/wpt/wasm/webapi/META.yml b/test/fixtures/wpt/wasm/webapi/META.yml
new file mode 100644
index 00000000000..69715cd7c8d
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/META.yml
@@ -0,0 +1 @@
+spec: https://webassembly.github.io/spec/web-api/
diff --git a/test/fixtures/wpt/wasm/webapi/abort.any.js b/test/fixtures/wpt/wasm/webapi/abort.any.js
new file mode 100644
index 00000000000..f5ddd353aa9
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/abort.any.js
@@ -0,0 +1,37 @@
+const methods = [
+ "compileStreaming",
+ "instantiateStreaming",
+];
+
+for (const method of methods) {
+ promise_test(async t => {
+ const controller = new AbortController();
+ const signal = controller.signal;
+ controller.abort();
+ const request = fetch('../incrementer.wasm', { signal });
+ return promise_rejects_dom(t, 'AbortError', WebAssembly[method](request),
+ `${method} should reject`);
+ }, `${method}() on an already-aborted request should reject with AbortError`);
+
+ promise_test(async t => {
+ const controller = new AbortController();
+ const signal = controller.signal;
+ const request = fetch('../incrementer.wasm', { signal });
+ const promise = WebAssembly[method](request);
+ controller.abort();
+ return promise_rejects_dom(t, 'AbortError', promise, `${method} should reject`);
+ }, `${method}() synchronously followed by abort should reject with AbortError`);
+
+ promise_test(async t => {
+ const controller = new AbortController();
+ const signal = controller.signal;
+ return fetch('../incrementer.wasm', { signal })
+ .then(response => {
+ Promise.resolve().then(() => controller.abort());
+ return WebAssembly[method](response);
+ })
+ .catch(err => {
+ assert_equals(err.name, "AbortError");
+ });
+ }, `${method}() asynchronously racing with abort should succeed or reject with AbortError`);
+}
diff --git a/test/fixtures/wpt/wasm/webapi/body.any.js b/test/fixtures/wpt/wasm/webapi/body.any.js
new file mode 100644
index 00000000000..4db7e8d123c
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/body.any.js
@@ -0,0 +1,19 @@
+// META: global=window,worker
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+for (const method of ["compileStreaming", "instantiateStreaming"]) {
+ promise_test(t => {
+ const buffer = new WasmModuleBuilder().toBuffer();
+ const argument = new Response(buffer, { headers: { "Content-Type": "application/wasm" } });
+ argument.arrayBuffer();
+ return promise_rejects_js(t, TypeError, WebAssembly[method](argument));
+ }, `${method} after consumption`);
+
+ promise_test(t => {
+ const buffer = new WasmModuleBuilder().toBuffer();
+ const argument = new Response(buffer, { headers: { "Content-Type": "application/wasm" } });
+ const promise = WebAssembly[method](argument);
+ argument.arrayBuffer();
+ return promise_rejects_js(t, TypeError, promise);
+ }, `${method} before consumption`);
+}
diff --git a/test/fixtures/wpt/wasm/webapi/contenttype.any.js b/test/fixtures/wpt/wasm/webapi/contenttype.any.js
new file mode 100644
index 00000000000..0a2f5f1122c
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/contenttype.any.js
@@ -0,0 +1,64 @@
+// META: global=window,worker
+// META: script=/wasm/jsapi/assertions.js
+
+promise_test(t => {
+ const response = fetch("/wasm/incrementer.wasm").then(res => new Response(res.body));
+ return promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(response));
+}, "Response with no Content-Type: compileStreaming");
+
+promise_test(t => {
+ const response = fetch("/wasm/incrementer.wasm").then(res => new Response(res.body));
+ return promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(response));
+}, "Response with no Content-Type: instantiateStreaming");
+
+const invalidContentTypes = [
+ "",
+ "application/javascript",
+ "application/octet-stream",
+ "text/wasm",
+ "application/wasm;",
+ "application/wasm;x",
+ "application/wasm;charset=UTF-8",
+];
+
+for (const contenttype of invalidContentTypes) {
+ promise_test(t => {
+ const response = fetch(`/wasm/incrementer.wasm?pipe=header(Content-Type,${encodeURIComponent(contenttype)})`);
+ return promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(response));
+ }, `Response with Content-Type ${format_value(contenttype)}: compileStreaming`);
+
+ promise_test(t => {
+ const response = fetch(`/wasm/incrementer.wasm?pipe=header(Content-Type,${encodeURIComponent(contenttype)})`);
+ return promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(response));
+ }, `Response with Content-Type ${format_value(contenttype)}: instantiateStreaming`);
+}
+
+const validContentTypes = [
+ "application/wasm",
+ "APPLICATION/wasm",
+ "APPLICATION/WASM",
+];
+
+for (const contenttype of validContentTypes) {
+ promise_test(async t => {
+ const response = fetch(`/wasm/incrementer.wasm?pipe=header(Content-Type,${encodeURIComponent(contenttype)})`);
+ const module = await WebAssembly.compileStreaming(response);
+ assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype,
+ "prototype");
+ }, `Response with Content-Type ${format_value(contenttype)}: compileStreaming`);
+
+ promise_test(async t => {
+ const response = fetch(`/wasm/incrementer.wasm?pipe=header(Content-Type,${encodeURIComponent(contenttype)})`);
+ const result = await WebAssembly.instantiateStreaming(response);
+ assert_WebAssemblyInstantiatedSource(
+ result,
+ {
+ "increment": {
+ "kind": "function",
+ "name": "0",
+ "length": 1
+ }
+ }
+ );
+ }, `Response with Content-Type ${format_value(contenttype)}: instantiateStreaming`);
+}
diff --git a/test/fixtures/wpt/wasm/webapi/empty-body.any.js b/test/fixtures/wpt/wasm/webapi/empty-body.any.js
new file mode 100644
index 00000000000..0771647b708
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/empty-body.any.js
@@ -0,0 +1,20 @@
+// META: global=window,worker
+
+const invalidArguments = [
+ [() => new Response(undefined, { headers: { "Content-Type": "application/wasm" } }), "no body"],
+ [() => new Response("", { headers: { "Content-Type": "application/wasm" } }), "empty body"],
+];
+
+for (const method of ["compileStreaming", "instantiateStreaming"]) {
+ for (const [argumentFactory, name] of invalidArguments) {
+ promise_test(t => {
+ const argument = argumentFactory();
+ return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly[method](argument));
+ }, `${method}: ${name}`);
+
+ promise_test(t => {
+ const argument = Promise.resolve(argumentFactory());
+ return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly[method](argument));
+ }, `${method}: ${name} in a promise`);
+ }
+}
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/execute-start.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/execute-start.tentative.html
new file mode 100644
index 00000000000..a35adbe8ebf
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/execute-start.tentative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>Check execution of WebAssembly start function</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Importing a WebAssembly module should execute the start function.");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_array_equals(log, ["executed"]);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./resources/execute-start.wasm" onerror="unreachable()""></script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/exported-names.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/exported-names.tentative.html
new file mode 100644
index 00000000000..16a9c59787b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/exported-names.tentative.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<title>Exported names from a WebAssembly module</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type=module>
+setup({ single_test: true });
+import * as mod from "./resources/exported-names.wasm";
+assert_array_equals(Object.getOwnPropertyNames(mod).sort(),
+ ["func", "glob", "mem", "tab"]);
+assert_true(mod.func instanceof Function);
+assert_true(mod.mem instanceof WebAssembly.Memory);
+assert_true(mod.glob instanceof WebAssembly.Global);
+assert_true(mod.tab instanceof WebAssembly.Table);
+assert_throws_js(TypeError, () => { mod.func = 2; });
+done();
+</script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/invalid-bytecode.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/invalid-bytecode.tentative.html
new file mode 100644
index 00000000000..0e447dbee51
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/invalid-bytecode.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Handling of importing invalid WebAssembly modules</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that imports of invalid WebAssembly modules leads to WebAssembly.CompileError on window.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 2);
+ assert_equals(log[0].constructor, WebAssembly.CompileError);
+ assert_equals(log[1].constructor, WebAssembly.CompileError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./resources/invalid-bytecode.wasm" onerror="unreachable()"></script>
+<script type="module" src="./resources/invalid-module.wasm" onerror="unreachable()"></script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html
new file mode 100644
index 00000000000..f45e06ece5f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>Cyclic linking between JavaScript and WebAssembly (JS higher)</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Check cyclic linking between JavaScript and WebAssembly where JavaScript is higher in the module graph.");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, WebAssembly.LinkError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 10);
+ assert_equals(log[1], 1);
+ assert_equals(log[3], 2);
+ assert_equals(log[5], 3);
+ assert_equals(log[7], 4);
+ assert_equals(log[9], 5);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./resources/js-wasm-cycle-value.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./resources/js-wasm-cycle-global.js"
+ onerror="unreachable()" onload="log.push(2)"></script>
+<script type="module" src="./resources/js-wasm-cycle-memory.js"
+ onerror="unreachable()" onload="log.push(3)"></script>
+<script type="module" src="./resources/js-wasm-cycle-table.js"
+ onerror="unreachable()" onload="log.push(4)"></script>
+<script type="module" src="./resources/js-wasm-cycle-function-error.js"
+ onerror="unreachable()" onload="log.push(5)"></script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html
new file mode 100644
index 00000000000..38b0d3203c2
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>Check bindings in JavaScript and WebAssembly cycle (JS higher)</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type=module>
+setup({ single_test: true });
+import { f } from "./resources/js-wasm-cycle.js";
+assert_equals(f(), 24);
+done();
+</script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/module-parse-error.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/module-parse-error.tentative.html
new file mode 100644
index 00000000000..0e447dbee51
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/module-parse-error.tentative.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>Handling of importing invalid WebAssembly modules</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ window.log = [];
+
+ window.addEventListener("error", ev => log.push(ev.error));
+
+ const test_load = async_test(
+ "Test that imports of invalid WebAssembly modules leads to WebAssembly.CompileError on window.");
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 2);
+ assert_equals(log[0].constructor, WebAssembly.CompileError);
+ assert_equals(log[1].constructor, WebAssembly.CompileError);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./resources/invalid-bytecode.wasm" onerror="unreachable()"></script>
+<script type="module" src="./resources/invalid-module.wasm" onerror="unreachable()"></script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.js
new file mode 100644
index 00000000000..e0dcf493f8e
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.js
@@ -0,0 +1 @@
+export { f } from "./resources/resolve-export.wasm";
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.tentative.html
new file mode 100644
index 00000000000..14688221021
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resolve-export.tentative.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<title>Check ResolveExport on invalid re-export from WebAssembly</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Re-export of missing Wasm export should result in SyntaxError.");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, SyntaxError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log[1], 1);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./resolve-export.js"
+ onerror="unreachable()" onload="log.push(1)"></script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm
new file mode 100644
index 00000000000..ecfdda1f9af
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/execute-start.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm
new file mode 100644
index 00000000000..ebffad193c3
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/exported-names.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm
new file mode 100644
index 00000000000..1ae8b721f3b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm
new file mode 100644
index 00000000000..dd711f0953b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/invalid-module.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js
new file mode 100644
index 00000000000..06cb8a0ad9f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js
@@ -0,0 +1,2 @@
+export const func = 42;
+import { f } from "./js-wasm-cycle-function-error.wasm";
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm
new file mode 100644
index 00000000000..b89d94dde74
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js
new file mode 100644
index 00000000000..1f375b8ce15
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js
@@ -0,0 +1,2 @@
+export const glob = new WebAssembly.Global({ value: "i32" }, 42);
+import { f } from "./js-wasm-cycle-global.wasm";
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm
new file mode 100644
index 00000000000..2a9017f87b1
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js
new file mode 100644
index 00000000000..92e37a86acd
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js
@@ -0,0 +1,2 @@
+export const mem = new WebAssembly.Memory({ initial: 10 });
+import { f } from "./js-wasm-cycle-memory.wasm";
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm
new file mode 100644
index 00000000000..e699a9b3c47
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js
new file mode 100644
index 00000000000..5d6794489f0
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js
@@ -0,0 +1,2 @@
+export const tab = new WebAssembly.Table({ element: "anyfunc" });
+import { f } from "./js-wasm-cycle-table.wasm";
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm
new file mode 100644
index 00000000000..ec4883e652e
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js
new file mode 100644
index 00000000000..f7b0d62080b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js
@@ -0,0 +1,2 @@
+export const val = 42;
+import { f } from "./js-wasm-cycle-value.wasm";
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm
new file mode 100644
index 00000000000..083409e2606
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.js
new file mode 100644
index 00000000000..8ee579e2ad3
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.js
@@ -0,0 +1,13 @@
+function f() { return 42; }
+export { f };
+
+import { mem, tab, glob, func } from "./js-wasm-cycle.wasm";
+assert_true(glob instanceof WebAssembly.Global);
+assert_equals(glob.valueOf(), 1);
+assert_true(mem instanceof WebAssembly.Memory);
+assert_true(tab instanceof WebAssembly.Table);
+assert_true(func instanceof Function);
+
+f = () => { return 24 };
+
+assert_equals(func(), 42);
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm
new file mode 100644
index 00000000000..77a3b86ab67
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/log.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/log.js
new file mode 100644
index 00000000000..0c4f5ed519b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/log.js
@@ -0,0 +1 @@
+export function logExec() { log.push("executed"); }
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm
new file mode 100644
index 00000000000..d8fc92d022f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/resolve-export.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm
new file mode 100644
index 00000000000..f9f0cf27992
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm
new file mode 100644
index 00000000000..0ee948f96fd
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm
new file mode 100644
index 00000000000..c27bcb068de
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm
new file mode 100644
index 00000000000..652ff143100
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.js
new file mode 100644
index 00000000000..78982c32dc6
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.js
@@ -0,0 +1 @@
+export let f = 5;
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm
new file mode 100644
index 00000000000..2f23c58520f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-func.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.js
new file mode 100644
index 00000000000..4258cd2d7d1
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.js
@@ -0,0 +1 @@
+export let g = 5;
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm
new file mode 100644
index 00000000000..2f8bd77940c
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-global.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.js
new file mode 100644
index 00000000000..4cee8898383
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.js
@@ -0,0 +1 @@
+export let m = 5;
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm
new file mode 100644
index 00000000000..d9474047cd3
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.js
new file mode 100644
index 00000000000..ca823646cbb
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.js
@@ -0,0 +1 @@
+export let t = 5;
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm
new file mode 100644
index 00000000000..8ccc8be7f21
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-import-table.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.js
new file mode 100644
index 00000000000..161edab4f6f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.js
@@ -0,0 +1,15 @@
+import * as mod from "./wasm-js-cycle.wasm";
+
+let jsGlob = new WebAssembly.Global({ value: "i32", mutable: true }, 42);
+let jsMem = new WebAssembly.Memory({ initial: 10 });
+let jsTab = new WebAssembly.Table({ initial: 10, element: "anyfunc" });
+let jsFunc = () => { return 42; };
+
+export { jsGlob, jsMem, jsTab, jsFunc };
+
+export function mutateBindings() {
+ jsGlob = 0;
+ jsMem = 0;
+ jsTab = 0;
+ jsFunc = 0;
+}
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm
new file mode 100644
index 00000000000..b700377b279
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker-helper.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker-helper.js
new file mode 100644
index 00000000000..277bb4c1ea5
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker-helper.js
@@ -0,0 +1 @@
+export function pm(x) { postMessage(x); }
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.js b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.js
new file mode 100644
index 00000000000..c72464f71a4
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.js
@@ -0,0 +1 @@
+import * as mod from "./worker.wasm"
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm
new file mode 100644
index 00000000000..e942dc54acf
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/resources/worker.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html
new file mode 100644
index 00000000000..3761a22f218
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<title>Check import and export between WebAssembly modules</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type=module>
+setup({ single_test: true });
+window.log = [];
+import { logExec } from "./resources/wasm-import-from-wasm.wasm";
+logExec();
+assert_equals(log.length, 1);
+assert_equals(log[0], "executed");
+done();
+</script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import.tentative.html
new file mode 100644
index 00000000000..243cfd46e4b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-import.tentative.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>Errors for imports of WebAssembly modules</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Invalid imports for WebAssembly modules should error.");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, WebAssembly.LinkError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log[1], 1);
+ assert_equals(log[3], 2);
+ assert_equals(log[5], 3);
+ assert_equals(log[7], 4);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./resources/wasm-import-func.wasm"
+ onerror="unreachable()" onload="log.push(1)"></script>
+<script type="module" src="./resources/wasm-import-memory.wasm"
+ onerror="unreachable()" onload="log.push(2)"></script>
+<script type="module" src="./resources/wasm-import-table.wasm"
+ onerror="unreachable()" onload="log.push(3)"></script>
+<script type="module" src="./resources/wasm-import-global.wasm"
+ onerror="unreachable()" onload="log.push(4)"></script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html
new file mode 100644
index 00000000000..298d4d40b00
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>Check bindings in JavaScript and WebAssembly cycle (Wasm higher)</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type=module>
+setup({ single_test: true });
+import * as wasm from "./resources/wasm-js-cycle.wasm";
+import * as js from "./resources/wasm-js-cycle.js";
+
+js.mutateBindings();
+
+assert_true(wasm.wasmGlob instanceof WebAssembly.Global);
+assert_equals(wasm.wasmGlob.valueOf(), 24);
+
+assert_true(wasm.wasmFunc instanceof Function);
+assert_equals(wasm.wasmFunc(), 43);
+
+assert_equals(wasm.incrementGlob(), 43);
+
+const buf = new Int32Array(wasm.wasmMem.buffer);
+assert_equals(buf[0], 0);
+assert_equals(wasm.mutateMem(), 42);
+assert_equals(buf[0], 42);
+
+assert_equals(wasm.wasmTab.get(0), null);
+const ref = wasm.mutateTab();
+assert_true(ref instanceof Function);
+assert_equals(wasm.wasmTab.get(0), ref);
+
+done();
+</script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html
new file mode 100644
index 00000000000..6c43e72b09b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>Errors for linking WebAssembly module scripts</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ setup({allow_uncaught_exception: true});
+
+ const test_load = async_test(
+ "Link errors for imports between WebAssembly modules should be reported.");
+
+ window.log = [];
+ window.addEventListener("error", ev => {
+ test_load.step(() => assert_equals(ev.error.constructor, WebAssembly.LinkError));
+ log.push(ev.message);
+ });
+
+ window.addEventListener("load", test_load.step_func_done(ev => {
+ assert_equals(log.length, 2);
+ assert_equals(log[1], 1);
+ }));
+
+ function unreachable() { log.push("unexpected"); }
+</script>
+<script type="module" src="./resources/wasm-import-error-from-wasm.wasm"
+ onerror="unreachable()" onload="log.push(1)"></script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import.tentative.html
new file mode 100644
index 00000000000..739f2d3f28c
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/worker-import.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>Testing import of WebAssembly from JavaScript worker</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type=module>
+setup({ single_test: true });
+const worker = new Worker("resources/worker.js", { type: "module" });
+worker.onmessage = (msg) => {
+ assert_equals(msg.data, 42);
+ done();
+}
+</script>
diff --git a/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html b/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html
new file mode 100644
index 00000000000..8002e07ce7f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/esm-integration/worker.tentative.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>Testing WebAssembly worker</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type=module>
+setup({ single_test: true });
+const worker = new Worker("resources/worker.wasm", { type: "module" });
+worker.onmessage = (msg) => {
+ assert_equals(msg, 42);
+ done();
+}
+</script>
diff --git a/test/fixtures/wpt/wasm/webapi/historical.any.js b/test/fixtures/wpt/wasm/webapi/historical.any.js
new file mode 100644
index 00000000000..257112c4160
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/historical.any.js
@@ -0,0 +1,29 @@
+// META: global=window,worker
+
+promise_test(async t => {
+ const db_name = "WebAssembly";
+ const obj_store = "store";
+ const module_key = "module";
+
+ await new Promise((resolve, reject) => {
+ const delete_request = indexedDB.deleteDatabase(db_name);
+ delete_request.onsuccess = resolve;
+ delete_request.onerror = reject;
+ });
+
+ const db = await new Promise((resolve, reject) => {
+ const open_request = indexedDB.open(db_name);
+ open_request.onupgradeneeded = function() {
+ open_request.result.createObjectStore(obj_store);
+ };
+ open_request.onsuccess = function() {
+ resolve(open_request.result);
+ };
+ open_request.onerror = reject;
+ });
+
+ const mod = await WebAssembly.compileStreaming(fetch('../incrementer.wasm'));
+ const tx = db.transaction(obj_store, 'readwrite');
+ const store = tx.objectStore(obj_store);
+ assert_throws_dom("DataCloneError", () => store.put(mod, module_key));
+});
diff --git a/test/fixtures/wpt/wasm/webapi/idlharness.any.js b/test/fixtures/wpt/wasm/webapi/idlharness.any.js
new file mode 100644
index 00000000000..0c4669e6caa
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/idlharness.any.js
@@ -0,0 +1,10 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+"use strict";
+
+idl_test(
+ ["wasm-web-api"],
+ ["wasm-js-api"],
+ idl_array => {}
+);
diff --git a/test/fixtures/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.js b/test/fixtures/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.js
new file mode 100644
index 00000000000..38ecc40252e
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/instantiateStreaming-bad-imports.any.js
@@ -0,0 +1,13 @@
+// META: global=window,worker
+// META: script=/wasm/jsapi/wasm-module-builder.js
+// META: script=/wasm/jsapi/bad-imports.js
+
+test_bad_imports((name, error, build, ...args) => {
+ promise_test(t => {
+ const builder = new WasmModuleBuilder();
+ build(builder);
+ const buffer = builder.toBuffer();
+ const response = new Response(buffer, { "headers": { "Content-Type": "application/wasm" } });
+ return promise_rejects_js(t, error, WebAssembly.instantiateStreaming(response, ...args));
+ }, name);
+});
diff --git a/test/fixtures/wpt/wasm/webapi/instantiateStreaming.any.js b/test/fixtures/wpt/wasm/webapi/instantiateStreaming.any.js
new file mode 100644
index 00000000000..cf3a5e7331f
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/instantiateStreaming.any.js
@@ -0,0 +1,49 @@
+// META: global=window,worker
+// 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();
+});
+
+for (const [name, fn] of instanceTestFactory) {
+ promise_test(async () => {
+ const { buffer, args, exports, verify } = fn();
+ const response = new Response(buffer, { "headers": { "Content-Type": "application/wasm" } });
+ const result = await WebAssembly.instantiateStreaming(response, ...args);
+ assert_WebAssemblyInstantiatedSource(result, exports);
+ verify(result.instance);
+ }, name);
+}
+
+promise_test(async () => {
+ const builder = new WasmModuleBuilder();
+ builder.addImportedGlobal("module", "global", kWasmI32);
+ const buffer = builder.toBuffer();
+ const response = new Response(buffer, { "headers": { "Content-Type": "application/wasm" } });
+ 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.instantiateStreaming(response, imports);
+ assert_array_equals(order, []);
+ const result = await p;
+ assert_WebAssemblyInstantiatedSource(result, {});
+ assert_array_equals(order, expected);
+}, "Synchronous options handling");
diff --git a/test/fixtures/wpt/wasm/webapi/invalid-args.any.js b/test/fixtures/wpt/wasm/webapi/invalid-args.any.js
new file mode 100644
index 00000000000..b27e018a984
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/invalid-args.any.js
@@ -0,0 +1,28 @@
+// META: global=window,worker
+
+const invalidArguments = [
+ [undefined],
+ [null],
+ [true],
+ ["test"],
+ [Symbol()],
+ [0],
+ [0.1],
+ [NaN],
+ [{}, "Empty object"],
+ [Response, "Response interface object"],
+ [Response.prototype, "Response interface prototype object"],
+];
+
+for (const method of ["compileStreaming", "instantiateStreaming"]) {
+ for (const [argument, name = format_value(argument)] of invalidArguments) {
+ promise_test(t => {
+ return promise_rejects_js(t, TypeError, WebAssembly[method](argument));
+ }, `${method}: ${name}`);
+
+ promise_test(t => {
+ const promise = Promise.resolve(argument);
+ return promise_rejects_js(t, TypeError, WebAssembly[method](argument));
+ }, `${method}: ${name} in a promise`);
+ }
+}
diff --git a/test/fixtures/wpt/wasm/webapi/invalid-code.any.js b/test/fixtures/wpt/wasm/webapi/invalid-code.any.js
new file mode 100644
index 00000000000..37373d49971
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/invalid-code.any.js
@@ -0,0 +1,21 @@
+// META: global=window,worker
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+let emptyModuleBinary;
+setup(() => {
+ emptyModuleBinary = new WasmModuleBuilder().toBuffer();
+});
+
+for (const method of ["compileStreaming", "instantiateStreaming"]) {
+ promise_test(t => {
+ const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0]));
+ const response = new Response(buffer, { headers: { "Content-Type": "application/wasm" } });
+ return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly[method](response));
+ }, `Invalid code (0x0000): ${method}`);
+
+ promise_test(t => {
+ const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0xCA, 0xFE]));
+ const response = new Response(buffer, { headers: { "Content-Type": "application/wasm" } });
+ return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly[method](response));
+ }, `Invalid code (0xCAFE): ${method}`);
+}
diff --git a/test/fixtures/wpt/wasm/webapi/modified-contenttype.any.js b/test/fixtures/wpt/wasm/webapi/modified-contenttype.any.js
new file mode 100644
index 00000000000..354930517c7
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/modified-contenttype.any.js
@@ -0,0 +1,24 @@
+// META: global=window,worker
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+["compileStreaming", "instantiateStreaming"].forEach(method => {
+ promise_test(async t => {
+ const buffer = new WasmModuleBuilder().toBuffer();
+ const argument = new Response(buffer, { headers: { "Content-Type": "test/test" } });
+ argument.headers.set("Content-Type", "application/wasm");
+ // This should resolve successfully
+ await WebAssembly[method](argument);
+ // Ensure body can only be read once
+ return promise_rejects_js(t, TypeError, argument.blob());
+ }, `${method} with Content-Type set late`);
+
+ promise_test(async t => {
+ const buffer = new WasmModuleBuilder().toBuffer();
+ const argument = new Response(buffer, { headers: { "Content-Type": "application/wasm" } });
+ argument.headers.delete("Content-Type");
+ // Ensure Wasm cannot be created
+ await promise_rejects_js(t, TypeError, WebAssembly[method](argument));
+ // This should resolve successfully
+ await argument.arrayBuffer();
+ }, `${method} with Content-Type removed late`);
+});
diff --git a/test/fixtures/wpt/wasm/webapi/origin.sub.any.js b/test/fixtures/wpt/wasm/webapi/origin.sub.any.js
new file mode 100644
index 00000000000..bf7901eedde
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/origin.sub.any.js
@@ -0,0 +1,15 @@
+// META: global=window,worker
+
+for (const method of ["compileStreaming", "instantiateStreaming"]) {
+ promise_test(t => {
+ const url = "http://{{domains[www]}}:{{ports[http][0]}}/wasm/incrementer.wasm";
+ const response = fetch(url, { "mode": "no-cors" });
+ return promise_rejects_js(t, TypeError, WebAssembly[method](response));
+ }, `Opaque response: ${method}`);
+
+ promise_test(t => {
+ const url = "/fetch/api/resources/redirect.py?redirect_status=301&location=/wasm/incrementer.wasm";
+ const response = fetch(url, { "mode": "no-cors", "redirect": "manual" });
+ return promise_rejects_js(t, TypeError, WebAssembly[method](response));
+ }, `Opaque redirect response: ${method}`);
+}
diff --git a/test/fixtures/wpt/wasm/webapi/rejected-arg.any.js b/test/fixtures/wpt/wasm/webapi/rejected-arg.any.js
new file mode 100644
index 00000000000..49018db5e89
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/rejected-arg.any.js
@@ -0,0 +1,9 @@
+// META: global=window,worker
+
+for (const method of ["compileStreaming", "instantiateStreaming"]) {
+ promise_test(t => {
+ const error = { "name": "custom error" };
+ const promise = Promise.reject(error);
+ return promise_rejects_exactly(t, error, WebAssembly[method](promise));
+ }, `${method}`);
+}
diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm b/test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm
new file mode 100644
index 00000000000..47afcdef2a2
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/resources/incrementer.no_mime_type.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm
new file mode 100644
index 00000000000..47afcdef2a2
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm.headers b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm.headers
new file mode 100644
index 00000000000..76b9c163b6c
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wasm.headers
@@ -0,0 +1,2 @@
+Content-Type: application/wasm
+Cache-Control: max-age=3600
diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm
new file mode 100644
index 00000000000..47afcdef2a2
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm
Binary files differ
diff --git a/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers
new file mode 100644
index 00000000000..833ee71634d
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers
@@ -0,0 +1,2 @@
+Content-Type: text/css
+Cache-Control: max-age=3600
diff --git a/test/fixtures/wpt/wasm/webapi/status.any.js b/test/fixtures/wpt/wasm/webapi/status.any.js
new file mode 100644
index 00000000000..f3859646cc5
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/status.any.js
@@ -0,0 +1,21 @@
+// META: global=window,worker
+
+const statuses = [
+ 0,
+ 300,
+ 400,
+ 404,
+ 500,
+ 600,
+ 700,
+ 999,
+];
+
+for (const method of ["compileStreaming", "instantiateStreaming"]) {
+ for (const status of statuses) {
+ promise_test(t => {
+ const response = fetch(`status.py?status=${status}`);
+ return promise_rejects_js(t, TypeError, WebAssembly[method](response));
+ }, `Response with status ${status}: ${method}`);
+ }
+}
diff --git a/test/fixtures/wpt/wasm/webapi/wasm_stream_compile_test.html b/test/fixtures/wpt/wasm/webapi/wasm_stream_compile_test.html
new file mode 100644
index 00000000000..790410e425b
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/wasm_stream_compile_test.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAssembly.compileStreaming</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+ promise_test(async function() {
+ const response = await fetch('resources/incrementer.wasm');
+ const module = await WebAssembly.compileStreaming(response);
+ assert_true(module instanceof WebAssembly.Module);
+ }, "compileStreaming using resolved response");
+
+ promise_test(async function() {
+ const response = await fetch('resources/incrementer.wasm');
+ const module = await WebAssembly.compileStreaming(response);
+ const instance = new WebAssembly.Instance(module);
+ assert_true(instance instanceof WebAssembly.Instance);
+ }, "compileStreaming using resolved response and check instantiate");
+
+ promise_test(async function() {
+ const result = fetch('resources/incrementer.wasm');
+ const module = await WebAssembly.compileStreaming(result);
+ const instance = new WebAssembly.Instance(module);
+ assert_true(instance instanceof WebAssembly.Instance);
+ }, "compileStreaming using promise response from fetch and check instantiate");
+
+ promise_test(async function(t) {
+ const result = fetch('resources/incrementer.wrong_mime_type.wasm');
+ await promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(result));
+ }, "compileStreaming raise error if wrong mime type");
+
+ promise_test(async function(t) {
+ const result = fetch('resources/incrementer.no_mime_type.wasm?pipe=header(Content-Type,)');
+ await promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(result));
+ }, "compileStreaming raise error if no mime type");
+
+ promise_test(async function(t) {
+ const result = fetch('webapi/status.py?status=404');
+ await promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(result));
+ }, "compileStreaming raise error if 404 status");
+
+ const getWasmUrl = fileName => {
+ const host_info = get_host_info();
+ const url = host_info.HTTP_ORIGIN_WITH_DIFFERENT_PORT + '/wasm/webapi/';
+ return url + fileName + "?pipe=header(Access-Control-Allow-Origin,*)";
+ };
+
+ promise_test(async function() {
+ const result = fetch(getWasmUrl('resources/incrementer.wasm'), {"mode": "cors"} );
+ const module = await WebAssembly.compileStreaming(result);
+ assert_true(module instanceof WebAssembly.Module);
+ }, "compileStreaming check CORS");
+
+ promise_test(async function(t) {
+ const result = fetch(getWasmUrl('resources/incrementer.wasm'), {"mode": "no-cors"} );
+ await promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(result));
+ }, "compileStreaming raise error if no-cors");
+
+ promise_test(async function() {
+ const v = await fetch('resources/incrementer.wasm');
+ const buffer = await v.arrayBuffer();
+ const response = new Response(buffer, { headers: { "Content-Type" : "application/wasm" }});
+ const module = await WebAssembly.compileStreaming(response);
+ assert_true(module instanceof WebAssembly.Module);
+ }, "compileStreaming receive promise with response created from ArrayBuffer");
+
+ promise_test(async function() {
+ const v = await fetch('resources/incrementer.wasm');
+ const buffer = await v.arrayBuffer();
+ const stream = new ReadableStream({
+ start(controller) {
+ (async () => {
+ await Promise.resolve().then(() => controller.enqueue(new Uint8Array(buffer.slice(0, 20))));
+ await Promise.resolve().then(() => controller.enqueue(new Uint8Array(buffer.slice(20, buffer.byteLength))));
+ await Promise.resolve().then(() => controller.close());
+ })();
+ }
+ });
+ const response = new Response(stream, { headers: { "Content-Type" : "application/wasm" }});
+ const module = await WebAssembly.compileStreaming(response);
+ assert_true(module instanceof WebAssembly.Module);
+ }, "compileStreaming using ReadableStream with Uint8Array chunks");
+
+ promise_test(async function(t) {
+ const v = await fetch('resources/incrementer.wasm');
+ const buffer = await v.arrayBuffer();
+ const stream = new ReadableStream({
+ start(controller) {
+ // Enqueuing an ArrayBuffer rather a Uint8Array per
+ // https://streams.spec.whatwg.org/#read-loop
+ controller.enqueue(buffer);
+ controller.close();
+ }
+ });
+ const response = new Response(stream, { headers: { "Content-Type" : "application/wasm" }});
+ await promise_rejects_js(t, TypeError, WebAssembly.compileStreaming(response));
+ }, "compileStreaming using ReadableStream with ArrayBuffer chunk");
+
+ promise_test(async function() {
+ const response = await fetch('resources/incrementer.wasm');
+ const blob = await response.blob();
+ const module = await WebAssembly.compileStreaming(new Response(blob, { headers: { "Content-Type" : "application/wasm" }}));
+ assert_true(module instanceof WebAssembly.Module);
+ }, "compileStreaming using blob");
+
+ promise_test(async function(t) {
+ const response = await fetch('resources/incrementer.wasm');
+ const blob = await response.blob();
+ const formData = new FormData;
+ formData.append('blob', blob);
+ formData.append('blob2', "Hello");
+ await promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compileStreaming(new Response(formData, { headers: { "Content-Type" : "application/wasm" }})));
+ }, "compileStreaming using FormData");
+</script>
diff --git a/test/fixtures/wpt/wasm/webapi/wasm_stream_instantiate_test.html b/test/fixtures/wpt/wasm/webapi/wasm_stream_instantiate_test.html
new file mode 100644
index 00000000000..f39f6504953
--- /dev/null
+++ b/test/fixtures/wpt/wasm/webapi/wasm_stream_instantiate_test.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebAssembly.instantiateStreaming</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+ promise_test(async function() {
+ const response = await fetch('resources/incrementer.wasm');
+ const { instance, module } = await WebAssembly.instantiateStreaming(response);
+ assert_true(instance instanceof WebAssembly.Instance);
+ assert_true(module instanceof WebAssembly.Module);
+ }, "instantiateStreaming using resolved response");
+
+ promise_test(async function() {
+ const response = await fetch('resources/incrementer.wasm');
+ const { instance } = await WebAssembly.instantiateStreaming(response);
+ assert_true(instance instanceof WebAssembly.Instance);
+ }, "instantiateStreaming using resolved response and check instantiate");
+
+ promise_test(async function() {
+ const result = fetch('resources/incrementer.wasm');
+ const { instance } = await WebAssembly.instantiateStreaming(result);
+ assert_true(instance instanceof WebAssembly.Instance);
+ }, "instantiateStreaming using promise response from fetch and check instantiate");
+
+ promise_test(async function(t) {
+ const result = fetch('resources/incrementer.wrong_mime_type.wasm');
+ await promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(result));
+ }, "instantiateStreaming raise error if wrong mime type");
+
+ promise_test(async function(t) {
+ const result = fetch('resources/incrementer.no_mime_type.wasm?pipe=header(Content-Type,)');
+ await promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(result));
+ }, "instantiateStreaming raise error if no mime type");
+
+ promise_test(async function(t) {
+ const result = fetch('webapi/status.py?status=404');
+ await promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(result));
+ }, "instantiateStreaming raise error if 404 status");
+
+ const getWasmUrl = fileName => {
+ const host_info = get_host_info();
+ const url = host_info.HTTP_ORIGIN_WITH_DIFFERENT_PORT + '/wasm/webapi/';
+ return url + fileName + "?pipe=header(Access-Control-Allow-Origin,*)";
+ };
+
+ promise_test(async function() {
+ const result = fetch(getWasmUrl('resources/incrementer.wasm'), {"mode": "cors"} );
+ const { instance } = await WebAssembly.instantiateStreaming(result);
+ assert_true(instance instanceof WebAssembly.Instance);
+ }, "instantiateStreaming check CORS");
+
+ promise_test(async function(t) {
+ const result = fetch(getWasmUrl('resources/incrementer.wasm'), {"mode": "no-cors"} );
+ await promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(result));
+ }, "instantiateStreaming raise error if no-cors");
+
+ promise_test(async function() {
+ const v = await fetch('resources/incrementer.wasm');
+ const buffer = await v.arrayBuffer();
+ const response = new Response(buffer, { headers: { "Content-Type" : "application/wasm" }});
+ const { instance } = await WebAssembly.instantiateStreaming(response);
+ assert_true(instance instanceof WebAssembly.Instance);
+ }, "instantiateStreaming receive promise with response created from ArrayBuffer");
+
+ promise_test(async function() {
+ const v = await fetch('resources/incrementer.wasm');
+ const buffer = await v.arrayBuffer();
+ const stream = new ReadableStream({
+ start(controller) {
+ (async () => {
+ await Promise.resolve().then(() => controller.enqueue(new Uint8Array(buffer.slice(0, 20))));
+ await Promise.resolve().then(() => controller.enqueue(new Uint8Array(buffer.slice(20, buffer.byteLength))));
+ await Promise.resolve().then(() => controller.close());
+ })();
+ }
+ });
+ const response = new Response(stream, { headers: { "Content-Type" : "application/wasm" }});
+ const { instance } = await WebAssembly.instantiateStreaming(response);
+ assert_true(instance instanceof WebAssembly.Instance);
+ }, "instantiateStreaming using ReadableStream with Uint8Array chunks");
+
+ promise_test(async function(t) {
+ const v = await fetch('resources/incrementer.wasm');
+ const buffer = await v.arrayBuffer();
+ const stream = new ReadableStream({
+ start(controller) {
+ // Enqueuing an ArrayBuffer rather a Uint8Array per
+ // https://streams.spec.whatwg.org/#read-loop
+ controller.enqueue(buffer);
+ controller.close();
+ }
+ });
+ const response = new Response(stream, { headers: { "Content-Type" : "application/wasm" }});
+ await promise_rejects_js(t, TypeError, WebAssembly.instantiateStreaming(response));
+ }, "instantiateStreaming using ReadableStream with ArrayBuffer chunk");
+
+ promise_test(async function() {
+ const response = await fetch('resources/incrementer.wasm');
+ const blob = await response.blob();
+ const { instance, module } = await WebAssembly.instantiateStreaming(new Response(blob, { headers: { "Content-Type" : "application/wasm" }}));
+ assert_true(instance instanceof WebAssembly.Instance);
+ assert_true(module instanceof WebAssembly.Module);
+ }, "instantiateStreaming using blob");
+
+ promise_test(async function(t) {
+ const response = await fetch('resources/incrementer.wasm');
+ const blob = await response.blob();
+ const formData = new FormData;
+ formData.append('blob', blob);
+ formData.append('blob2', "Hello");
+ await promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.instantiateStreaming(new Response(formData, { headers: { "Content-Type" : "application/wasm" }})));
+ }, "instantiateStreaming using FormData");
+</script>
diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js
index d5f69e8165f..d5bf6e133eb 100644
--- a/test/parallel/test-bootstrap-modules.js
+++ b/test/parallel/test-bootstrap-modules.js
@@ -42,6 +42,7 @@ const expectedModules = new Set([
'Internal Binding util',
'Internal Binding uv',
'Internal Binding v8',
+ 'Internal Binding wasm_web_api',
'Internal Binding worker',
'NativeModule buffer',
'NativeModule events',
diff --git a/test/parallel/test-fetch-disabled.mjs b/test/parallel/test-fetch-disabled.mjs
index f06d484701c..ea6b6807d8d 100644
--- a/test/parallel/test-fetch-disabled.mjs
+++ b/test/parallel/test-fetch-disabled.mjs
@@ -8,3 +8,6 @@ assert.strictEqual(typeof globalThis.FormData, 'undefined');
assert.strictEqual(typeof globalThis.Headers, 'undefined');
assert.strictEqual(typeof globalThis.Request, 'undefined');
assert.strictEqual(typeof globalThis.Response, 'undefined');
+
+assert.strictEqual(typeof WebAssembly.compileStreaming, 'undefined');
+assert.strictEqual(typeof WebAssembly.instantiateStreaming, 'undefined');
diff --git a/test/parallel/test-wasm-web-api.js b/test/parallel/test-wasm-web-api.js
new file mode 100644
index 00000000000..9576e13d669
--- /dev/null
+++ b/test/parallel/test-wasm-web-api.js
@@ -0,0 +1,226 @@
+'use strict';
+
+const common = require('../common');
+const fixtures = require('../common/fixtures');
+
+const assert = require('assert');
+const events = require('events');
+const fs = require('fs/promises');
+const { createServer } = require('http');
+
+assert.strictEqual(typeof WebAssembly.compileStreaming, 'function');
+assert.strictEqual(typeof WebAssembly.instantiateStreaming, 'function');
+
+const simpleWasmBytes = fixtures.readSync('simple.wasm');
+
+// Sets up an HTTP server with the given response handler and calls fetch() to
+// obtain a Response from the newly created server.
+async function testRequest(handler) {
+ const server = createServer((_, res) => handler(res)).unref().listen(0);
+ await events.once(server, 'listening');
+ const { port } = server.address();
+ return fetch(`http://127.0.0.1:${port}/`);
+}
+
+// Runs the given function both with the promise itself and as a continuation
+// of the promise. We use this to test that the API accepts not just a Response
+// but also a Promise that resolves to a Response.
+function withPromiseAndResolved(makePromise, consume) {
+ return Promise.all([
+ consume(makePromise()),
+ makePromise().then(consume),
+ ]);
+}
+
+// The makeResponsePromise function must return a Promise that resolves to a
+// Response. The checkResult function receives the Promise returned by
+// WebAssembly.compileStreaming and must return a Promise itself.
+function testCompileStreaming(makeResponsePromise, checkResult) {
+ return withPromiseAndResolved(
+ common.mustCall(makeResponsePromise, 2),
+ common.mustCall((response) => {
+ return checkResult(WebAssembly.compileStreaming(response));
+ }, 2)
+ );
+}
+
+function testCompileStreamingSuccess(makeResponsePromise) {
+ return testCompileStreaming(makeResponsePromise, async (modPromise) => {
+ const mod = await modPromise;
+ assert.strictEqual(mod.constructor, WebAssembly.Module);
+ });
+}
+
+function testCompileStreamingRejection(makeResponsePromise, rejection) {
+ return testCompileStreaming(makeResponsePromise, (modPromise) => {
+ assert.strictEqual(modPromise.constructor, Promise);
+ return assert.rejects(modPromise, rejection);
+ });
+}
+
+function testCompileStreamingSuccessUsingFetch(responseCallback) {
+ return testCompileStreamingSuccess(() => testRequest(responseCallback));
+}
+
+function testCompileStreamingRejectionUsingFetch(responseCallback, rejection) {
+ return testCompileStreamingRejection(() => testRequest(responseCallback),
+ rejection);
+}
+
+(async () => {
+ // A non-Response should cause a TypeError.
+ for (const invalid of [undefined, null, 0, true, 'foo', {}, [], Symbol()]) {
+ await withPromiseAndResolved(() => Promise.resolve(invalid), (arg) => {
+ return assert.rejects(() => WebAssembly.compileStreaming(arg), {
+ name: 'TypeError',
+ code: 'ERR_INVALID_ARG_TYPE',
+ message: /^The "source" argument .*$/
+ });
+ });
+ }
+
+ // When given a Promise, any rejection should be propagated as-is.
+ {
+ const err = new RangeError('foo');
+ await assert.rejects(() => {
+ return WebAssembly.compileStreaming(Promise.reject(err));
+ }, (actualError) => actualError === err);
+ }
+
+ // A valid WebAssembly file with the correct MIME type.
+ await testCompileStreamingSuccessUsingFetch((res) => {
+ res.setHeader('Content-Type', 'application/wasm');
+ res.end(simpleWasmBytes);
+ });
+
+ // The same valid WebAssembly file with the same MIME type, but using a
+ // Response whose body is a Buffer instead of calling fetch().
+ await testCompileStreamingSuccess(() => {
+ return Promise.resolve(new Response(simpleWasmBytes, {
+ status: 200,
+ headers: { 'Content-Type': 'application/wasm' }
+ }));
+ });
+
+ // The same valid WebAssembly file with the same MIME type, but using a
+ // Response whose body is a ReadableStream instead of calling fetch().
+ await testCompileStreamingSuccess(async () => {
+ const handle = await fs.open(fixtures.path('simple.wasm'));
+ const stream = handle.readableWebStream();
+ return Promise.resolve(new Response(stream, {
+ status: 200,
+ headers: { 'Content-Type': 'application/wasm' }
+ }));
+ });
+
+ // A larger valid WebAssembly file with the correct MIME type that causes the
+ // client to pass it to the compiler in many separate chunks. For this, we use
+ // the same WebAssembly file as in the previous test but insert useless custom
+ // sections into the WebAssembly module to increase the file size without
+ // changing the relevant contents.
+ await testCompileStreamingSuccessUsingFetch((res) => {
+ res.setHeader('Content-Type', 'application/wasm');
+
+ // Send the WebAssembly magic and version first.
+ res.write(simpleWasmBytes.slice(0, 8), common.mustCall());
+
+ // Construct a 4KiB custom section.
+ const customSection = Buffer.concat([
+ Buffer.from([
+ 0, // Custom section.
+ 134, 32, // (134 & 0x7f) + 0x80 * 32 = 6 + 4096 bytes in this section.
+ 5, // The length of the following section name.
+ ]),
+ Buffer.from('?'.repeat(5)), // The section name
+ Buffer.from('\0'.repeat(4096)), // The actual section data
+ ]);
+
+ // Now repeatedly send useless custom sections. These have no use for the
+ // WebAssembly compiler but they are syntactically valid. The client has to
+ // keep reading the stream until the very end to obtain the relevant
+ // sections within the module. This adds up to a few hundred kibibytes.
+ (function next(i) {
+ if (i < 100) {
+ while (res.write(customSection));
+ res.once('drain', () => next(i + 1));
+ } else {
+ // End the response body with the actual module contents.
+ res.end(simpleWasmBytes.slice(8));
+ }
+ })(0);
+ });
+
+ // A valid WebAssembly file with an empty parameter in the (otherwise valid)
+ // MIME type.
+ await testCompileStreamingRejectionUsingFetch((res) => {
+ res.setHeader('Content-Type', 'application/wasm;');
+ res.end(simpleWasmBytes);
+ }, {
+ name: 'TypeError',
+ code: 'ERR_WEBASSEMBLY_RESPONSE',
+ message: 'WebAssembly response has unsupported MIME type ' +
+ "'application/wasm;'"
+ });
+
+ // A valid WebAssembly file with an invalid MIME type.
+ await testCompileStreamingRejectionUsingFetch((res) => {
+ res.setHeader('Content-Type', 'application/octet-stream');
+ res.end(simpleWasmBytes);
+ }, {
+ name: 'TypeError',
+ code: 'ERR_WEBASSEMBLY_RESPONSE',
+ message: 'WebAssembly response has unsupported MIME type ' +
+ "'application/octet-stream'"
+ });
+
+ // HTTP status code indiciating an error.
+ await testCompileStreamingRejectionUsingFetch((res) => {
+ res.statusCode = 418;
+ res.setHeader('Content-Type', 'application/wasm');
+ res.end(simpleWasmBytes);
+ }, {
+ name: 'TypeError',
+ code: 'ERR_WEBASSEMBLY_RESPONSE',
+ message: /^WebAssembly response has status code 418$/
+ });
+
+ // HTTP status code indiciating an error, but using a Response whose body is
+ // a Buffer instead of calling fetch().
+ await testCompileStreamingSuccess(() => {
+ return Promise.resolve(new Response(simpleWasmBytes, {
+ status: 200,
+ headers: { 'Content-Type': 'application/wasm' }
+ }));
+ });
+
+ // Extra bytes after the WebAssembly file.
+ await testCompileStreamingRejectionUsingFetch((res) => {
+ res.setHeader('Content-Type', 'application/wasm');
+ res.end(Buffer.concat([simpleWasmBytes, Buffer.from('foo')]));
+ }, {
+ name: 'CompileError',
+ message: /^WebAssembly\.compileStreaming\(\): .*$/
+ });
+
+ // Missing bytes at the end of the WebAssembly file.
+ await testCompileStreamingRejectionUsingFetch((res) => {
+ res.setHeader('Content-Type', 'application/wasm');
+ res.end(simpleWasmBytes.subarray(0, simpleWasmBytes.length - 3));
+ }, {
+ name: 'CompileError',
+ message: /^WebAssembly\.compileStreaming\(\): .*$/
+ });
+
+ // Incomplete HTTP response body. The TypeError might come as a surprise, but
+ // it originates from within fetch().
+ await testCompileStreamingRejectionUsingFetch((res) => {
+ res.setHeader('Content-Length', simpleWasmBytes.length);
+ res.setHeader('Content-Type', 'application/wasm');
+ res.write(simpleWasmBytes.slice(0, 5), common.mustSucceed(() => {
+ res.destroy();
+ }));
+ }, {
+ name: 'TypeError',
+ message: /terminated/
+ });
+})().then(common.mustCall());
diff --git a/test/wpt/status/wasm/webapi.json b/test/wpt/status/wasm/webapi.json
new file mode 100644
index 00000000000..e631f5c0a95
--- /dev/null
+++ b/test/wpt/status/wasm/webapi.json
@@ -0,0 +1,24 @@
+{
+ "historical.any.js": {
+ "skip": "indexedDB is not defined"
+ },
+ "origin.sub.any.js": {
+ "skip": "CORS not implemented"
+ },
+
+ "abort.any.js": {
+ "skip": "WPTRunner does not support fetch()"
+ },
+ "contenttype.any.js": {
+ "skip": "WPTRunner does not support fetch()"
+ },
+ "empty-body.any.js": {
+ "skip": "Bug in undici, see https://github.com/nodejs/undici/issues/1345"
+ },
+ "idlharness.any.js": {
+ "skip": "not configured"
+ },
+ "status.any.js": {
+ "skip": "WPTRunner does not support fetch()"
+ }
+}
diff --git a/test/wpt/test-wasm-webapi.js b/test/wpt/test-wasm-webapi.js
new file mode 100644
index 00000000000..fecc6e89a3f
--- /dev/null
+++ b/test/wpt/test-wasm-webapi.js
@@ -0,0 +1,7 @@
+'use strict';
+
+require('../common');
+const { WPTRunner } = require('../common/wpt');
+
+const runner = new WPTRunner('wasm/webapi');
+runner.runJsTests();