summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/wasm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/wasm
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/wasm')
-rw-r--r--testing/web-platform/tests/wasm/META.yml3
-rw-r--r--testing/web-platform/tests/wasm/compile_worker.js11
-rw-r--r--testing/web-platform/tests/wasm/create_multiple_memory.worker.js13
-rw-r--r--testing/web-platform/tests/wasm/incrementer.wasmbin0 -> 46 bytes
-rw-r--r--testing/web-platform/tests/wasm/jsapi/META.yml1
-rw-r--r--testing/web-platform/tests/wasm/jsapi/assertions.js105
-rw-r--r--testing/web-platform/tests/wasm/jsapi/bad-imports.js186
-rw-r--r--testing/web-platform/tests/wasm/jsapi/constructor/compile.any.js85
-rw-r--r--testing/web-platform/tests/wasm/jsapi/constructor/instantiate-bad-imports.any.js22
-rw-r--r--testing/web-platform/tests/wasm/jsapi/constructor/instantiate.any.js152
-rw-r--r--testing/web-platform/tests/wasm/jsapi/constructor/multi-value.any.js149
-rw-r--r--testing/web-platform/tests/wasm/jsapi/constructor/toStringTag.any.js42
-rw-r--r--testing/web-platform/tests/wasm/jsapi/constructor/validate.any.js99
-rw-r--r--testing/web-platform/tests/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js13
-rw-r--r--testing/web-platform/tests/wasm/jsapi/exception/basic.tentative.any.js120
-rw-r--r--testing/web-platform/tests/wasm/jsapi/exception/constructor.tentative.any.js62
-rw-r--r--testing/web-platform/tests/wasm/jsapi/exception/getArg.tentative.any.js54
-rw-r--r--testing/web-platform/tests/wasm/jsapi/exception/identity.tentative.any.js61
-rw-r--r--testing/web-platform/tests/wasm/jsapi/exception/is.tentative.any.js25
-rw-r--r--testing/web-platform/tests/wasm/jsapi/exception/toString.tentative.any.js21
-rw-r--r--testing/web-platform/tests/wasm/jsapi/function/call.tentative.any.js16
-rw-r--r--testing/web-platform/tests/wasm/jsapi/function/constructor.tentative.any.js65
-rw-r--r--testing/web-platform/tests/wasm/jsapi/function/table.tentative.any.js30
-rw-r--r--testing/web-platform/tests/wasm/jsapi/function/type.tentative.any.js28
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/entry-different-function-realm.html45
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/entry.html43
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/helper.js12
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/incumbent.html54
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/README.md5
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/current/current.html4
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/current/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/entry-incumbent.html15
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/function/function.html3
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/function/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/incumbent-incumbent.html24
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/relevant/relevant.html14
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/wasm/jsapi/functions/resources/window-to-open.html3
-rw-r--r--testing/web-platform/tests/wasm/jsapi/gc/casts.tentative.any.js332
-rw-r--r--testing/web-platform/tests/wasm/jsapi/gc/exported-object.tentative.any.js190
-rw-r--r--testing/web-platform/tests/wasm/jsapi/gc/i31.tentative.any.js98
-rw-r--r--testing/web-platform/tests/wasm/jsapi/global/constructor.any.js171
-rw-r--r--testing/web-platform/tests/wasm/jsapi/global/toString.any.js17
-rw-r--r--testing/web-platform/tests/wasm/jsapi/global/type.tentative.any.js65
-rw-r--r--testing/web-platform/tests/wasm/jsapi/global/value-get-set.any.js152
-rw-r--r--testing/web-platform/tests/wasm/jsapi/global/valueOf.any.js28
-rw-r--r--testing/web-platform/tests/wasm/jsapi/idlharness.any.js22
-rw-r--r--testing/web-platform/tests/wasm/jsapi/instance/constructor-bad-imports.any.js13
-rw-r--r--testing/web-platform/tests/wasm/jsapi/instance/constructor-caching.any.js54
-rw-r--r--testing/web-platform/tests/wasm/jsapi/instance/constructor.any.js54
-rw-r--r--testing/web-platform/tests/wasm/jsapi/instance/exports.any.js66
-rw-r--r--testing/web-platform/tests/wasm/jsapi/instance/toString.any.js19
-rw-r--r--testing/web-platform/tests/wasm/jsapi/instanceTestFactory.js763
-rw-r--r--testing/web-platform/tests/wasm/jsapi/interface.any.js160
-rw-r--r--testing/web-platform/tests/wasm/jsapi/memory/assertions.js38
-rw-r--r--testing/web-platform/tests/wasm/jsapi/memory/buffer.any.js64
-rw-r--r--testing/web-platform/tests/wasm/jsapi/memory/constructor-shared.tentative.any.js54
-rw-r--r--testing/web-platform/tests/wasm/jsapi/memory/constructor-types.tentative.any.js20
-rw-r--r--testing/web-platform/tests/wasm/jsapi/memory/constructor.any.js139
-rw-r--r--testing/web-platform/tests/wasm/jsapi/memory/grow.any.js189
-rw-r--r--testing/web-platform/tests/wasm/jsapi/memory/toString.any.js17
-rw-r--r--testing/web-platform/tests/wasm/jsapi/memory/type.tentative.any.js37
-rw-r--r--testing/web-platform/tests/wasm/jsapi/module/constructor.any.js69
-rw-r--r--testing/web-platform/tests/wasm/jsapi/module/customSections.any.js140
-rw-r--r--testing/web-platform/tests/wasm/jsapi/module/exports.any.js185
-rw-r--r--testing/web-platform/tests/wasm/jsapi/module/imports.any.js185
-rw-r--r--testing/web-platform/tests/wasm/jsapi/module/moduleSource.tentative.any.js41
-rw-r--r--testing/web-platform/tests/wasm/jsapi/module/toString.any.js18
-rw-r--r--testing/web-platform/tests/wasm/jsapi/proto-from-ctor-realm.html95
-rw-r--r--testing/web-platform/tests/wasm/jsapi/prototypes.any.js43
-rw-r--r--testing/web-platform/tests/wasm/jsapi/table/assertions.js24
-rw-r--r--testing/web-platform/tests/wasm/jsapi/table/constructor-types.tentative.any.js20
-rw-r--r--testing/web-platform/tests/wasm/jsapi/table/constructor.any.js208
-rw-r--r--testing/web-platform/tests/wasm/jsapi/table/get-set.any.js263
-rw-r--r--testing/web-platform/tests/wasm/jsapi/table/grow.any.js126
-rw-r--r--testing/web-platform/tests/wasm/jsapi/table/length.any.js60
-rw-r--r--testing/web-platform/tests/wasm/jsapi/table/toString.any.js17
-rw-r--r--testing/web-platform/tests/wasm/jsapi/table/type.tentative.any.js26
-rw-r--r--testing/web-platform/tests/wasm/jsapi/tag/constructor.tentative.any.js49
-rw-r--r--testing/web-platform/tests/wasm/jsapi/tag/toString.tentative.any.js20
-rw-r--r--testing/web-platform/tests/wasm/jsapi/tag/type.tentative.any.js21
-rw-r--r--testing/web-platform/tests/wasm/jsapi/wasm-module-builder.js1531
-rw-r--r--testing/web-platform/tests/wasm/resources/load_wasm.js12
-rw-r--r--testing/web-platform/tests/wasm/serialization/arraybuffer/transfer.window.js11
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/broadcastchannel-success-and-failure.html38
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/broadcastchannel-success.html59
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/cross-origin-module-sharing-fails.html38
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/identity-not-preserved.html66
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/incrementer.wasmbin0 -> 46 bytes
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/nested-worker-success.any.js9
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/no-transferring.html31
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/blank.html2
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-iframe.html20
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-sharedworker.js7
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-worker.js9
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/create-empty-wasm-module.js4
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/echo-iframe.html11
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/echo-worker.js5
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe-domain.sub.html12
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe-failure.html5
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe.html11
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/incrementer-popup.html11
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/incrementer-worker-with-channel.js8
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/incrementer-worker.js5
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/incrementer.wasmbin0 -> 46 bytes
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-1.html5
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-2.html5
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-3.html5
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-4-incrementer.html11
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/serviceworker-failure.js35
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/sharedworker-failure.js21
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/resources/test-incrementer.js57
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/serialization-via-history.html34
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/serialization-via-idb.any.js45
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/serialization-via-notifications-api.any.js28
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/share-module-cross-origin-fails.sub.html31
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/window-domain-success.sub.html26
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/window-messagechannel-success.html21
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/window-serviceworker-failure.https.html56
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/window-sharedworker-failure.html33
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/window-similar-but-cross-origin-success.sub.html25
-rw-r--r--testing/web-platform/tests/wasm/serialization/module/window-simple-success.html57
-rw-r--r--testing/web-platform/tests/wasm/webapi/META.yml1
-rw-r--r--testing/web-platform/tests/wasm/webapi/abort.any.js37
-rw-r--r--testing/web-platform/tests/wasm/webapi/body.any.js19
-rw-r--r--testing/web-platform/tests/wasm/webapi/contenttype.any.js64
-rw-r--r--testing/web-platform/tests/wasm/webapi/empty-body.any.js20
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/execute-start.tentative.html23
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/exported-names.tentative.html17
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/invalid-bytecode.tentative.html24
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html38
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html11
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/module-parse-error.tentative.html24
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resolve-export.js1
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resolve-export.tentative.html25
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/execute-start.wasmbin0 -> 51 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/exported-names.wasmbin0 -> 73 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/invalid-bytecode.wasmbin0 -> 14 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/invalid-module.wasmbin0 -> 27 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js2
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasmbin0 -> 75 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js2
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasmbin0 -> 68 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js2
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasmbin0 -> 67 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js2
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasmbin0 -> 67 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js2
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasmbin0 -> 66 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle.js13
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasmbin0 -> 101 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/log.js1
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/resolve-export.wasmbin0 -> 8 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasmbin0 -> 23 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasmbin0 -> 45 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasmbin0 -> 46 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasmbin0 -> 75 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-func.js1
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-func.wasmbin0 -> 45 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-global.js1
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-global.wasmbin0 -> 40 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-memory.js1
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-memory.wasmbin0 -> 41 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-table.js1
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-table.wasmbin0 -> 40 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-js-cycle.js15
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasmbin0 -> 294 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-helper.js1
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js3
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker.js2
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker.wasmbin0 -> 62 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/source-phase.tentative.html35
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html14
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/wasm-import.tentative.html34
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html32
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html26
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/worker-import.tentative.html13
-rw-r--r--testing/web-platform/tests/wasm/webapi/esm-integration/worker.tentative.html16
-rw-r--r--testing/web-platform/tests/wasm/webapi/historical.any.js29
-rw-r--r--testing/web-platform/tests/wasm/webapi/idlharness.any.js10
-rw-r--r--testing/web-platform/tests/wasm/webapi/instantiateStreaming-bad-imports.any.js13
-rw-r--r--testing/web-platform/tests/wasm/webapi/instantiateStreaming.any.js49
-rw-r--r--testing/web-platform/tests/wasm/webapi/invalid-args.any.js28
-rw-r--r--testing/web-platform/tests/wasm/webapi/invalid-code.any.js21
-rw-r--r--testing/web-platform/tests/wasm/webapi/modified-contenttype.any.js24
-rw-r--r--testing/web-platform/tests/wasm/webapi/origin.sub.any.js15
-rw-r--r--testing/web-platform/tests/wasm/webapi/rejected-arg.any.js9
-rw-r--r--testing/web-platform/tests/wasm/webapi/resources/incrementer.no_mime_type.wasmbin0 -> 46 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/resources/incrementer.wasmbin0 -> 46 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/resources/incrementer.wasm.headers2
-rw-r--r--testing/web-platform/tests/wasm/webapi/resources/incrementer.wrong_mime_type.wasmbin0 -> 46 bytes
-rw-r--r--testing/web-platform/tests/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers2
-rw-r--r--testing/web-platform/tests/wasm/webapi/status.any.js21
-rw-r--r--testing/web-platform/tests/wasm/webapi/status.py4
-rw-r--r--testing/web-platform/tests/wasm/webapi/wasm_stream_compile_test.html115
-rw-r--r--testing/web-platform/tests/wasm/webapi/wasm_stream_instantiate_test.html115
197 files changed, 9349 insertions, 0 deletions
diff --git a/testing/web-platform/tests/wasm/META.yml b/testing/web-platform/tests/wasm/META.yml
new file mode 100644
index 0000000000..3ea02ee7c9
--- /dev/null
+++ b/testing/web-platform/tests/wasm/META.yml
@@ -0,0 +1,3 @@
+suggested_reviewers:
+ - titzer
+ - Ms2ger
diff --git a/testing/web-platform/tests/wasm/compile_worker.js b/testing/web-platform/tests/wasm/compile_worker.js
new file mode 100644
index 0000000000..9a28efb970
--- /dev/null
+++ b/testing/web-platform/tests/wasm/compile_worker.js
@@ -0,0 +1,11 @@
+// Copyright 2016 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+this.importScripts("resources/load_wasm.js");
+
+onmessage = function(msg) {
+ createWasmModule()
+ .then(postMessage)
+
+}
diff --git a/testing/web-platform/tests/wasm/create_multiple_memory.worker.js b/testing/web-platform/tests/wasm/create_multiple_memory.worker.js
new file mode 100644
index 0000000000..5bb1dcfc8a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/create_multiple_memory.worker.js
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The WebAssembly spec doesn't specify a limit on how many Memory objects can
+// be allocated. This test makes sure we can have at least two at once.
+
+importScripts("/resources/testharness.js");
+test(function () {
+ const mem1 = new WebAssembly.Memory({initial: 1});
+ const mem2 = new WebAssembly.Memory({initial: 1});
+}, "WebAssembly#CreateMultipleMemories.");
+done();
diff --git a/testing/web-platform/tests/wasm/incrementer.wasm b/testing/web-platform/tests/wasm/incrementer.wasm
new file mode 100644
index 0000000000..47afcdef2a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/incrementer.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/jsapi/META.yml b/testing/web-platform/tests/wasm/jsapi/META.yml
new file mode 100644
index 0000000000..cf5525ae11
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/META.yml
@@ -0,0 +1 @@
+spec: https://webassembly.github.io/spec/js-api/
diff --git a/testing/web-platform/tests/wasm/jsapi/assertions.js b/testing/web-platform/tests/wasm/jsapi/assertions.js
new file mode 100644
index 0000000000..3b37019068
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/assertions.js
@@ -0,0 +1,105 @@
+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}`);
+}
+globalThis.assert_function_name = assert_function_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}`);
+}
+globalThis.assert_function_length = assert_function_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);
+}
+globalThis.assert_exported_function = assert_exported_function;
+
+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;
+ }
+ }
+}
+globalThis.assert_Instance = assert_Instance;
+
+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);
+}
+globalThis.assert_WebAssemblyInstantiatedSource = assert_WebAssemblyInstantiatedSource;
diff --git a/testing/web-platform/tests/wasm/jsapi/bad-imports.js b/testing/web-platform/tests/wasm/jsapi/bad-imports.js
new file mode 100644
index 0000000000..c7c9c6b6cd
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/bad-imports.js
@@ -0,0 +1,186 @@
+/**
+ * `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,
+ },
+ });
+ }
+}
+globalThis.test_bad_imports = test_bad_imports;
diff --git a/testing/web-platform/tests/wasm/jsapi/constructor/compile.any.js b/testing/web-platform/tests/wasm/jsapi/constructor/compile.any.js
new file mode 100644
index 0000000000..f822aa30e6
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/constructor/compile.any.js
@@ -0,0 +1,85 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/constructor/instantiate-bad-imports.any.js b/testing/web-platform/tests/wasm/jsapi/constructor/instantiate-bad-imports.any.js
new file mode 100644
index 0000000000..e4926c8d70
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/constructor/instantiate-bad-imports.any.js
@@ -0,0 +1,22 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/constructor/instantiate.any.js b/testing/web-platform/tests/wasm/jsapi/constructor/instantiate.any.js
new file mode 100644
index 0000000000..34e005c470
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/constructor/instantiate.any.js
@@ -0,0 +1,152 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/constructor/multi-value.any.js b/testing/web-platform/tests/wasm/jsapi/constructor/multi-value.any.js
new file mode 100644
index 0000000000..8786f9b953
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/constructor/multi-value.any.js
@@ -0,0 +1,149 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/constructor/toStringTag.any.js b/testing/web-platform/tests/wasm/jsapi/constructor/toStringTag.any.js
new file mode 100644
index 0000000000..5fae8304f8
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/constructor/toStringTag.any.js
@@ -0,0 +1,42 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+
+"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/testing/web-platform/tests/wasm/jsapi/constructor/validate.any.js b/testing/web-platform/tests/wasm/jsapi/constructor/validate.any.js
new file mode 100644
index 0000000000..fce43d1e17
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/constructor/validate.any.js
@@ -0,0 +1,99 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js b/testing/web-platform/tests/wasm/jsapi/error-interfaces-no-symbol-tostringtag.js
new file mode 100644
index 0000000000..572db0c01b
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/exception/basic.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/exception/basic.tentative.any.js
new file mode 100644
index 0000000000..cacce99d9c
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/exception/basic.tentative.any.js
@@ -0,0 +1,120 @@
+// META: global=window,worker,jsshell,shadowrealm
+// 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 kSig_v_r = makeSig([kWasmExternRef], []);
+ const builder = new WasmModuleBuilder();
+ const tagIndex = builder.addTag(kSig_v_r);
+ builder.addFunction("throw_param", kSig_v_r)
+ .addBody([
+ kExprLocalGet, 0,
+ kExprThrow, tagIndex,
+ ])
+ .exportFunc();
+ const buffer = builder.toBuffer();
+ const {instance} = await WebAssembly.instantiate(buffer, {});
+ const values = [
+ undefined,
+ null,
+ true,
+ false,
+ "test",
+ Symbol(),
+ 0,
+ 1,
+ 4.2,
+ NaN,
+ Infinity,
+ {},
+ () => {},
+ ];
+ for (const v of values) {
+ assert_throws_wasm(() => instance.exports.throw_param(v), String(v));
+ }
+}, "Wasm function throws argument");
+
+promise_test(async () => {
+ const builder = new WasmModuleBuilder();
+ const tagIndex = builder.addTag(kSig_v_a);
+ builder.addFunction("throw_null", kSig_v_v)
+ .addBody([
+ kExprRefNull, kAnyFuncCode,
+ kExprThrow, tagIndex,
+ ])
+ .exportFunc();
+ const buffer = builder.toBuffer();
+ const {instance} = await WebAssembly.instantiate(buffer, {});
+ assert_throws_wasm(() => instance.exports.throw_null());
+}, "Wasm function throws null");
+
+promise_test(async () => {
+ const builder = new WasmModuleBuilder();
+ const tagIndex = builder.addTag(kSig_v_i);
+ builder.addFunction("throw_int", kSig_v_v)
+ .addBody([
+ ...wasmI32Const(7),
+ kExprThrow, tagIndex,
+ ])
+ .exportFunc();
+ const buffer = builder.toBuffer();
+ const {instance} = await WebAssembly.instantiate(buffer, {});
+ assert_throws_wasm(() => instance.exports.throw_int());
+}, "Wasm function throws integer");
+
+promise_test(async () => {
+ const builder = new WasmModuleBuilder();
+ const fnIndex = builder.addImport("module", "fn", kSig_v_v);
+ const tagIndex= builder.addTag(kSig_v_r);
+ builder.addFunction("catch_exception", kSig_r_v)
+ .addBody([
+ kExprTry, kWasmStmt,
+ kExprCallFunction, fnIndex,
+ kExprCatch, tagIndex,
+ kExprReturn,
+ kExprEnd,
+ kExprRefNull, kExternRefCode,
+ ])
+ .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, kExternRefCode,
+ ])
+ .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/testing/web-platform/tests/wasm/jsapi/exception/constructor.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/exception/constructor.tentative.any.js
new file mode 100644
index 0000000000..a46d1816c3
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/exception/constructor.tentative.any.js
@@ -0,0 +1,62 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// META: script=/wasm/jsapi/assertions.js
+
+test(() => {
+ assert_function_name(
+ WebAssembly.Exception,
+ "Exception",
+ "WebAssembly.Exception"
+ );
+}, "name");
+
+test(() => {
+ assert_function_length(WebAssembly.Exception, 1, "WebAssembly.Exception");
+}, "length");
+
+test(() => {
+ assert_throws_js(TypeError, () => new WebAssembly.Exception());
+}, "No arguments");
+
+test(() => {
+ const tag = new WebAssembly.Tag({ parameters: [] });
+ assert_throws_js(TypeError, () => WebAssembly.Exception(tag));
+}, "Calling");
+
+test(() => {
+ const invalidArguments = [
+ undefined,
+ null,
+ false,
+ true,
+ "",
+ "test",
+ Symbol(),
+ 1,
+ NaN,
+ {},
+ ];
+ for (const invalidArgument of invalidArguments) {
+ assert_throws_js(
+ TypeError,
+ () => new WebAssembly.Exception(invalidArgument),
+ `new Exception(${format_value(invalidArgument)})`
+ );
+ }
+}, "Invalid descriptor argument");
+
+test(() => {
+ const typesAndArgs = [
+ ["i32", 123n],
+ ["i32", Symbol()],
+ ["f32", 123n],
+ ["f64", 123n],
+ ["i64", undefined],
+ ];
+ for (const typeAndArg of typesAndArgs) {
+ const tag = new WebAssembly.Tag({ parameters: [typeAndArg[0]] });
+ assert_throws_js(
+ TypeError,
+ () => new WebAssembly.Exception(tag, typeAndArg[1])
+ );
+ }
+}, "Invalid exception argument");
diff --git a/testing/web-platform/tests/wasm/jsapi/exception/getArg.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/exception/getArg.tentative.any.js
new file mode 100644
index 0000000000..87719c7ebd
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/exception/getArg.tentative.any.js
@@ -0,0 +1,54 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// META: script=/wasm/jsapi/memory/assertions.js
+
+test(() => {
+ const tag = new WebAssembly.Tag({ parameters: [] });
+ const exn = new WebAssembly.Exception(tag, []);
+ assert_throws_js(TypeError, () => exn.getArg());
+ assert_throws_js(TypeError, () => exn.getArg(tag));
+}, "Missing arguments");
+
+test(() => {
+ const invalidValues = [undefined, null, true, "", Symbol(), 1, {}];
+ const tag = new WebAssembly.Tag({ parameters: [] });
+ const exn = new WebAssembly.Exception(tag, []);
+ for (argument of invalidValues) {
+ assert_throws_js(TypeError, () => exn.getArg(argument, 0));
+ }
+}, "Invalid exception argument");
+
+test(() => {
+ const tag = new WebAssembly.Tag({ parameters: [] });
+ const exn = new WebAssembly.Exception(tag, []);
+ assert_throws_js(RangeError, () => exn.getArg(tag, 1));
+}, "Index out of bounds");
+
+test(() => {
+ const outOfRangeValues = [
+ undefined,
+ NaN,
+ Infinity,
+ -Infinity,
+ -1,
+ 0x100000000,
+ 0x1000000000,
+ "0x100000000",
+ {
+ valueOf() {
+ return 0x100000000;
+ },
+ },
+ ];
+
+ const tag = new WebAssembly.Tag({ parameters: [] });
+ const exn = new WebAssembly.Exception(tag, []);
+ for (const value of outOfRangeValues) {
+ assert_throws_js(RangeError, () => exn.getArg(tag, value));
+ }
+}, "Getting out-of-range argument");
+
+test(() => {
+ const tag = new WebAssembly.Tag({ parameters: ["i32"] });
+ const exn = new WebAssembly.Exception(tag, [42]);
+ assert_equals(exn.getArg(tag, 0), 42);
+}, "getArg");
diff --git a/testing/web-platform/tests/wasm/jsapi/exception/identity.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/exception/identity.tentative.any.js
new file mode 100644
index 0000000000..2675668ec7
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/exception/identity.tentative.any.js
@@ -0,0 +1,61 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// META: script=/wasm/jsapi/assertions.js
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+test(() => {
+ const tag = new WebAssembly.Tag({ parameters: ["i32"] });
+ const exn = new WebAssembly.Exception(tag, [42]);
+ const exn_same_payload = new WebAssembly.Exception(tag, [42]);
+ const exn_diff_payload = new WebAssembly.Exception(tag, [53]);
+
+ const builder = new WasmModuleBuilder();
+ const jsfuncIndex = builder.addImport("module", "jsfunc", kSig_v_v);
+ const tagIndex = builder.addImportedTag("module", "tag", kSig_v_i);
+ const imports = {
+ module: {
+ jsfunc: function() { throw exn; },
+ tag: tag
+ }
+ };
+
+ builder
+ .addFunction("catch_rethrow", kSig_v_v)
+ .addBody([
+ kExprTry, kWasmStmt,
+ kExprCallFunction, jsfuncIndex,
+ kExprCatch, tagIndex,
+ kExprDrop,
+ kExprRethrow, 0x00,
+ kExprEnd
+ ])
+ .exportFunc();
+
+ builder
+ .addFunction("catch_all_rethrow", kSig_v_v)
+ .addBody([
+ kExprTry, kWasmStmt,
+ kExprCallFunction, jsfuncIndex,
+ kExprCatchAll,
+ kExprRethrow, 0x00,
+ kExprEnd
+ ])
+ .exportFunc();
+
+ const buffer = builder.toBuffer();
+ WebAssembly.instantiate(buffer, imports).then(result => {
+ try {
+ result.instance.exports.catch_rethrow();
+ } catch (e) {
+ assert_equals(e, exn);
+ assert_not_equals(e, exn_same_payload);
+ assert_not_equals(e, exn_diff_payload);
+ }
+ try {
+ result.instance.exports.catch_all_rethrow();
+ } catch (e) {
+ assert_equals(e, exn);
+ assert_not_equals(e, exn_same_payload);
+ assert_not_equals(e, exn_diff_payload);
+ }
+ });
+}, "Identity check");
diff --git a/testing/web-platform/tests/wasm/jsapi/exception/is.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/exception/is.tentative.any.js
new file mode 100644
index 0000000000..840d00bf0d
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/exception/is.tentative.any.js
@@ -0,0 +1,25 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/exception/toString.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/exception/toString.tentative.any.js
new file mode 100644
index 0000000000..6885cf0deb
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/exception/toString.tentative.any.js
@@ -0,0 +1,21 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+
+test(() => {
+ const argument = { parameters: [] };
+ const tag = new WebAssembly.Tag(argument);
+ const exn = new WebAssembly.Exception(tag, []);
+ assert_class_string(exn, "WebAssembly.Exception");
+}, "Object.prototype.toString on an Exception");
+
+test(() => {
+ assert_own_property(WebAssembly.Exception.prototype, Symbol.toStringTag);
+
+ const propDesc = Object.getOwnPropertyDescriptor(
+ WebAssembly.Exception.prototype,
+ Symbol.toStringTag
+ );
+ assert_equals(propDesc.value, "WebAssembly.Exception", "value");
+ assert_equals(propDesc.configurable, true, "configurable");
+ assert_equals(propDesc.enumerable, false, "enumerable");
+ assert_equals(propDesc.writable, false, "writable");
+}, "@@toStringTag exists on the prototype with the appropriate descriptor");
diff --git a/testing/web-platform/tests/wasm/jsapi/function/call.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/function/call.tentative.any.js
new file mode 100644
index 0000000000..2e63d5fa10
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/function/call.tentative.any.js
@@ -0,0 +1,16 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/function/constructor.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/function/constructor.tentative.any.js
new file mode 100644
index 0000000000..fc92fcfaf0
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/function/constructor.tentative.any.js
@@ -0,0 +1,65 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/function/table.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/function/table.tentative.any.js
new file mode 100644
index 0000000000..f0dd6ea6f8
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/function/table.tentative.any.js
@@ -0,0 +1,30 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/function/type.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/function/type.tentative.any.js
new file mode 100644
index 0000000000..72a7f1bfbe
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/function/type.tentative.any.js
@@ -0,0 +1,28 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/functions/entry-different-function-realm.html b/testing/web-platform/tests/wasm/jsapi/functions/entry-different-function-realm.html
new file mode 100644
index 0000000000..3af3dd924f
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/entry.html b/testing/web-platform/tests/wasm/jsapi/functions/entry.html
new file mode 100644
index 0000000000..1501807449
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/helper.js b/testing/web-platform/tests/wasm/jsapi/functions/helper.js
new file mode 100644
index 0000000000..487791c69a
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/incumbent.html b/testing/web-platform/tests/wasm/jsapi/functions/incumbent.html
new file mode 100644
index 0000000000..cb27632977
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/README.md b/testing/web-platform/tests/wasm/jsapi/functions/resources/README.md
new file mode 100644
index 0000000000..a89258a4e0
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/current/current.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/current/current.html
new file mode 100644
index 0000000000..63d9c437fc
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/current/resources/window-to-open.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/current/resources/window-to-open.html
new file mode 100644
index 0000000000..1bc4cca9a3
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/entry-incumbent.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/entry-incumbent.html
new file mode 100644
index 0000000000..6b210563e9
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/function/function.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/function/function.html
new file mode 100644
index 0000000000..979b902eaa
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/function/resources/window-to-open.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/function/resources/window-to-open.html
new file mode 100644
index 0000000000..3928c1f8aa
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/incumbent-incumbent.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/incumbent-incumbent.html
new file mode 100644
index 0000000000..5e84f65a08
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/relevant/relevant.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/relevant/relevant.html
new file mode 100644
index 0000000000..06df91c237
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/relevant/resources/window-to-open.html
new file mode 100644
index 0000000000..4138b5a084
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/resources/window-to-open.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/resources/window-to-open.html
new file mode 100644
index 0000000000..7743b9b578
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/functions/resources/window-to-open.html b/testing/web-platform/tests/wasm/jsapi/functions/resources/window-to-open.html
new file mode 100644
index 0000000000..ce357937f5
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/gc/casts.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/gc/casts.tentative.any.js
new file mode 100644
index 0000000000..cce06224fd
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/gc/casts.tentative.any.js
@@ -0,0 +1,332 @@
+// META: global=window,dedicatedworker,jsshell
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+let exports = {};
+setup(() => {
+ const builder = new WasmModuleBuilder();
+ const structIndex = builder.addStruct([makeField(kWasmI32, true)]);
+ const arrayIndex = builder.addArray(kWasmI32, true);
+ const structIndex2 = builder.addStruct([makeField(kWasmF32, true)]);
+ const arrayIndex2 = builder.addArray(kWasmF32, true);
+ const funcIndex = builder.addType({ params: [], results: [] });
+ const funcIndex2 = builder.addType({ params: [], results: [kWasmI32] });
+
+ const argFunctions = [
+ { name: "any", code: kWasmAnyRef },
+ { name: "eq", code: kWasmEqRef },
+ { name: "struct", code: kWasmStructRef },
+ { name: "array", code: kWasmArrayRef },
+ { name: "i31", code: kWasmI31Ref },
+ { name: "func", code: kWasmFuncRef },
+ { name: "extern", code: kWasmExternRef },
+ { name: "none", code: kWasmNullRef },
+ { name: "nofunc", code: kWasmNullFuncRef },
+ { name: "noextern", code: kWasmNullExternRef },
+ { name: "concreteStruct", code: structIndex },
+ { name: "concreteArray", code: arrayIndex },
+ { name: "concreteFunc", code: funcIndex },
+ ];
+
+ for (const desc of argFunctions) {
+ builder
+ .addFunction(desc.name + "Arg", makeSig_v_x(wasmRefType(desc.code)))
+ .addBody([])
+ .exportFunc();
+
+ builder
+ .addFunction(desc.name + "NullableArg", makeSig_v_x(wasmRefNullType(desc.code)))
+ .addBody([])
+ .exportFunc();
+ }
+
+ builder
+ .addFunction("makeStruct", makeSig_r_v(wasmRefType(structIndex)))
+ .addBody([...wasmI32Const(42),
+ ...GCInstr(kExprStructNew), structIndex])
+ .exportFunc();
+
+ builder
+ .addFunction("makeArray", makeSig_r_v(wasmRefType(arrayIndex)))
+ .addBody([...wasmI32Const(5), ...wasmI32Const(42),
+ ...GCInstr(kExprArrayNew), arrayIndex])
+ .exportFunc();
+
+ builder
+ .addFunction("makeStruct2", makeSig_r_v(wasmRefType(structIndex2)))
+ .addBody([...wasmF32Const(42),
+ ...GCInstr(kExprStructNew), structIndex2])
+ .exportFunc();
+
+ builder
+ .addFunction("makeArray2", makeSig_r_v(wasmRefType(arrayIndex2)))
+ .addBody([...wasmF32Const(42), ...wasmI32Const(5),
+ ...GCInstr(kExprArrayNew), arrayIndex2])
+ .exportFunc();
+
+ builder
+ .addFunction("testFunc", funcIndex)
+ .addBody([])
+ .exportFunc();
+
+ builder
+ .addFunction("testFunc2", funcIndex2)
+ .addBody([...wasmI32Const(42)])
+ .exportFunc();
+
+ const buffer = builder.toBuffer();
+ const module = new WebAssembly.Module(buffer);
+ const instance = new WebAssembly.Instance(module, {});
+ exports = instance.exports;
+});
+
+test(() => {
+ exports.anyArg(exports.makeStruct());
+ exports.anyArg(exports.makeArray());
+ exports.anyArg(42);
+ exports.anyArg(42n);
+ exports.anyArg("foo");
+ exports.anyArg({});
+ exports.anyArg(() => {});
+ exports.anyArg(exports.testFunc);
+ assert_throws_js(TypeError, () => exports.anyArg(null));
+
+ exports.anyNullableArg(null);
+ exports.anyNullableArg(exports.makeStruct());
+ exports.anyNullableArg(exports.makeArray());
+ exports.anyNullableArg(42);
+ exports.anyNullableArg(42n);
+ exports.anyNullableArg("foo");
+ exports.anyNullableArg({});
+ exports.anyNullableArg(() => {});
+ exports.anyNullableArg(exports.testFunc);
+}, "anyref casts");
+
+test(() => {
+ exports.eqArg(exports.makeStruct());
+ exports.eqArg(exports.makeArray());
+ exports.eqArg(42);
+ assert_throws_js(TypeError, () => exports.eqArg(42n));
+ assert_throws_js(TypeError, () => exports.eqArg("foo"));
+ assert_throws_js(TypeError, () => exports.eqArg({}));
+ assert_throws_js(TypeError, () => exports.eqArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.eqArg(() => {}));
+ assert_throws_js(TypeError, () => exports.eqArg(null));
+
+ exports.eqNullableArg(null);
+ exports.eqNullableArg(exports.makeStruct());
+ exports.eqNullableArg(exports.makeArray());
+ exports.eqNullableArg(42);
+ assert_throws_js(TypeError, () => exports.eqNullableArg(42n));
+ assert_throws_js(TypeError, () => exports.eqNullableArg("foo"));
+ assert_throws_js(TypeError, () => exports.eqNullableArg({}));
+ assert_throws_js(TypeError, () => exports.eqNullableArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.eqNullableArg(() => {}));
+}, "eqref casts");
+
+test(() => {
+ exports.structArg(exports.makeStruct());
+ assert_throws_js(TypeError, () => exports.structArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.structArg(42));
+ assert_throws_js(TypeError, () => exports.structArg(42n));
+ assert_throws_js(TypeError, () => exports.structArg("foo"));
+ assert_throws_js(TypeError, () => exports.structArg({}));
+ assert_throws_js(TypeError, () => exports.structArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.structArg(() => {}));
+ assert_throws_js(TypeError, () => exports.structArg(null));
+
+ exports.structNullableArg(null);
+ exports.structNullableArg(exports.makeStruct());
+ assert_throws_js(TypeError, () => exports.structNullableArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.structNullableArg(42));
+ assert_throws_js(TypeError, () => exports.structNullableArg(42n));
+ assert_throws_js(TypeError, () => exports.structNullableArg("foo"));
+ assert_throws_js(TypeError, () => exports.structNullableArg({}));
+ assert_throws_js(TypeError, () => exports.structNullableArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.structNullableArg(() => {}));
+}, "structref casts");
+
+test(() => {
+ exports.arrayArg(exports.makeArray());
+ assert_throws_js(TypeError, () => exports.arrayArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.arrayArg(42));
+ assert_throws_js(TypeError, () => exports.arrayArg(42n));
+ assert_throws_js(TypeError, () => exports.arrayArg("foo"));
+ assert_throws_js(TypeError, () => exports.arrayArg({}));
+ assert_throws_js(TypeError, () => exports.arrayArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.arrayArg(() => {}));
+ assert_throws_js(TypeError, () => exports.arrayArg(null));
+
+ exports.arrayNullableArg(null);
+ exports.arrayNullableArg(exports.makeArray());
+ assert_throws_js(TypeError, () => exports.arrayNullableArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.arrayNullableArg(42));
+ assert_throws_js(TypeError, () => exports.arrayNullableArg(42n));
+ assert_throws_js(TypeError, () => exports.arrayNullableArg("foo"));
+ assert_throws_js(TypeError, () => exports.arrayNullableArg({}));
+ assert_throws_js(TypeError, () => exports.arrayNullableArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.arrayNullableArg(() => {}));
+}, "arrayref casts");
+
+test(() => {
+ exports.i31Arg(42);
+ assert_throws_js(TypeError, () => exports.i31Arg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.i31Arg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.i31Arg(42n));
+ assert_throws_js(TypeError, () => exports.i31Arg("foo"));
+ assert_throws_js(TypeError, () => exports.i31Arg({}));
+ assert_throws_js(TypeError, () => exports.i31Arg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.i31Arg(() => {}));
+ assert_throws_js(TypeError, () => exports.i31Arg(null));
+
+ exports.i31NullableArg(null);
+ exports.i31NullableArg(42);
+ assert_throws_js(TypeError, () => exports.i31NullableArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.i31NullableArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.i31NullableArg(42n));
+ assert_throws_js(TypeError, () => exports.i31NullableArg("foo"));
+ assert_throws_js(TypeError, () => exports.i31NullableArg({}));
+ assert_throws_js(TypeError, () => exports.i31NullableArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.i31NullableArg(() => {}));
+}, "i31ref casts");
+
+test(() => {
+ exports.funcArg(exports.testFunc);
+ assert_throws_js(TypeError, () => exports.funcArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.funcArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.funcArg(42));
+ assert_throws_js(TypeError, () => exports.funcArg(42n));
+ assert_throws_js(TypeError, () => exports.funcArg("foo"));
+ assert_throws_js(TypeError, () => exports.funcArg({}));
+ assert_throws_js(TypeError, () => exports.funcArg(() => {}));
+ assert_throws_js(TypeError, () => exports.funcArg(null));
+
+ exports.funcNullableArg(null);
+ exports.funcNullableArg(exports.testFunc);
+ assert_throws_js(TypeError, () => exports.funcNullableArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.funcNullableArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.funcNullableArg(42));
+ assert_throws_js(TypeError, () => exports.funcNullableArg(42n));
+ assert_throws_js(TypeError, () => exports.funcNullableArg("foo"));
+ assert_throws_js(TypeError, () => exports.funcNullableArg({}));
+ assert_throws_js(TypeError, () => exports.funcNullableArg(() => {}));
+}, "funcref casts");
+
+test(() => {
+ exports.externArg(exports.makeArray());
+ exports.externArg(exports.makeStruct());
+ exports.externArg(42);
+ exports.externArg(42n);
+ exports.externArg("foo");
+ exports.externArg({});
+ exports.externArg(exports.testFunc);
+ exports.externArg(() => {});
+ assert_throws_js(TypeError, () => exports.externArg(null));
+
+ exports.externNullableArg(null);
+ exports.externNullableArg(exports.makeArray());
+ exports.externNullableArg(exports.makeStruct());
+ exports.externNullableArg(42);
+ exports.externNullableArg(42n);
+ exports.externNullableArg("foo");
+ exports.externNullableArg({});
+ exports.externNullableArg(exports.testFunc);
+ exports.externNullableArg(() => {});
+}, "externref casts");
+
+test(() => {
+ for (const nullfunc of [exports.noneArg, exports.nofuncArg, exports.noexternArg]) {
+ assert_throws_js(TypeError, () => nullfunc(exports.makeStruct()));
+ assert_throws_js(TypeError, () => nullfunc(exports.makeArray()));
+ assert_throws_js(TypeError, () => nullfunc(42));
+ assert_throws_js(TypeError, () => nullfunc(42n));
+ assert_throws_js(TypeError, () => nullfunc("foo"));
+ assert_throws_js(TypeError, () => nullfunc({}));
+ assert_throws_js(TypeError, () => nullfunc(exports.testFunc));
+ assert_throws_js(TypeError, () => nullfunc(() => {}));
+ assert_throws_js(TypeError, () => nullfunc(null));
+ }
+
+ for (const nullfunc of [exports.noneNullableArg, exports.nofuncNullableArg, exports.noexternNullableArg]) {
+ nullfunc(null);
+ assert_throws_js(TypeError, () => nullfunc(exports.makeStruct()));
+ assert_throws_js(TypeError, () => nullfunc(exports.makeArray()));
+ assert_throws_js(TypeError, () => nullfunc(42));
+ assert_throws_js(TypeError, () => nullfunc(42n));
+ assert_throws_js(TypeError, () => nullfunc("foo"));
+ assert_throws_js(TypeError, () => nullfunc({}));
+ assert_throws_js(TypeError, () => nullfunc(exports.testFunc));
+ assert_throws_js(TypeError, () => nullfunc(() => {}));
+ }
+}, "null casts");
+
+test(() => {
+ exports.concreteStructArg(exports.makeStruct());
+ assert_throws_js(TypeError, () => exports.concreteStructArg(exports.makeStruct2()));
+ assert_throws_js(TypeError, () => exports.concreteStructArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.concreteStructArg(42));
+ assert_throws_js(TypeError, () => exports.concreteStructArg(42n));
+ assert_throws_js(TypeError, () => exports.concreteStructArg("foo"));
+ assert_throws_js(TypeError, () => exports.concreteStructArg({}));
+ assert_throws_js(TypeError, () => exports.concreteStructArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.concreteStructArg(() => {}));
+ assert_throws_js(TypeError, () => exports.concreteStructArg(null));
+
+ exports.concreteStructNullableArg(null);
+ exports.concreteStructNullableArg(exports.makeStruct());
+ assert_throws_js(TypeError, () => exports.concreteStructNullableArg(exports.makeStruct2()));
+ assert_throws_js(TypeError, () => exports.concreteStructNullableArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.concreteStructNullableArg(42));
+ assert_throws_js(TypeError, () => exports.concreteStructNullableArg(42n));
+ assert_throws_js(TypeError, () => exports.concreteStructNullableArg("foo"));
+ assert_throws_js(TypeError, () => exports.concreteStructNullableArg({}));
+ assert_throws_js(TypeError, () => exports.concreteStructNullableArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.concreteStructNullableArg(() => {}));
+}, "concrete struct casts");
+
+test(() => {
+ exports.concreteArrayArg(exports.makeArray());
+ assert_throws_js(TypeError, () => exports.concreteArrayArg(exports.makeArray2()));
+ assert_throws_js(TypeError, () => exports.concreteArrayArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.concreteArrayArg(42));
+ assert_throws_js(TypeError, () => exports.concreteArrayArg(42n));
+ assert_throws_js(TypeError, () => exports.concreteArrayArg("foo"));
+ assert_throws_js(TypeError, () => exports.concreteArrayArg({}));
+ assert_throws_js(TypeError, () => exports.concreteArrayArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.concreteArrayArg(() => {}));
+ assert_throws_js(TypeError, () => exports.concreteArrayArg(null));
+
+ exports.concreteArrayNullableArg(null);
+ exports.concreteArrayNullableArg(exports.makeArray());
+ assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(exports.makeArray2()));
+ assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(42));
+ assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(42n));
+ assert_throws_js(TypeError, () => exports.concreteArrayNullableArg("foo"));
+ assert_throws_js(TypeError, () => exports.concreteArrayNullableArg({}));
+ assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(exports.testFunc));
+ assert_throws_js(TypeError, () => exports.concreteArrayNullableArg(() => {}));
+}, "concrete array casts");
+
+test(() => {
+ exports.concreteFuncArg(exports.testFunc);
+ assert_throws_js(TypeError, () => exports.concreteFuncArg(exports.testFunc2));
+ assert_throws_js(TypeError, () => exports.concreteFuncArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.concreteFuncArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.concreteFuncArg(42));
+ assert_throws_js(TypeError, () => exports.concreteFuncArg(42n));
+ assert_throws_js(TypeError, () => exports.concreteFuncArg("foo"));
+ assert_throws_js(TypeError, () => exports.concreteFuncArg({}));
+ assert_throws_js(TypeError, () => exports.concreteFuncArg(() => {}));
+ assert_throws_js(TypeError, () => exports.concreteFuncArg(null));
+
+ exports.concreteFuncNullableArg(null);
+ exports.concreteFuncNullableArg(exports.testFunc);
+ assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(exports.testFunc2));
+ assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(exports.makeArray()));
+ assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(exports.makeStruct()));
+ assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(42));
+ assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(42n));
+ assert_throws_js(TypeError, () => exports.concreteFuncNullableArg("foo"));
+ assert_throws_js(TypeError, () => exports.concreteFuncNullableArg({}));
+ assert_throws_js(TypeError, () => exports.concreteFuncNullableArg(() => {}));
+}, "concrete func casts");
diff --git a/testing/web-platform/tests/wasm/jsapi/gc/exported-object.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/gc/exported-object.tentative.any.js
new file mode 100644
index 0000000000..b572f14006
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/gc/exported-object.tentative.any.js
@@ -0,0 +1,190 @@
+// META: global=window,dedicatedworker,jsshell
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+let functions = {};
+setup(() => {
+ const builder = new WasmModuleBuilder();
+
+ const structIndex = builder.addStruct([makeField(kWasmI32, true)]);
+ const arrayIndex = builder.addArray(kWasmI32, true);
+ const structRef = wasmRefType(structIndex);
+ const arrayRef = wasmRefType(arrayIndex);
+
+ builder
+ .addFunction("makeStruct", makeSig_r_v(structRef))
+ .addBody([...wasmI32Const(42),
+ ...GCInstr(kExprStructNew), structIndex])
+ .exportFunc();
+
+ builder
+ .addFunction("makeArray", makeSig_r_v(arrayRef))
+ .addBody([...wasmI32Const(5), ...wasmI32Const(42),
+ ...GCInstr(kExprArrayNew), arrayIndex])
+ .exportFunc();
+
+ const buffer = builder.toBuffer();
+ const module = new WebAssembly.Module(buffer);
+ const instance = new WebAssembly.Instance(module, {});
+ functions = instance.exports;
+});
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_equals(struct.foo, undefined);
+ assert_equals(struct[0], undefined);
+ assert_equals(array.foo, undefined);
+ assert_equals(array[0], undefined);
+}, "property access");
+
+test(() => {
+ "use strict";
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => { struct.foo = 5; });
+ assert_throws_js(TypeError, () => { array.foo = 5; });
+ assert_throws_js(TypeError, () => { struct[0] = 5; });
+ assert_throws_js(TypeError, () => { array[0] = 5; });
+}, "property assignment (strict mode)");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => { struct.foo = 5; });
+ assert_throws_js(TypeError, () => { array.foo = 5; });
+ assert_throws_js(TypeError, () => { struct[0] = 5; });
+ assert_throws_js(TypeError, () => { array[0] = 5; });
+}, "property assignment (non-strict mode)");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_equals(Object.getOwnPropertyNames(struct).length, 0);
+ assert_equals(Object.getOwnPropertyNames(array).length, 0);
+}, "ownPropertyNames");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => Object.defineProperty(struct, "foo", { value: 1 }));
+ assert_throws_js(TypeError, () => Object.defineProperty(array, "foo", { value: 1 }));
+}, "defineProperty");
+
+test(() => {
+ "use strict";
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => delete struct.foo);
+ assert_throws_js(TypeError, () => delete struct[0]);
+ assert_throws_js(TypeError, () => delete array.foo);
+ assert_throws_js(TypeError, () => delete array[0]);
+}, "delete (strict mode)");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => delete struct.foo);
+ assert_throws_js(TypeError, () => delete struct[0]);
+ assert_throws_js(TypeError, () => delete array.foo);
+ assert_throws_js(TypeError, () => delete array[0]);
+}, "delete (non-strict mode)");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_equals(Object.getPrototypeOf(struct), null);
+ assert_equals(Object.getPrototypeOf(array), null);
+}, "getPrototypeOf");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => Object.setPrototypeOf(struct, {}));
+ assert_throws_js(TypeError, () => Object.setPrototypeOf(array, {}));
+}, "setPrototypeOf");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_false(Object.isExtensible(struct));
+ assert_false(Object.isExtensible(array));
+}, "isExtensible");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => Object.preventExtensions(struct));
+ assert_throws_js(TypeError, () => Object.preventExtensions(array));
+}, "preventExtensions");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => Object.seal(struct));
+ assert_throws_js(TypeError, () => Object.seal(array));
+}, "sealing");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_equals(typeof struct, "object");
+ assert_equals(typeof array, "object");
+}, "typeof");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => struct.toString());
+ assert_equals(Object.prototype.toString.call(struct), "[object Object]");
+ assert_throws_js(TypeError, () => array.toString());
+ assert_equals(Object.prototype.toString.call(array), "[object Object]");
+}, "toString");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ assert_throws_js(TypeError, () => struct.valueOf());
+ assert_equals(Object.prototype.valueOf.call(struct), struct);
+ assert_throws_js(TypeError, () => array.valueOf());
+ assert_equals(Object.prototype.valueOf.call(array), array);
+}, "valueOf");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ const map = new Map();
+ map.set(struct, "struct");
+ map.set(array, "array");
+ assert_equals(map.get(struct), "struct");
+ assert_equals(map.get(array), "array");
+}, "GC objects as map keys");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ const set = new Set();
+ set.add(struct);
+ set.add(array);
+ assert_true(set.has(struct));
+ assert_true(set.has(array));
+}, "GC objects as set element");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ const map = new WeakMap();
+ map.set(struct, "struct");
+ map.set(array, "array");
+ assert_equals(map.get(struct), "struct");
+ assert_equals(map.get(array), "array");
+}, "GC objects as weak map keys");
+
+test(() => {
+ const struct = functions.makeStruct();
+ const array = functions.makeArray();
+ const set = new WeakSet();
+ set.add(struct);
+ set.add(array);
+ assert_true(set.has(struct));
+ assert_true(set.has(array));
+}, "GC objects as weak set element");
diff --git a/testing/web-platform/tests/wasm/jsapi/gc/i31.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/gc/i31.tentative.any.js
new file mode 100644
index 0000000000..17fd82440c
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/gc/i31.tentative.any.js
@@ -0,0 +1,98 @@
+// META: global=window,dedicatedworker,jsshell
+// META: script=/wasm/jsapi/wasm-module-builder.js
+
+let exports = {};
+setup(() => {
+ const builder = new WasmModuleBuilder();
+ const i31Ref = wasmRefType(kWasmI31Ref);
+ const i31NullableRef = wasmRefNullType(kWasmI31Ref);
+ const anyRef = wasmRefType(kWasmAnyRef);
+
+ builder
+ .addFunction("makeI31", makeSig_r_x(i31Ref, kWasmI32))
+ .addBody([kExprLocalGet, 0,
+ ...GCInstr(kExprI31New)])
+ .exportFunc();
+
+ builder
+ .addFunction("castI31", makeSig_r_x(kWasmI32, anyRef))
+ .addBody([kExprLocalGet, 0,
+ ...GCInstr(kExprRefCast), kI31RefCode,
+ ...GCInstr(kExprI31GetU)])
+ .exportFunc();
+
+ builder
+ .addFunction("getI31", makeSig_r_x(kWasmI32, i31Ref))
+ .addBody([kExprLocalGet, 0,
+ ...GCInstr(kExprI31GetS)])
+ .exportFunc();
+
+ builder
+ .addFunction("argI31", makeSig_v_x(i31NullableRef))
+ .addBody([])
+ .exportFunc();
+
+ builder
+ .addGlobal(i31NullableRef, true, [...wasmI32Const(0), ...GCInstr(kExprI31New)])
+ builder
+ .addExportOfKind("i31Global", kExternalGlobal, 0);
+
+ builder
+ .addTable(i31NullableRef, 10)
+ builder
+ .addExportOfKind("i31Table", kExternalTable, 0);
+
+ const buffer = builder.toBuffer();
+ const module = new WebAssembly.Module(buffer);
+ const instance = new WebAssembly.Instance(module, {});
+ exports = instance.exports;
+});
+
+test(() => {
+ assert_equals(exports.makeI31(42), 42);
+ assert_equals(exports.makeI31(2 ** 30 - 1), 2 ** 30 - 1);
+ assert_equals(exports.makeI31(2 ** 30), -(2 ** 30));
+ assert_equals(exports.makeI31(-(2 ** 30)), -(2 ** 30));
+ assert_equals(exports.makeI31(2 ** 31 - 1), -1);
+ assert_equals(exports.makeI31(2 ** 31), 0);
+}, "i31ref conversion to Number");
+
+test(() => {
+ assert_equals(exports.getI31(exports.makeI31(42)), 42);
+ assert_equals(exports.getI31(42), 42);
+ assert_equals(exports.getI31(2.0 ** 30 - 1), 2 ** 30 - 1);
+ assert_equals(exports.getI31(-(2 ** 30)), -(2 ** 30));
+}, "Number conversion to i31ref");
+
+test(() => {
+ exports.argI31(null);
+ assert_throws_js(TypeError, () => exports.argI31(2 ** 30));
+ assert_throws_js(TypeError, () => exports.argI31(-(2 ** 30) - 1));
+ assert_throws_js(TypeError, () => exports.argI31(2n));
+ assert_throws_js(TypeError, () => exports.argI31(() => 3));
+ assert_throws_js(TypeError, () => exports.argI31(exports.getI31));
+}, "Check i31ref argument type");
+
+test(() => {
+ assert_equals(exports.castI31(42), 42);
+ assert_equals(exports.castI31(2 ** 30 - 1), 2 ** 30 - 1);
+ assert_throws_js(WebAssembly.RuntimeError, () => { exports.castI31(2 ** 30); });
+ assert_throws_js(WebAssembly.RuntimeError, () => { exports.castI31(-(2 ** 30) - 1); });
+ assert_throws_js(WebAssembly.RuntimeError, () => { exports.castI31(2 ** 32); });
+}, "Numbers in i31 range are i31ref, not hostref");
+
+test(() => {
+ assert_equals(exports.i31Global.value, 0);
+ exports.i31Global.value = 42;
+ assert_throws_js(TypeError, () => exports.i31Global.value = 2 ** 30);
+ assert_throws_js(TypeError, () => exports.i31Global.value = -(2 ** 30) - 1);
+ assert_equals(exports.i31Global.value, 42);
+}, "i31ref global");
+
+test(() => {
+ assert_equals(exports.i31Table.get(0), null);
+ exports.i31Table.set(0, 42);
+ assert_throws_js(TypeError, () => exports.i31Table.set(0, 2 ** 30));
+ assert_throws_js(TypeError, () => exports.i31Table.set(0, -(2 ** 30) - 1));
+ assert_equals(exports.i31Table.get(0), 42);
+}, "i31ref table");
diff --git a/testing/web-platform/tests/wasm/jsapi/global/constructor.any.js b/testing/web-platform/tests/wasm/jsapi/global/constructor.any.js
new file mode 100644
index 0000000000..f83f77a5c3
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/constructor.any.js
@@ -0,0 +1,171 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// META: script=/wasm/jsapi/assertions.js
+
+function assert_Global(actual, expected) {
+ assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype,
+ "prototype");
+ assert_true(Object.isExtensible(actual), "extensible");
+
+ assert_equals(actual.value, expected, "value");
+ assert_equals(actual.valueOf(), expected, "valueOf");
+}
+
+test(() => {
+ assert_function_name(WebAssembly.Global, "Global", "WebAssembly.Global");
+}, "name");
+
+test(() => {
+ assert_function_length(WebAssembly.Global, 1, "WebAssembly.Global");
+}, "length");
+
+test(() => {
+ assert_throws_js(TypeError, () => new WebAssembly.Global());
+}, "No arguments");
+
+test(() => {
+ const argument = { "value": "i32" };
+ assert_throws_js(TypeError, () => WebAssembly.Global(argument));
+}, "Calling");
+
+test(() => {
+ const order = [];
+
+ new WebAssembly.Global({
+ get value() {
+ order.push("descriptor value");
+ return {
+ toString() {
+ order.push("descriptor value toString");
+ return "f64";
+ },
+ };
+ },
+
+ get mutable() {
+ order.push("descriptor mutable");
+ return false;
+ },
+ }, {
+ valueOf() {
+ order.push("value valueOf()");
+ }
+ });
+
+ assert_array_equals(order, [
+ "descriptor mutable",
+ "descriptor value",
+ "descriptor value toString",
+ "value valueOf()",
+ ]);
+}, "Order of evaluation");
+
+test(() => {
+ const invalidArguments = [
+ undefined,
+ null,
+ false,
+ true,
+ "",
+ "test",
+ Symbol(),
+ 1,
+ NaN,
+ {},
+ ];
+ for (const invalidArgument of invalidArguments) {
+ assert_throws_js(TypeError,
+ () => new WebAssembly.Global(invalidArgument),
+ `new Global(${format_value(invalidArgument)})`);
+ }
+}, "Invalid descriptor argument");
+
+test(() => {
+ const invalidTypes = ["i16", "i128", "f16", "f128", "u32", "u64", "i32\0"];
+ for (const value of invalidTypes) {
+ const argument = { value };
+ assert_throws_js(TypeError, () => new WebAssembly.Global(argument));
+ }
+}, "Invalid type argument");
+
+test(() => {
+ const argument = { "value": "v128" };
+ assert_throws_js(TypeError, () => new WebAssembly.Global(argument));
+}, "Construct v128 global");
+
+test(() => {
+ const argument = { "value": "i64" };
+ const global = new WebAssembly.Global(argument);
+ assert_Global(global, 0n);
+}, "i64 with default");
+
+for (const type of ["i32", "f32", "f64"]) {
+ test(() => {
+ const argument = { "value": type };
+ const global = new WebAssembly.Global(argument);
+ assert_Global(global, 0);
+ }, `Default value for type ${type}`);
+
+ const valueArguments = [
+ [undefined, 0],
+ [null, 0],
+ [true, 1],
+ [false, 0],
+ [2, 2],
+ ["3", 3],
+ [{ toString() { return "5" } }, 5, "object with toString returning string"],
+ [{ valueOf() { return "8" } }, 8, "object with valueOf returning string"],
+ [{ toString() { return 6 } }, 6, "object with toString returning number"],
+ [{ valueOf() { return 9 } }, 9, "object with valueOf returning number"],
+ ];
+ for (const [value, expected, name = format_value(value)] of valueArguments) {
+ test(() => {
+ const argument = { "value": type };
+ const global = new WebAssembly.Global(argument, value);
+ assert_Global(global, expected);
+ }, `Explicit value ${name} for type ${type}`);
+ }
+
+ test(() => {
+ const argument = { "value": type };
+ assert_throws_js(TypeError, () => new WebAssembly.Global(argument, 0n));
+ }, `BigInt value for type ${type}`);
+}
+
+const valueArguments = [
+ [undefined, 0n],
+ [true, 1n],
+ [false, 0n],
+ ["3", 3n],
+ [123n, 123n],
+ [{ toString() { return "5" } }, 5n, "object with toString returning string"],
+ [{ valueOf() { return "8" } }, 8n, "object with valueOf returning string"],
+ [{ toString() { return 6n } }, 6n, "object with toString returning bigint"],
+ [{ valueOf() { return 9n } }, 9n, "object with valueOf returning bigint"],
+];
+for (const [value, expected, name = format_value(value)] of valueArguments) {
+ test(() => {
+ const argument = { "value": "i64" };
+ const global = new WebAssembly.Global(argument, value);
+ assert_Global(global, expected);
+ }, `Explicit value ${name} for type i64`);
+}
+
+const invalidBigints = [
+ null,
+ 666,
+ { toString() { return 5 } },
+ { valueOf() { return 8 } },
+ Symbol(),
+];
+for (const invalidBigint of invalidBigints) {
+ test(() => {
+ var argument = { "value": "i64" };
+ assert_throws_js(TypeError, () => new WebAssembly.Global(argument, invalidBigint));
+ }, `Pass non-bigint as i64 Global value: ${format_value(invalidBigint)}`);
+}
+
+test(() => {
+ const argument = { "value": "i32" };
+ const global = new WebAssembly.Global(argument, 0, {});
+ assert_Global(global, 0);
+}, "Stray argument");
diff --git a/testing/web-platform/tests/wasm/jsapi/global/toString.any.js b/testing/web-platform/tests/wasm/jsapi/global/toString.any.js
new file mode 100644
index 0000000000..b308498982
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/toString.any.js
@@ -0,0 +1,17 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+
+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/testing/web-platform/tests/wasm/jsapi/global/type.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/global/type.tentative.any.js
new file mode 100644
index 0000000000..78d612529d
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/type.tentative.any.js
@@ -0,0 +1,65 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// META: script=/wasm/jsapi/assertions.js
+
+function assert_type(argument) {
+ const myglobal = new WebAssembly.Global(argument);
+ const globaltype = myglobal.type();
+
+ assert_equals(globaltype.value, argument.value);
+ assert_equals(globaltype.mutable, argument.mutable);
+}
+
+test(() => {
+ assert_type({ "value": "i32", "mutable": true});
+}, "i32, mutable");
+
+test(() => {
+ assert_type({ "value": "i32", "mutable": false});
+}, "i32, immutable");
+
+test(() => {
+ assert_type({ "value": "i64", "mutable": true});
+}, "i64, mutable");
+
+test(() => {
+ assert_type({ "value": "i64", "mutable": false});
+}, "i64, immutable");
+
+test(() => {
+ assert_type({ "value": "f32", "mutable": true});
+}, "f32, mutable");
+
+test(() => {
+ assert_type({ "value": "f32", "mutable": false});
+}, "f32, immutable");
+
+test(() => {
+ assert_type({ "value": "f64", "mutable": true});
+}, "f64, mutable");
+
+test(() => {
+ assert_type({ "value": "f64", "mutable": false});
+}, "f64, immutable");
+
+test(() => {
+ assert_type({"value": "externref", "mutable": true})
+}, "externref, mutable")
+
+test(() => {
+ assert_type({"value": "externref", "mutable": false})
+}, "externref, immutable")
+
+test(() => {
+ assert_type({"value": "funcref", "mutable": true})
+}, "funcref, mutable")
+
+test(() => {
+ assert_type({"value": "funcref", "mutable": false})
+}, "funcref, immutable")
+
+test(() => {
+ const myglobal = new WebAssembly.Global({"value": "i32", "mutable": true});
+ const propertyNames = Object.getOwnPropertyNames(myglobal.type());
+ assert_equals(propertyNames[0], "mutable");
+ assert_equals(propertyNames[1], "value");
+}, "key ordering");
diff --git a/testing/web-platform/tests/wasm/jsapi/global/value-get-set.any.js b/testing/web-platform/tests/wasm/jsapi/global/value-get-set.any.js
new file mode 100644
index 0000000000..bee5581f41
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/value-get-set.any.js
@@ -0,0 +1,152 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+
+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/testing/web-platform/tests/wasm/jsapi/global/valueOf.any.js b/testing/web-platform/tests/wasm/jsapi/global/valueOf.any.js
new file mode 100644
index 0000000000..5bcb171825
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/global/valueOf.any.js
@@ -0,0 +1,28 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+
+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/testing/web-platform/tests/wasm/jsapi/idlharness.any.js b/testing/web-platform/tests/wasm/jsapi/idlharness.any.js
new file mode 100644
index 0000000000..98713d4bf6
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/instance/constructor-bad-imports.any.js b/testing/web-platform/tests/wasm/jsapi/instance/constructor-bad-imports.any.js
new file mode 100644
index 0000000000..1ef4f8423d
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/instance/constructor-bad-imports.any.js
@@ -0,0 +1,13 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/instance/constructor-caching.any.js b/testing/web-platform/tests/wasm/jsapi/instance/constructor-caching.any.js
new file mode 100644
index 0000000000..f969364d93
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/instance/constructor-caching.any.js
@@ -0,0 +1,54 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/instance/constructor.any.js b/testing/web-platform/tests/wasm/jsapi/instance/constructor.any.js
new file mode 100644
index 0000000000..24bf97356c
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/instance/constructor.any.js
@@ -0,0 +1,54 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/instance/exports.any.js b/testing/web-platform/tests/wasm/jsapi/instance/exports.any.js
new file mode 100644
index 0000000000..f7244923d8
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/instance/exports.any.js
@@ -0,0 +1,66 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/instance/toString.any.js b/testing/web-platform/tests/wasm/jsapi/instance/toString.any.js
new file mode 100644
index 0000000000..d77037d65b
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/instance/toString.any.js
@@ -0,0 +1,19 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/instanceTestFactory.js b/testing/web-platform/tests/wasm/jsapi/instanceTestFactory.js
new file mode 100644
index 0000000000..2e015af819
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/instanceTestFactory.js
@@ -0,0 +1,763 @@
+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 = wasmI32Const(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 = wasmI32Const(7);
+ builder.addGlobal(kWasmF64, true)
+ .exportAs("global2")
+ .init = wasmF64Const(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: () => {}
+ };
+ }
+ ],
+];
+
+globalThis.instanceTestFactory = instanceTestFactory;
diff --git a/testing/web-platform/tests/wasm/jsapi/interface.any.js b/testing/web-platform/tests/wasm/jsapi/interface.any.js
new file mode 100644
index 0000000000..8256fc209a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/interface.any.js
@@ -0,0 +1,160 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/memory/assertions.js b/testing/web-platform/tests/wasm/jsapi/memory/assertions.js
new file mode 100644
index 0000000000..b539513adc
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/memory/buffer.any.js b/testing/web-platform/tests/wasm/jsapi/memory/buffer.any.js
new file mode 100644
index 0000000000..fb1d1200b8
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/memory/constructor-shared.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/memory/constructor-shared.tentative.any.js
new file mode 100644
index 0000000000..0134b30774
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/memory/constructor-shared.tentative.any.js
@@ -0,0 +1,54 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/memory/constructor-types.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/memory/constructor-types.tentative.any.js
new file mode 100644
index 0000000000..4653c6686a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/memory/constructor-types.tentative.any.js
@@ -0,0 +1,20 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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");
diff --git a/testing/web-platform/tests/wasm/jsapi/memory/constructor.any.js b/testing/web-platform/tests/wasm/jsapi/memory/constructor.any.js
new file mode 100644
index 0000000000..8f413c9f48
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/memory/constructor.any.js
@@ -0,0 +1,139 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/memory/grow.any.js b/testing/web-platform/tests/wasm/jsapi/memory/grow.any.js
new file mode 100644
index 0000000000..2eafe5761e
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/memory/grow.any.js
@@ -0,0 +1,189 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/memory/toString.any.js b/testing/web-platform/tests/wasm/jsapi/memory/toString.any.js
new file mode 100644
index 0000000000..f4059f7657
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/memory/type.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/memory/type.tentative.any.js
new file mode 100644
index 0000000000..3f6531f596
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/memory/type.tentative.any.js
@@ -0,0 +1,37 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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");
diff --git a/testing/web-platform/tests/wasm/jsapi/module/constructor.any.js b/testing/web-platform/tests/wasm/jsapi/module/constructor.any.js
new file mode 100644
index 0000000000..95604aabe4
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/module/constructor.any.js
@@ -0,0 +1,69 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/module/customSections.any.js b/testing/web-platform/tests/wasm/jsapi/module/customSections.any.js
new file mode 100644
index 0000000000..96958316e0
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/module/customSections.any.js
@@ -0,0 +1,140 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/module/exports.any.js b/testing/web-platform/tests/wasm/jsapi/module/exports.any.js
new file mode 100644
index 0000000000..0c32e984a2
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/module/exports.any.js
@@ -0,0 +1,185 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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 = wasmI32Const(7);
+ builder.addGlobal(kWasmF64, true)
+ .exportAs("global2")
+ .init = wasmF64Const(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 = wasmI32Const(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/testing/web-platform/tests/wasm/jsapi/module/imports.any.js b/testing/web-platform/tests/wasm/jsapi/module/imports.any.js
new file mode 100644
index 0000000000..2ab1725359
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/module/imports.any.js
@@ -0,0 +1,185 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/module/moduleSource.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/module/moduleSource.tentative.any.js
new file mode 100644
index 0000000000..a3d09d55d6
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/module/moduleSource.tentative.any.js
@@ -0,0 +1,41 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// META: script=/wasm/jsapi/wasm-module-builder.js
+// META: script=/wasm/jsapi/assertions.js
+
+let emptyModuleBinary;
+setup(() => {
+ emptyModuleBinary = new WasmModuleBuilder().toBuffer();
+});
+
+test(() => {
+ assert_equals(typeof AbstractModuleSource, "undefined");
+ const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module);
+ assert_equals(AbstractModuleSource.name, "AbstractModuleSource");
+ assert_not_equals(AbstractModuleSource, Function);
+}, "AbstractModuleSource intrinsic");
+
+test(() => {
+ const AbstractModuleSourceProto = Object.getPrototypeOf(WebAssembly.Module.prototype);
+ assert_not_equals(AbstractModuleSourceProto, Object);
+ const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module);
+ assert_equals(AbstractModuleSource.prototype, AbstractModuleSourceProto);
+}, "AbstractModuleSourceProto intrinsic");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+
+ builder
+ .addFunction("fn", kSig_v_v)
+ .addBody([])
+ .exportFunc();
+ builder.addMemory(0, 256, true);
+
+ const buffer = builder.toBuffer()
+ const module = new WebAssembly.Module(buffer);
+
+ const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module);
+ const toStringTag = Object.getOwnPropertyDescriptor(AbstractModuleSource.prototype, Symbol.toStringTag).get;
+
+ assert_equals(toStringTag.call(module), "WebAssembly.Module");
+ assert_throws_js(TypeError, () => toStringTag.call({}));
+}, "AbstractModuleSourceProto toStringTag brand check"); \ No newline at end of file
diff --git a/testing/web-platform/tests/wasm/jsapi/module/toString.any.js b/testing/web-platform/tests/wasm/jsapi/module/toString.any.js
new file mode 100644
index 0000000000..10c707979d
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/module/toString.any.js
@@ -0,0 +1,18 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/proto-from-ctor-realm.html b/testing/web-platform/tests/wasm/jsapi/proto-from-ctor-realm.html
new file mode 100644
index 0000000000..45405b5290
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/prototypes.any.js b/testing/web-platform/tests/wasm/jsapi/prototypes.any.js
new file mode 100644
index 0000000000..2316f7d9b4
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/prototypes.any.js
@@ -0,0 +1,43 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/table/assertions.js b/testing/web-platform/tests/wasm/jsapi/table/assertions.js
new file mode 100644
index 0000000000..19cc5c3b92
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/jsapi/table/constructor-types.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/table/constructor-types.tentative.any.js
new file mode 100644
index 0000000000..b4015bdc6f
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/constructor-types.tentative.any.js
@@ -0,0 +1,20 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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");
diff --git a/testing/web-platform/tests/wasm/jsapi/table/constructor.any.js b/testing/web-platform/tests/wasm/jsapi/table/constructor.any.js
new file mode 100644
index 0000000000..51b1070d41
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/constructor.any.js
@@ -0,0 +1,208 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/table/get-set.any.js b/testing/web-platform/tests/wasm/jsapi/table/get-set.any.js
new file mode 100644
index 0000000000..abe6fecac9
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/get-set.any.js
@@ -0,0 +1,263 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/table/grow.any.js b/testing/web-platform/tests/wasm/jsapi/table/grow.any.js
new file mode 100644
index 0000000000..4038f1e649
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/grow.any.js
@@ -0,0 +1,126 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/table/length.any.js b/testing/web-platform/tests/wasm/jsapi/table/length.any.js
new file mode 100644
index 0000000000..0e6de3f83e
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/length.any.js
@@ -0,0 +1,60 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+
+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/testing/web-platform/tests/wasm/jsapi/table/toString.any.js b/testing/web-platform/tests/wasm/jsapi/table/toString.any.js
new file mode 100644
index 0000000000..b5fb25b9c1
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/toString.any.js
@@ -0,0 +1,17 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+
+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/testing/web-platform/tests/wasm/jsapi/table/type.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/table/type.tentative.any.js
new file mode 100644
index 0000000000..ef9bbc5aa4
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/table/type.tentative.any.js
@@ -0,0 +1,26 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// META: script=/wasm/jsapi/assertions.js
+
+function assert_type(argument) {
+ const mytable = new WebAssembly.Table(argument);
+ const tabletype = mytable.type()
+ assert_equals(tabletype.minimum, argument.minimum);
+ assert_equals(tabletype.maximum, argument.maximum);
+ assert_equals(tabletype.element, argument.element);
+}
+
+test(() => {
+ assert_type({ "minimum": 0, "element": "funcref"});
+}, "Zero initial, no maximum");
+
+test(() => {
+ assert_type({ "minimum": 5, "element": "funcref" });
+}, "Non-zero initial, no maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "maximum": 0, "element": "funcref" });
+}, "Zero maximum");
+
+test(() => {
+ assert_type({ "minimum": 0, "maximum": 5, "element": "funcref" });
+}, "Non-zero maximum");
diff --git a/testing/web-platform/tests/wasm/jsapi/tag/constructor.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/tag/constructor.tentative.any.js
new file mode 100644
index 0000000000..54edf8c8f5
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/tag/constructor.tentative.any.js
@@ -0,0 +1,49 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/tag/toString.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/tag/toString.tentative.any.js
new file mode 100644
index 0000000000..76fff0feef
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/tag/toString.tentative.any.js
@@ -0,0 +1,20 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+
+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/testing/web-platform/tests/wasm/jsapi/tag/type.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/tag/type.tentative.any.js
new file mode 100644
index 0000000000..58c96078bf
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/tag/type.tentative.any.js
@@ -0,0 +1,21 @@
+// META: global=window,dedicatedworker,jsshell,shadowrealm
+// 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/testing/web-platform/tests/wasm/jsapi/wasm-module-builder.js b/testing/web-platform/tests/wasm/jsapi/wasm-module-builder.js
new file mode 100644
index 0000000000..1d8db0a6e6
--- /dev/null
+++ b/testing/web-platform/tests/wasm/jsapi/wasm-module-builder.js
@@ -0,0 +1,1531 @@
+// 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 kTagSectionCode = 13; // Tag section (between Memory & Global)
+
+// Name section types
+let kModuleNameCode = 0;
+let kFunctionNamesCode = 1;
+let kLocalNamesCode = 2;
+
+let kWasmFunctionTypeForm = 0x60;
+let kWasmAnyFunctionTypeForm = 0x70;
+let kWasmStructTypeForm = 0x5f;
+let kWasmArrayTypeForm = 0x5e;
+let kWasmSubtypeForm = 0x50;
+let kWasmSubtypeFinalForm = 0x4f;
+let kWasmRecursiveTypeGroupForm = 0x4e;
+
+let kNoSuperType = 0xFFFFFFFF;
+
+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;
+
+// These are defined as negative integers to distinguish them from positive type
+// indices.
+let kWasmNullFuncRef = -0x0d;
+let kWasmNullExternRef = -0x0e;
+let kWasmNullRef = -0x0f;
+let kWasmFuncRef = -0x10;
+let kWasmAnyFunc = kWasmFuncRef; // Alias named as in the JS API spec
+let kWasmExternRef = -0x11;
+let kWasmAnyRef = -0x12;
+let kWasmEqRef = -0x13;
+let kWasmI31Ref = -0x14;
+let kWasmStructRef = -0x15;
+let kWasmArrayRef = -0x16;
+
+// Use the positive-byte versions inside function bodies.
+let kLeb128Mask = 0x7f;
+let kFuncRefCode = kWasmFuncRef & kLeb128Mask;
+let kAnyFuncCode = kFuncRefCode; // Alias named as in the JS API spec
+let kExternRefCode = kWasmExternRef & kLeb128Mask;
+let kAnyRefCode = kWasmAnyRef & kLeb128Mask;
+let kEqRefCode = kWasmEqRef & kLeb128Mask;
+let kI31RefCode = kWasmI31Ref & kLeb128Mask;
+let kNullExternRefCode = kWasmNullExternRef & kLeb128Mask;
+let kNullFuncRefCode = kWasmNullFuncRef & kLeb128Mask;
+let kStructRefCode = kWasmStructRef & kLeb128Mask;
+let kArrayRefCode = kWasmArrayRef & kLeb128Mask;
+let kNullRefCode = kWasmNullRef & kLeb128Mask;
+
+let kWasmRefNull = 0x63;
+let kWasmRef = 0x64;
+function wasmRefNullType(heap_type) {
+ return {opcode: kWasmRefNull, heap_type: heap_type};
+}
+function wasmRefType(heap_type) {
+ return {opcode: kWasmRef, heap_type: heap_type};
+}
+
+let kExternalFunction = 0;
+let kExternalTable = 1;
+let kExternalMemory = 2;
+let kExternalGlobal = 3;
+let kExternalTag = 4;
+
+let kTableZero = 0;
+let kMemoryZero = 0;
+let kSegmentZero = 0;
+
+let kTagAttribute = 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([kWasmExternRef], [kWasmExternRef]);
+let kSig_a_a = makeSig([kWasmAnyFunc], [kWasmAnyFunc]);
+let kSig_i_r = makeSig([kWasmExternRef], [kWasmI32]);
+let kSig_v_r = makeSig([kWasmExternRef], []);
+let kSig_v_a = makeSig([kWasmAnyFunc], []);
+let kSig_v_rr = makeSig([kWasmExternRef, kWasmExternRef], []);
+let kSig_v_aa = makeSig([kWasmAnyFunc, kWasmAnyFunc], []);
+let kSig_r_v = makeSig([], [kWasmExternRef]);
+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 kGCPrefix = 0xfb;
+let kNumericPrefix = 0xfc;
+let kSimdPrefix = 0xfd;
+let kAtomicPrefix = 0xfe;
+
+// Use these for multi-byte instructions (opcode > 0x7F needing two LEB bytes):
+function GCInstr(opcode) {
+ if (opcode <= 0x7F) return [kGCPrefix, opcode];
+ return [kGCPrefix, 0x80 | (opcode & 0x7F), opcode >> 7];
+}
+
+// GC opcodes
+let kExprStructNew = 0x00;
+let kExprStructNewDefault = 0x01;
+let kExprStructGet = 0x02;
+let kExprStructGetS = 0x03;
+let kExprStructGetU = 0x04;
+let kExprStructSet = 0x05;
+let kExprArrayNew = 0x06;
+let kExprArrayNewDefault = 0x07;
+let kExprArrayNewFixed = 0x08;
+let kExprArrayNewData = 0x09;
+let kExprArrayNewElem = 0x0a;
+let kExprArrayGet = 0x0b;
+let kExprArrayGetS = 0x0c;
+let kExprArrayGetU = 0x0d;
+let kExprArraySet = 0x0e;
+let kExprArrayLen = 0x0f;
+let kExprArrayFill = 0x10;
+let kExprArrayCopy = 0x11;
+let kExprArrayInitData = 0x12;
+let kExprArrayInitElem = 0x13;
+let kExprRefTest = 0x14;
+let kExprRefTestNull = 0x15;
+let kExprRefCast = 0x16;
+let kExprRefCastNull = 0x17;
+let kExprBrOnCast = 0x18;
+let kExprBrOnCastFail = 0x19;
+let kExprExternInternalize = 0x1a;
+let kExprExternExternalize = 0x1b;
+let kExprI31New = 0x1c;
+let kExprI31GetS = 0x1d;
+let kExprI31GetU = 0x1e;
+
+// 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_heap_type(heap_type) {
+ this.emit_bytes(wasmSignedLeb(heap_type, kMaxVarInt32Size));
+ }
+
+ emit_type(type) {
+ if ((typeof type) == 'number') {
+ this.emit_u8(type >= 0 ? type : type & kLeb128Mask);
+ } else {
+ this.emit_u8(type.opcode);
+ if ('depth' in type) this.emit_u8(type.depth);
+ this.emit_heap_type(type.heap_type);
+ }
+ }
+
+ emit_init_expr(expr) {
+ this.emit_bytes(expr);
+ this.emit_u8(kExprEnd);
+ }
+
+ 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, init) {
+ this.module = module;
+ this.type = type;
+ this.mutable = mutable;
+ this.init = init;
+ }
+
+ exportAs(name) {
+ this.module.exports.push({name: name, kind: kExternalGlobal,
+ index: this.index});
+ return this;
+ }
+}
+
+function checkExpr(expr) {
+ for (let b of expr) {
+ if (typeof b !== 'number' || (b & (~0xFF)) !== 0) {
+ throw new Error(
+ 'invalid body (entries must be 8 bit numbers): ' + expr);
+ }
+ }
+}
+
+class WasmTableBuilder {
+ constructor(module, type, initial_size, max_size, init_expr) {
+ this.module = module;
+ this.type = type;
+ this.initial_size = initial_size;
+ this.has_max = max_size != undefined;
+ this.max_size = max_size;
+ this.init_expr = init_expr;
+ this.has_init = init_expr !== undefined;
+ }
+
+ exportAs(name) {
+ this.module.exports.push({name: name, kind: kExternalTable,
+ index: this.index});
+ return this;
+ }
+}
+
+function makeField(type, mutability) {
+ if ((typeof mutability) != 'boolean') {
+ throw new Error('field mutability must be boolean');
+ }
+ return {type: type, mutability: mutability};
+}
+
+class WasmStruct {
+ constructor(fields, is_final, supertype_idx) {
+ if (!Array.isArray(fields)) {
+ throw new Error('struct fields must be an array');
+ }
+ this.fields = fields;
+ this.type_form = kWasmStructTypeForm;
+ this.is_final = is_final;
+ this.supertype = supertype_idx;
+ }
+}
+
+class WasmArray {
+ constructor(type, mutability, is_final, supertype_idx) {
+ this.type = type;
+ this.mutability = mutability;
+ this.type_form = kWasmArrayTypeForm;
+ this.is_final = is_final;
+ this.supertype = supertype_idx;
+ }
+}
+
+class WasmModuleBuilder {
+ constructor() {
+ this.types = [];
+ this.imports = [];
+ this.exports = [];
+ this.globals = [];
+ this.tables = [];
+ this.tags = [];
+ this.functions = [];
+ this.element_segments = [];
+ this.data_segments = [];
+ this.explicit = [];
+ this.rec_groups = [];
+ this.num_imported_funcs = 0;
+ this.num_imported_globals = 0;
+ this.num_imported_tables = 0;
+ this.num_imported_tags = 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));
+ }
+
+ // We use {is_final = true} so that the MVP syntax is generated for
+ // signatures.
+ addType(type, supertype_idx = kNoSuperType, is_final = true) {
+ var pl = type.params.length; // should have params
+ var rl = type.results.length; // should have results
+ var type_copy = {params: type.params, results: type.results,
+ is_final: is_final, supertype: supertype_idx};
+ this.types.push(type_copy);
+ return this.types.length - 1;
+ }
+
+ addStruct(fields, supertype_idx = kNoSuperType, is_final = false) {
+ this.types.push(new WasmStruct(fields, is_final, supertype_idx));
+ return this.types.length - 1;
+ }
+
+ addArray(type, mutability, supertype_idx = kNoSuperType, is_final = false) {
+ this.types.push(new WasmArray(type, mutability, is_final, supertype_idx));
+ return this.types.length - 1;
+ }
+
+ static defaultFor(type) {
+ switch (type) {
+ case kWasmI32:
+ return wasmI32Const(0);
+ case kWasmI64:
+ return wasmI64Const(0);
+ case kWasmF32:
+ return wasmF32Const(0.0);
+ case kWasmF64:
+ return wasmF64Const(0.0);
+ case kWasmS128:
+ return [kSimdPrefix, kExprS128Const, ...(new Array(16).fill(0))];
+ default:
+ if ((typeof type) != 'number' && type.opcode != kWasmRefNull) {
+ throw new Error("Non-defaultable type");
+ }
+ let heap_type = (typeof type) == 'number' ? type : type.heap_type;
+ return [kExprRefNull, ...wasmSignedLeb(heap_type, kMaxVarInt32Size)];
+ }
+ }
+
+ addGlobal(type, mutable, init) {
+ if (init === undefined) init = WasmModuleBuilder.defaultFor(type);
+ checkExpr(init);
+ let glob = new WasmGlobalBuilder(this, type, mutable, init);
+ glob.index = this.globals.length + this.num_imported_globals;
+ this.globals.push(glob);
+ return glob;
+ }
+
+ addTable(type, initial_size, max_size = undefined, init_expr = undefined) {
+ if (type == kWasmI32 || type == kWasmI64 || type == kWasmF32 ||
+ type == kWasmF64 || type == kWasmS128 || type == kWasmStmt) {
+ throw new Error('Tables must be of a reference type');
+ }
+ if (init_expr != undefined) checkExpr(init_expr);
+ let table = new WasmTableBuilder(
+ this, type, initial_size, max_size, init_expr);
+ table.index = this.tables.length + this.num_imported_tables;
+ this.tables.push(table);
+ return table;
+ }
+
+ addTag(type) {
+ let type_index = (typeof type) == "number" ? type : this.addType(type);
+ let tag_index = this.tags.length + this.num_imported_tags;
+ this.tags.push(type_index);
+ return tag_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++;
+ }
+
+ addImportedTag(module, name, type) {
+ if (this.tags.length != 0) {
+ throw new Error('Imported tags must be declared before local ones');
+ }
+ let type_index = (typeof type) == "number" ? type : this.addType(type);
+ let o = {module: module, name: name, kind: kExternalTag, type: type_index};
+ this.imports.push(o);
+ return this.num_imported_tags++;
+ }
+
+ 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;
+ }
+
+ startRecGroup() {
+ this.rec_groups.push({start: this.types.length, size: 0});
+ }
+
+ endRecGroup() {
+ if (this.rec_groups.length == 0) {
+ throw new Error("Did not start a recursive group before ending one")
+ }
+ let last_element = this.rec_groups[this.rec_groups.length - 1]
+ if (last_element.size != 0) {
+ throw new Error("Did not start a recursive group before ending one")
+ }
+ last_element.size = this.types.length - last_element.start;
+ }
+
+ 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 => {
+ let length_with_groups = wasm.types.length;
+ for (let group of wasm.rec_groups) {
+ length_with_groups -= group.size - 1;
+ }
+ section.emit_u32v(length_with_groups);
+
+ let rec_group_index = 0;
+
+ for (let i = 0; i < wasm.types.length; i++) {
+ if (rec_group_index < wasm.rec_groups.length &&
+ wasm.rec_groups[rec_group_index].start == i) {
+ section.emit_u8(kWasmRecursiveTypeGroupForm);
+ section.emit_u32v(wasm.rec_groups[rec_group_index].size);
+ rec_group_index++;
+ }
+
+ let type = wasm.types[i];
+ if (type.supertype != kNoSuperType) {
+ section.emit_u8(type.is_final ? kWasmSubtypeFinalForm
+ : kWasmSubtypeForm);
+ section.emit_u8(1); // supertype count
+ section.emit_u32v(type.supertype);
+ } else if (!type.is_final) {
+ section.emit_u8(kWasmSubtypeForm);
+ section.emit_u8(0); // no supertypes
+ }
+ if (type instanceof WasmStruct) {
+ section.emit_u8(kWasmStructTypeForm);
+ section.emit_u32v(type.fields.length);
+ for (let field of type.fields) {
+ section.emit_type(field.type);
+ section.emit_u8(field.mutability ? 1 : 0);
+ }
+ } else if (type instanceof WasmArray) {
+ section.emit_u8(kWasmArrayTypeForm);
+ section.emit_type(type.type);
+ section.emit_u8(type.mutability ? 1 : 0);
+ } else {
+ section.emit_u8(kWasmFunctionTypeForm);
+ section.emit_u32v(type.params.length);
+ for (let param of type.params) {
+ section.emit_type(param);
+ }
+ section.emit_u32v(type.results.length);
+ for (let result of type.results) {
+ section.emit_type(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_index);
+ } else if (imp.kind == kExternalGlobal) {
+ section.emit_type(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_type(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 == kExternalTag) {
+ section.emit_u32v(kTagAttribute);
+ 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_type(table.type);
+ section.emit_u8(table.has_max);
+ section.emit_u32v(table.initial_size);
+ if (table.has_max) section.emit_u32v(table.max_size);
+ if (table.has_init) section.emit_init_expr(table.init_expr);
+ }
+ });
+ }
+
+ // 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_type(global.type);
+ section.emit_u8(global.mutable);
+ section.emit_init_expr(global.init);
+ }
+ });
+ }
+
+ // Add tags.
+ if (wasm.tags.length > 0) {
+ if (debug) print("emitting tags @ " + binary.length);
+ binary.emit_section(kTagSectionCode, section => {
+ section.emit_u32v(wasm.tags.length);
+ for (let type of wasm.tags) {
+ section.emit_u32v(kTagAttribute);
+ 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: kWasmExternRef});
+ }
+ 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_type(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));
+ }
+}
+globalThis.WasmModuleBuilder = WasmModuleBuilder;
+
+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);
+}
+globalThis.wasmSignedLeb = wasmSignedLeb;
+
+function wasmI32Const(val) {
+ return [kExprI32Const, ...wasmSignedLeb(val, 5)];
+}
+globalThis.wasmI32Const = wasmI32Const;
+
+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]
+ ];
+}
+globalThis.wasmI32Const = wasmI32Const;
+
+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]
+ ];
+}
+globalThis.wasmF64Const = wasmF64Const;
diff --git a/testing/web-platform/tests/wasm/resources/load_wasm.js b/testing/web-platform/tests/wasm/resources/load_wasm.js
new file mode 100644
index 0000000000..7f280ac032
--- /dev/null
+++ b/testing/web-platform/tests/wasm/resources/load_wasm.js
@@ -0,0 +1,12 @@
+// Copyright 2016 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function createWasmModule() {
+ return fetch('/wasm/incrementer.wasm')
+ .then(response => {
+ if (!response.ok) throw new Error(response.statusText);
+ return response.arrayBuffer();
+ })
+ .then(WebAssembly.compile);
+}
diff --git a/testing/web-platform/tests/wasm/serialization/arraybuffer/transfer.window.js b/testing/web-platform/tests/wasm/serialization/arraybuffer/transfer.window.js
new file mode 100644
index 0000000000..0258581b1e
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/arraybuffer/transfer.window.js
@@ -0,0 +1,11 @@
+test(() => {
+ const buffer = new WebAssembly.Memory({initial: 4}).buffer;
+ postMessage(buffer, '*');
+}, "Serializing a WebAssembly.Memory-backed ArrayBuffer works");
+
+test(() => {
+ const buffer = new WebAssembly.Memory({initial: 4}).buffer;
+ assert_throws_js(TypeError, () => {
+ postMessage('foo', '*', [buffer]);
+ });
+}, "Transfering a WebAssembly.Memory-backed ArrayBuffer throws");
diff --git a/testing/web-platform/tests/wasm/serialization/module/broadcastchannel-success-and-failure.html b/testing/web-platform/tests/wasm/serialization/module/broadcastchannel-success-and-failure.html
new file mode 100644
index 0000000000..0d11cc595b
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/broadcastchannel-success-and-failure.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<title>WebAssembly.Module cannot cross agent clusters, BroadcastChannel edition</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/test-incrementer.js"></script>
+<script>
+async_test(t => {
+ const channel = new BroadcastChannel("anne was here"),
+ dw = new Worker("resources/broadcastchannel-worker.js"),
+ sw = new SharedWorker("resources/broadcastchannel-sharedworker.js");
+ let startCounter = 0,
+ dwStatus = "unknown",
+ swStatus = "unknown";
+
+ channel.onmessage = t.step_func(({ data }) => {
+ if(data === "hi") {
+ startCounter++;
+ if(startCounter === 2) {
+ createWasmModule().then(module => {
+ channel.postMessage(module);
+ });
+ } else if(startCounter > 2) {
+ assert_unreached();
+ }
+ } else if(data === "dw-success") {
+ dwStatus = "success";
+ } else if(data === "sw-success") {
+ swStatus = "success";
+ } else {
+ assert_unreached();
+ }
+ if(dwStatus === "success" && swStatus === "success") {
+ t.done();
+ }
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/broadcastchannel-success.html b/testing/web-platform/tests/wasm/serialization/module/broadcastchannel-success.html
new file mode 100644
index 0000000000..ddc242b82e
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/broadcastchannel-success.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<meta charset="utf-8">
+<title>Structured cloning of WebAssembly.Module: BroadcastChannel within the same agent cluster</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/test-incrementer.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ return createWasmModule().then(module => {
+ let loadedIframes = 0;
+ return Promise.all([
+ createIFrame("resources/broadcastchannel-iframe.html"),
+ createIFrame("resources/broadcastchannel-iframe.html"),
+ createIFrame("resources/broadcastchannel-iframe.html")
+ ]).then(() => {
+ const thisIframe = loadedIframes++;
+ const channel = new BroadcastChannel("channel name");
+
+ return new Promise(resolve => {
+ let soFar = 0;
+ channel.onmessage = t.step_func(msg => {
+ if (msg.module) {
+ // We only care about "broadcasts" from the workers.
+ return;
+ }
+
+ let {i, result} = msg.data;
+
+ assert_in_array(i, [0, 1, 2], "Any message events must come from expected sources");
+ assert_equals(result, i + 1, `iframe ${i} must return ${i+1}`);
+ ++soFar;
+
+ if (soFar === 3) {
+ resolve();
+ }
+ });
+
+ channel.postMessage({ module, i: thisIframe });
+ });
+ });
+ });
+});
+
+function createIFrame(src) {
+ return new Promise((resolve, reject) => {
+ const iframe = document.createElement("iframe");
+ iframe.src = src;
+ iframe.onload = () => resolve(iframe);
+ iframe.onerror = () => reject(`iframe with URL ${src} failed to load`);
+ document.body.appendChild(iframe);
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/cross-origin-module-sharing-fails.html b/testing/web-platform/tests/wasm/serialization/module/cross-origin-module-sharing-fails.html
new file mode 100644
index 0000000000..cd3e99b9ec
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/cross-origin-module-sharing-fails.html
@@ -0,0 +1,38 @@
+<title>Postmessage of a WebAssembly.Module cross-origin fails with a messageerror</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="resources/test-incrementer.js"></script>
+
+<body>
+<script>
+async function testPostMessageErrorForOrigin(t, remoteOrigin){
+ const iframe = document.createElement('iframe');
+ iframe.src = `${remoteOrigin}${base_path()}resources/incrementer-iframe-failure.html`;
+ const iframeLoaded = new Promise(resolve => iframe.onload = resolve);
+ document.body.appendChild(iframe);
+ t.add_cleanup(() => {
+ iframe.remove();
+ });
+ await iframeLoaded;
+
+ const module = await createWasmModule();
+ const messageErrorReceived =
+ new Promise(resolve => window.onmessage = resolve);
+ iframe.contentWindow.postMessage({message: 'send module', module}, "*");
+ let reply = await messageErrorReceived;
+ assert_equals('messageerror received', reply.data);
+}
+
+promise_test(async t => {
+ const remoteOrigin = get_host_info().OTHER_ORIGIN;
+ await testPostMessageErrorForOrigin(t, remoteOrigin);
+}, "postMessaging a wasm module to an iframe in a different agent cluster fails");
+
+promise_test(async t => {
+ const remoteOrigin = get_host_info().HTTPS_ORIGIN;
+ await testPostMessageErrorForOrigin(t, remoteOrigin);
+}, "postMessaging a wasm module to a cross-origin iframe in the same agent cluster fails");
+</script>
+</body>
diff --git a/testing/web-platform/tests/wasm/serialization/module/identity-not-preserved.html b/testing/web-platform/tests/wasm/serialization/module/identity-not-preserved.html
new file mode 100644
index 0000000000..24bb3b16d8
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/identity-not-preserved.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<meta charset="utf-8">
+<title>WebAssembly.Modules, when cloned, do not give back the same object</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/test-incrementer.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+async_test(t => {
+ createWasmModule().then(module => {
+ window.addEventListener("message", t.step_func(({ data }) => {
+ if (data.testId !== 1) {
+ return;
+ }
+
+ assert_not_equals(data.module, module);
+
+ t.done();
+ }));
+
+ window.postMessage({ module, testId: 1 }, "*");
+ });
+}, "postMessaging to this window does not give back the same WebAssembly.Module");
+
+async_test(t => {
+ createWasmModule().then(module => {
+ const worker = new Worker("resources/echo-worker.js");
+
+ worker.addEventListener("message", t.step_func(({ data }) => {
+ if (data.testId !== 2) {
+ return;
+ }
+
+ assert_not_equals(data.module, module);
+ t.done();
+ }));
+
+ worker.postMessage({ testId: 2, module });
+ });
+}, "postMessaging to a worker and back does not give back the same WebAssembly.Module");
+
+async_test(t => {
+ createWasmModule().then(module => {
+ window.addEventListener("message", t.step_func(({ data }) => {
+ if (data.testId !== 3) {
+ return;
+ }
+
+ assert_not_equals(data.module, module);
+ t.done();
+ }));
+
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ iframe.contentWindow.postMessage({ testId: 3, module }, "*");
+ });
+ iframe.src = "resources/echo-iframe.html";
+ document.body.appendChild(iframe);
+ });
+}, "postMessaging to an iframe and back does not give back the same WebAssembly.Module");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/incrementer.wasm b/testing/web-platform/tests/wasm/serialization/module/incrementer.wasm
new file mode 100644
index 0000000000..47afcdef2a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/incrementer.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/serialization/module/nested-worker-success.any.js b/testing/web-platform/tests/wasm/serialization/module/nested-worker-success.any.js
new file mode 100644
index 0000000000..84fbd92a29
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/nested-worker-success.any.js
@@ -0,0 +1,9 @@
+// META: global=dedicatedworker,sharedworker
+// META: script=resources/test-incrementer.js
+"use strict";
+
+promise_test(t => {
+ const worker = new Worker("resources/incrementer-worker.js");
+
+ return testSharingViaIncrementerScript(t, worker, "parent worker", worker, "sub-worker");
+}, "postMessaging to a dedicated sub-worker allows them to see each others' modifications");
diff --git a/testing/web-platform/tests/wasm/serialization/module/no-transferring.html b/testing/web-platform/tests/wasm/serialization/module/no-transferring.html
new file mode 100644
index 0000000000..2f0f674b1f
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/no-transferring.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<meta charset="utf-8">
+<title>WebAssembly.Modules cannot be transferred</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/create-empty-wasm-module.js"></script>
+
+<script>
+"use strict";
+
+test(() => {
+ const module = createEmptyWasmModule();
+ assert_throws_dom("DataCloneError", () => window.postMessage(module, "*", [module]));
+ assert_throws_dom("DataCloneError", () => window.postMessage("test", "*", [module]));
+}, "Trying to transfer a WebAssembly.Module to this window throws");
+
+test(() => {
+ const module = createEmptyWasmModule();
+ const worker = new Worker("resources/echo-worker.js");
+ assert_throws_dom("DataCloneError", () => worker.postMessage(module, [module]));
+ assert_throws_dom("DataCloneError", () => worker.postMessage("test", [module]));
+}, "Trying to transfer a WebAssembly.Module to a worker throws");
+
+test(() => {
+ const module = createEmptyWasmModule();
+ const channel = new MessageChannel();
+ assert_throws_dom("DataCloneError", () => channel.port1.postMessage(module, [module]));
+ assert_throws_dom("DataCloneError", () => channel.port1.postMessage("test", [module]));
+}, "Trying to transfer a WebAssembly.Module through a MessagePort throws");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/blank.html b/testing/web-platform/tests/wasm/serialization/module/resources/blank.html
new file mode 100644
index 0000000000..a3c3a4689a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/blank.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<title>Empty doc</title>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-iframe.html b/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-iframe.html
new file mode 100644
index 0000000000..83e347b5cb
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-iframe.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that uses a given WebAssembly.Module sent from a BroadcastChannel</title>
+
+<script>
+"use strict";
+const channel = new BroadcastChannel("channel name");
+
+channel.onmessage = ({ data: { module, i }, source }) => {
+ if (!module) {
+ // We only care about "broadcasts" from the window
+ return;
+ }
+
+ let instance = new WebAssembly.Instance(module);
+ let increment = instance.exports["increment"];
+ let result = increment(i);
+ channel.postMessage({i, result});
+};
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-sharedworker.js b/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-sharedworker.js
new file mode 100644
index 0000000000..310e0e9358
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-sharedworker.js
@@ -0,0 +1,7 @@
+const channel = new BroadcastChannel("anne was here");
+channel.onmessageerror = ({ data }) => {
+ if(data === null) {
+ channel.postMessage("sw-success");
+ }
+}
+channel.postMessage("hi");
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-worker.js b/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-worker.js
new file mode 100644
index 0000000000..76a8177060
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/broadcastchannel-worker.js
@@ -0,0 +1,9 @@
+const channel = new BroadcastChannel("anne was here");
+channel.onmessage = ({ data }) => {
+ if(data === "hi" || data === "sw-success") {
+ return;
+ } else if(data instanceof WebAssembly.Module) {
+ channel.postMessage("dw-success");
+ }
+}
+channel.postMessage("hi");
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/create-empty-wasm-module.js b/testing/web-platform/tests/wasm/serialization/module/resources/create-empty-wasm-module.js
new file mode 100644
index 0000000000..7326710c9e
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/create-empty-wasm-module.js
@@ -0,0 +1,4 @@
+function createEmptyWasmModule() {
+ return new WebAssembly.Module(
+ new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
+}
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/echo-iframe.html b/testing/web-platform/tests/wasm/serialization/module/resources/echo-iframe.html
new file mode 100644
index 0000000000..c4fd5824a1
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/echo-iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that echos back anything postMessaged to it to its parent</title>
+
+<script>
+"use strict";
+
+window.onmessage = ({ data }) => {
+ parent.postMessage(data, "*");
+};
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/echo-worker.js b/testing/web-platform/tests/wasm/serialization/module/resources/echo-worker.js
new file mode 100644
index 0000000000..cbbde8a73c
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/echo-worker.js
@@ -0,0 +1,5 @@
+"use strict";
+
+self.onmessage = ({ data }) => {
+ self.postMessage(data);
+};
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe-domain.sub.html b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe-domain.sub.html
new file mode 100644
index 0000000000..d2d18de499
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe-domain.sub.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that is sent a WebAssembly Module</title>
+<script src="/resources/testharness.js"></script>
+<script src="test-incrementer.js"></script>
+
+<script>
+"use strict";
+
+document.domain = "{{host}}";
+setupDestinationIncrementer(self, parent, "*");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe-failure.html b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe-failure.html
new file mode 100644
index 0000000000..5212a9ec72
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe-failure.html
@@ -0,0 +1,5 @@
+<title>A test page that is sent a WebAssembly Module cross-origin cannot receive it</title>
+
+<script>
+self.onmessageerror = () => parent.postMessage('messageerror received', "*");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe.html b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe.html
new file mode 100644
index 0000000000..5c8bc0735e
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that is sent a WebAssembly Module</title>
+<script src="/resources/testharness.js"></script>
+<script src="test-incrementer.js"></script>
+
+<script>
+"use strict";
+
+setupDestinationIncrementer(self, parent, "*");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-popup.html b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-popup.html
new file mode 100644
index 0000000000..660e472b27
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-popup.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that is sent a WebAssembly Module</title>
+<script src="/resources/testharness.js"></script>
+<script src="test-incrementer.js"></script>
+
+<script>
+"use strict";
+
+setupDestinationIncrementer(self, opener, "*");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-worker-with-channel.js b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-worker-with-channel.js
new file mode 100644
index 0000000000..0323b3e52e
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-worker-with-channel.js
@@ -0,0 +1,8 @@
+"use strict";
+importScripts("/resources/testharness.js");
+importScripts("./test-incrementer.js");
+
+self.onmessage = ({ data }) => {
+ // data will be a MessagePort
+ setupDestinationIncrementer(data, data);
+};
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-worker.js b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-worker.js
new file mode 100644
index 0000000000..1779ceea52
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer-worker.js
@@ -0,0 +1,5 @@
+"use strict";
+importScripts("/resources/testharness.js");
+importScripts("./test-incrementer.js");
+
+setupDestinationIncrementer(self, self);
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/incrementer.wasm b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer.wasm
new file mode 100644
index 0000000000..47afcdef2a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/incrementer.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-1.html b/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-1.html
new file mode 100644
index 0000000000..fe93cc0c4b
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Nesting level 1</title>
+
+<iframe src="nested-iframe-2.html"></iframe>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-2.html b/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-2.html
new file mode 100644
index 0000000000..fad52ce9de
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-2.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Nesting level 2</title>
+
+<iframe src="nested-iframe-3.html"></iframe>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-3.html b/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-3.html
new file mode 100644
index 0000000000..7971022b2c
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-3.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Nesting level 3</title>
+
+<iframe src="nested-iframe-4-incrementer.html"></iframe>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-4-incrementer.html b/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-4-incrementer.html
new file mode 100644
index 0000000000..f419f4bc36
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/nested-iframe-4-incrementer.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>A test page that is sent a WebAssembly Module, nested 4 levels deep in iframes</title>
+<script src="/resources/testharness.js"></script>
+<script src="test-incrementer.js"></script>
+
+<script>
+"use strict";
+
+setupDestinationIncrementer(self, parent.parent.parent.parent.parent, "*");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/serviceworker-failure.js b/testing/web-platform/tests/wasm/serialization/module/resources/serviceworker-failure.js
new file mode 100644
index 0000000000..4ef380c253
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/serviceworker-failure.js
@@ -0,0 +1,35 @@
+// Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/.
+"use strict";
+self.importScripts("/resources/testharness.js");
+self.importScripts("./create-empty-wasm-module.js");
+
+let state = "start in worker";
+
+self.onmessage = e => {
+ if (e.data === "start in window") {
+ assert_equals(state, "start in worker");
+ e.source.postMessage(state);
+ state = "we are expecting a messageerror due to the window sending us a WebAssembly.Module";
+ } else if (e.data === "we are expecting a messageerror due to the worker sending us a WebAssembly.Module") {
+ assert_equals(state, "onmessageerror was received in worker");
+ e.source.postMessage(createEmptyWasmModule());
+ state = "done in worker";
+ } else {
+ e.source.postMessage(`worker onmessage was reached when in state "${state}" and data ${e.data}`);
+ }
+};
+
+self.onmessageerror = e => {
+ if (state === "we are expecting a messageerror due to the window sending us a WebAssembly.Module") {
+ assert_equals(e.constructor.name, "ExtendableMessageEvent", "type");
+ assert_equals(e.data, null, "data");
+ assert_equals(e.origin, self.origin, "origin");
+ assert_not_equals(e.source, null, "source");
+ assert_equals(e.ports.length, 0, "ports length");
+
+ state = "onmessageerror was received in worker";
+ e.source.postMessage(state);
+ } else {
+ e.source.postMessage(`worker onmessageerror was reached when in state "${state}" and data ${e.data}`);
+ }
+};
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/sharedworker-failure.js b/testing/web-platform/tests/wasm/serialization/module/resources/sharedworker-failure.js
new file mode 100644
index 0000000000..854c70b9e8
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/sharedworker-failure.js
@@ -0,0 +1,21 @@
+importScripts("./test-incrementer.js");
+importScripts("./create-empty-wasm-module.js");
+
+let state = "send-sw-failure"
+onconnect = initialE => {
+ let port = initialE.source;
+ port.postMessage(state)
+ port.onmessage = e => {
+ if(state === "" && e.data === "send-window-failure") {
+ port.postMessage(createEmptyWasmModule())
+ } else {
+ port.postMessage("failure")
+ }
+ }
+ port.onmessageerror = e => {
+ if(state === "send-sw-failure") {
+ port.postMessage("send-sw-failure-success")
+ state = ""
+ }
+ }
+}
diff --git a/testing/web-platform/tests/wasm/serialization/module/resources/test-incrementer.js b/testing/web-platform/tests/wasm/serialization/module/resources/test-incrementer.js
new file mode 100644
index 0000000000..65cb33227a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/resources/test-incrementer.js
@@ -0,0 +1,57 @@
+// Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/.
+//
+// This file is simplified from the one there, because it only tests that the
+// module can be passed and that functions can be run. The SharedArrayBuffer
+// version also tests that the memory is shared between the agents.
+
+"use strict";
+
+function createWasmModule() {
+ return fetch('incrementer.wasm')
+ .then(response => {
+ if (!response.ok)
+ throw new Error(response.statusText);
+ return response.arrayBuffer();
+ })
+ .then(WebAssembly.compile);
+}
+
+function testModule(module) {
+ let instance = new WebAssembly.Instance(module);
+ let increment = instance.exports["increment"];
+ assert_equals(typeof increment, "function", `The type of the increment export should be "function", got ${typeof increment}`);
+ let result = increment(42);
+ assert_equals(result, 43, `increment(42) should be 43, got ${result}`);
+}
+
+self.testSharingViaIncrementerScript = (t, whereToListen, whereToListenLabel, whereToSend, whereToSendLabel, origin) => {
+ return createWasmModule().then(module => {
+ return new Promise(resolve => {
+
+ whereToListen.onmessage = t.step_func(({ data }) => {
+ switch (data.message) {
+ case "module received": {
+ testModule(data.module);
+ resolve();
+ break;
+ }
+ }
+ });
+
+ whereToSend.postMessage({ message: "send module", module }, origin);
+ });
+ });
+};
+
+self.setupDestinationIncrementer = (whereToListen, whereToSendBackTo, origin) => {
+ whereToListen.onmessage = ({ data }) => {
+ switch (data.message) {
+ case "send module": {
+ let module = data.module;
+ testModule(data.module);
+ whereToSendBackTo.postMessage({ message: "module received", module }, origin);
+ break;
+ }
+ }
+ };
+};
diff --git a/testing/web-platform/tests/wasm/serialization/module/serialization-via-history.html b/testing/web-platform/tests/wasm/serialization/module/serialization-via-history.html
new file mode 100644
index 0000000000..38d4301d70
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/serialization-via-history.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<meta charset="utf-8">
+<title>WebAssembly.Module cloning via history's methods invoking StructuredSerializeForStorage</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/create-empty-wasm-module.js"></script>
+
+<script>
+"use strict";
+
+for (const method of ["pushState", "replaceState"]) {
+ test(() => {
+ assert_throws_dom("DataCloneError", () => {
+ history[method](createEmptyWasmModule(), "dummy title");
+ });
+ }, `history.${method}(): simple case`);
+
+ test(() => {
+ let getter1Called = false;
+ let getter2Called = false;
+ assert_throws_dom("DataCloneError", () => {
+ history[method]([
+ { get x() { getter1Called = true; return 5; } },
+ createEmptyWasmModule(),
+ { get x() { getter2Called = true; return 5; } }
+ ], "dummy title");
+ });
+
+ assert_true(getter1Called, "The getter before the WebAssembly.Module must have been called");
+ assert_false(getter2Called, "The getter after the WebAssembly.Module must not have been called");
+ }, `history.${method}(): is interleaved correctly`);
+}
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/serialization-via-idb.any.js b/testing/web-platform/tests/wasm/serialization/module/serialization-via-idb.any.js
new file mode 100644
index 0000000000..2591d2fe74
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/serialization-via-idb.any.js
@@ -0,0 +1,45 @@
+// META: script=/IndexedDB/resources/support.js
+"use strict";
+
+function createEmptyWasmModule() {
+ return new WebAssembly.Module(
+ new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
+}
+
+async_test(t => {
+ const openReq = createdb(t);
+
+ openReq.onupgradeneeded = e => {
+ const db = e.target.result;
+ const store = db.createObjectStore("store", { keyPath: "key" });
+
+ assert_throws_dom("DataCloneError", () => {
+ store.put({ key: 1, property: createEmptyWasmModule() });
+ });
+ t.done();
+ };
+}, "WebAssembly.Module cloning via IndexedDB: basic case");
+
+async_test(t => {
+ const openReq = createdb(t);
+
+ openReq.onupgradeneeded = e => {
+ const db = e.target.result;
+ const store = db.createObjectStore("store", { keyPath: "key" });
+
+ let getter1Called = false;
+ let getter2Called = false;
+
+ assert_throws_dom("DataCloneError", () => {
+ store.put({ key: 1, property: [
+ { get x() { getter1Called = true; return 5; } },
+ createEmptyWasmModule(),
+ { get x() { getter2Called = true; return 5; } }
+ ]});
+ });
+
+ assert_true(getter1Called, "The getter before the WebAssembly.Module must have been called");
+ assert_false(getter2Called, "The getter after the WebAssembly.Module must not have been called");
+ t.done();
+ };
+}, "WebAssembly.Module cloning via the IndexedDB: is interleaved correctly");
diff --git a/testing/web-platform/tests/wasm/serialization/module/serialization-via-notifications-api.any.js b/testing/web-platform/tests/wasm/serialization/module/serialization-via-notifications-api.any.js
new file mode 100644
index 0000000000..3672192df7
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/serialization-via-notifications-api.any.js
@@ -0,0 +1,28 @@
+"use strict";
+
+function createEmptyWasmModule() {
+ return new WebAssembly.Module(
+ new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]));
+}
+
+test(() => {
+ assert_throws_dom("DataCloneError", () => {
+ new Notification("Bob: Hi", { data: createEmptyWasmModule() });
+ })
+}, "WebAssembly.Module cloning via the Notifications API's data member: basic case");
+
+test(() => {
+ let getter1Called = false;
+ let getter2Called = false;
+
+ assert_throws_dom("DataCloneError", () => {
+ new Notification("Bob: Hi", { data: [
+ { get x() { getter1Called = true; return 5; } },
+ createEmptyWasmModule(),
+ { get x() { getter2Called = true; return 5; } }
+ ]});
+ });
+
+ assert_true(getter1Called, "The getter before the SAB must have been called");
+ assert_false(getter2Called, "The getter after the SAB must not have been called");
+}, "WebAssembly.Module cloning via the Notifications API's data member: is interleaved correctly");
diff --git a/testing/web-platform/tests/wasm/serialization/module/share-module-cross-origin-fails.sub.html b/testing/web-platform/tests/wasm/serialization/module/share-module-cross-origin-fails.sub.html
new file mode 100644
index 0000000000..5a52b96698
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/share-module-cross-origin-fails.sub.html
@@ -0,0 +1,31 @@
+<title>Postmessage of a WebAssembly.Module cross-origin fails with a messageerror</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/test-incrementer.js"></script>
+
+<body></body>
+
+<script>
+promise_test(async t => {
+ // Create an iframe. It will notify this document when the "messageerror"
+ // event is triggered.
+ const iframe = document.createElement("iframe");
+ iframe.src = "//{{domains[www1]}}:{{location[port]}}" +
+ "/wasm/serialization/module/resources/incrementer-iframe-failure.html";
+
+ // Instantiate and wait for the document to be loaded.
+ const iframeLoaded = new Promise(resolve => iframe.onload = resolve);
+ document.body.appendChild(iframe);
+ await iframeLoaded;
+
+ const module = await createWasmModule();
+ const messageErrorReceived = new Promise(resolve => {
+ window.onmessage = ({data}) => {
+ if (data == 'messageerror received')
+ resolve();
+ };
+ });
+ iframe.contentWindow.postMessage({message: 'send module', module}, "*");
+ await messageErrorReceived;
+}, "postMessaging a wasm module to a cross-origin iframe fails");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/window-domain-success.sub.html b/testing/web-platform/tests/wasm/serialization/module/window-domain-success.sub.html
new file mode 100644
index 0000000000..07360d8264
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/window-domain-success.sub.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<meta charset="utf-8">
+<title>Structured cloning of WebAssembly.Module into same-origin-domain windows</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/test-incrementer.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+document.domain = "{{host}}";
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ resolve(testSharingViaIncrementerScript(t, window, "window", iframe.contentWindow, "iframe", "*"));
+ });
+ iframe.src = "//{{domains[www1]}}:{{location[port]}}/wasm/serialization/module/resources/incrementer-iframe-domain.sub.html";
+ document.body.appendChild(iframe);
+ });
+}, "postMessaging to a same-origin-domain (but not same-origin) iframe allows them to instantiate");
+
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/window-messagechannel-success.html b/testing/web-platform/tests/wasm/serialization/module/window-messagechannel-success.html
new file mode 100644
index 0000000000..e686c81135
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/window-messagechannel-success.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<meta charset="utf-8">
+<title>Structured cloning of WebAssembly.Module using MessageChannel</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/test-incrementer.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ const worker = new Worker("resources/incrementer-worker-with-channel.js");
+ const channel = new MessageChannel();
+ worker.postMessage(channel.port2, [channel.port2]);
+
+ return testSharingViaIncrementerScript(t, channel.port1, "window", channel.port1, "worker");
+}, "postMessaging to a dedicated worker via MessageChannel allows them to instantiate");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/window-serviceworker-failure.https.html b/testing/web-platform/tests/wasm/serialization/module/window-serviceworker-failure.https.html
new file mode 100644
index 0000000000..d7285e2fa3
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/window-serviceworker-failure.https.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<title>WebAssembly.Module cannot cross agent clusters, service worker edition</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script src="./resources/create-empty-wasm-module.js"></script>
+
+<script>
+"use strict";
+promise_test(t => {
+ const scope = "resources/blank.html";
+ return service_worker_unregister_and_register(t, "resources/serviceworker-failure.js", scope)
+ .then(reg => {
+ t.add_cleanup(() => service_worker_unregister(t, scope));
+ return wait_for_state(t, reg.installing, "activated");
+ })
+ .then(() => with_iframe(scope))
+ .then(iframe => {
+ t.add_cleanup(() => iframe.remove());
+ const sw = iframe.contentWindow.navigator.serviceWorker;
+ let state = "start in window";
+
+ return new Promise(resolve => {
+ sw.onmessage = t.step_func(e => {
+ if (e.data === "start in worker") {
+ assert_equals(state, "start in window");
+ sw.controller.postMessage(createEmptyWasmModule());
+ state = "we are expecting confirmation of an onmessageerror in the worker";
+ } else if (e.data === "onmessageerror was received in worker") {
+ assert_equals(state, "we are expecting confirmation of an onmessageerror in the worker");
+ state = "we are expecting a messageerror due to the worker sending us a WebAssembly.Module";
+ sw.controller.postMessage(state);
+ } else {
+ assert_unreached("Got an unexpected message from the service worker: " + e.data);
+ }
+ });
+
+ sw.onmessageerror = t.step_func(e => {
+ assert_equals(state, "we are expecting a messageerror due to the worker sending us a WebAssembly.Module");
+
+ assert_equals(e.data, null, "data");
+ assert_equals(e.origin, self.origin, "origin");
+ assert_not_equals(e.source, null, "source");
+ assert_equals(e.ports.length, 0, "ports length");
+
+ state = "done in window";
+ resolve();
+ });
+
+ sw.controller.postMessage(state);
+ });
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/window-sharedworker-failure.html b/testing/web-platform/tests/wasm/serialization/module/window-sharedworker-failure.html
new file mode 100644
index 0000000000..667e985a30
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/window-sharedworker-failure.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<title>WebAssembly.Modules cannot cross agent clusters, shared worker edition</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/create-empty-wasm-module.js"></script>
+<script>
+async_test(t => {
+ const sw = new SharedWorker("resources/sharedworker-failure.js")
+ let state = ""
+ sw.port.onmessage = t.step_func(e => {
+ if(e.data === "send-sw-failure") {
+ sw.port.postMessage(createEmptyWasmModule())
+ } else if(e.data === "send-sw-failure-success") {
+ state = "send-window-failure"
+ sw.port.postMessage(state)
+ } else {
+ assert_unreached()
+ }
+ })
+ sw.port.onmessageerror = t.step_func(e => {
+ if(state === "send-window-failure") {
+ assert_equals(e.data, null, "data")
+ assert_equals(e.origin, "", "origin")
+ assert_equals(e.source, null, "source")
+ assert_equals(e.ports.length, 0, "ports length")
+ t.done()
+ } else {
+ assert_unreached()
+ }
+ })
+})
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/window-similar-but-cross-origin-success.sub.html b/testing/web-platform/tests/wasm/serialization/module/window-similar-but-cross-origin-success.sub.html
new file mode 100644
index 0000000000..a615547de0
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/window-similar-but-cross-origin-success.sub.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<meta charset="utf-8">
+<title>Structured cloning of WebAssembly.Module to similar-origin, but not same-origin, windows</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/test-incrementer.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+document.domain = "{{host}}";
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ resolve(testSharingViaIncrementerScript(t, window, "window", iframe.contentWindow, "iframe", "*"));
+ });
+ iframe.src = "//{{domains[www1]}}:{{location[port]}}/wasm/serialization/module/resources/incrementer-iframe.html";
+ document.body.appendChild(iframe);
+ });
+}, "postMessaging to a not same-origin-domain, but similar origin, iframe allows them to instantiate");
+</script>
diff --git a/testing/web-platform/tests/wasm/serialization/module/window-simple-success.html b/testing/web-platform/tests/wasm/serialization/module/window-simple-success.html
new file mode 100644
index 0000000000..6f2ccf465e
--- /dev/null
+++ b/testing/web-platform/tests/wasm/serialization/module/window-simple-success.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<!-- Based on similar tests in html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/ -->
+<meta charset="utf-8">
+<title>Structured cloning of WebAssembly.Module: simple success cases that don't need dedicated files</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/test-incrementer.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+
+promise_test(t => {
+ const worker = new Worker("resources/incrementer-worker.js");
+
+ return testSharingViaIncrementerScript(t, worker, "window", worker, "worker", undefined);
+}, "postMessaging to a dedicated worker allows them to instantiate");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ resolve(testSharingViaIncrementerScript(t, window, "window", iframe.contentWindow, "iframe", "*"));
+ });
+ iframe.src = "resources/incrementer-iframe.html";
+ document.body.appendChild(iframe);
+ });
+}, "postMessaging to a same-origin iframe allows them to instantiate");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const iframe = document.createElement("iframe");
+ iframe.onload = t.step_func(() => {
+ const level1 = iframe.contentWindow;
+ const level2 = level1.frames[0];
+ const level3 = level2.frames[0];
+ const targetWindow = level3.frames[0];
+ resolve(testSharingViaIncrementerScript(t, window, "window", targetWindow, "nested iframe", "*"));
+ });
+ iframe.src = "resources/nested-iframe-1.html";
+ document.body.appendChild(iframe);
+ });
+}, "postMessaging to a same-origin deeply-nested iframe allows them to instantiate");
+
+promise_test(t => {
+ return new Promise(resolve => {
+ const w = window.open("resources/incrementer-popup.html");
+ w.onload = t.step_func(() => {
+ resolve(testSharingViaIncrementerScript(t, window, "window", w, "popup window", "*").then(() => {
+ w.close();
+ }));
+ });
+ });
+}, "postMessaging to a same-origin opened window allows them to instantiate");
+
+</script>
diff --git a/testing/web-platform/tests/wasm/webapi/META.yml b/testing/web-platform/tests/wasm/webapi/META.yml
new file mode 100644
index 0000000000..69715cd7c8
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/META.yml
@@ -0,0 +1 @@
+spec: https://webassembly.github.io/spec/web-api/
diff --git a/testing/web-platform/tests/wasm/webapi/abort.any.js b/testing/web-platform/tests/wasm/webapi/abort.any.js
new file mode 100644
index 0000000000..f5ddd353aa
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/body.any.js b/testing/web-platform/tests/wasm/webapi/body.any.js
new file mode 100644
index 0000000000..4db7e8d123
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/contenttype.any.js b/testing/web-platform/tests/wasm/webapi/contenttype.any.js
new file mode 100644
index 0000000000..0a2f5f1122
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/empty-body.any.js b/testing/web-platform/tests/wasm/webapi/empty-body.any.js
new file mode 100644
index 0000000000..0771647b70
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/execute-start.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/execute-start.tentative.html
new file mode 100644
index 0000000000..a35adbe8eb
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/exported-names.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/exported-names.tentative.html
new file mode 100644
index 0000000000..16a9c59787
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/invalid-bytecode.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/invalid-bytecode.tentative.html
new file mode 100644
index 0000000000..0e447dbee5
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/js-wasm-cycle-errors.tentative.html
new file mode 100644
index 0000000000..f45e06ece5
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/js-wasm-cycle.tentative.html
new file mode 100644
index 0000000000..38b0d3203c
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/module-parse-error.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/module-parse-error.tentative.html
new file mode 100644
index 0000000000..0e447dbee5
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resolve-export.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resolve-export.js
new file mode 100644
index 0000000000..e0dcf493f8
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resolve-export.js
@@ -0,0 +1 @@
+export { f } from "./resources/resolve-export.wasm";
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resolve-export.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/resolve-export.tentative.html
new file mode 100644
index 0000000000..1468822102
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resources/execute-start.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/execute-start.wasm
new file mode 100644
index 0000000000..ecfdda1f9a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/execute-start.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/exported-names.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/exported-names.wasm
new file mode 100644
index 0000000000..ebffad193c
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/exported-names.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm
new file mode 100644
index 0000000000..1ae8b721f3
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/invalid-bytecode.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/invalid-module.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/invalid-module.wasm
new file mode 100644
index 0000000000..dd711f0953
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/invalid-module.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.js
new file mode 100644
index 0000000000..06cb8a0ad9
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm
new file mode 100644
index 0000000000..b89d94dde7
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-function-error.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.js
new file mode 100644
index 0000000000..1f375b8ce1
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm
new file mode 100644
index 0000000000..2a9017f87b
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-global.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.js
new file mode 100644
index 0000000000..92e37a86ac
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm
new file mode 100644
index 0000000000..e699a9b3c4
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-memory.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.js
new file mode 100644
index 0000000000..5d6794489f
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm
new file mode 100644
index 0000000000..ec4883e652
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-table.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.js
new file mode 100644
index 0000000000..f7b0d62080
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm
new file mode 100644
index 0000000000..083409e260
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle-value.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle.js
new file mode 100644
index 0000000000..8ee579e2ad
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm
new file mode 100644
index 0000000000..77a3b86ab6
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/js-wasm-cycle.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/log.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/log.js
new file mode 100644
index 0000000000..0c4f5ed519
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/log.js
@@ -0,0 +1 @@
+export function logExec() { log.push("executed"); }
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/resolve-export.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/resolve-export.wasm
new file mode 100644
index 0000000000..d8fc92d022
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/resolve-export.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm
new file mode 100644
index 0000000000..f9f0cf2799
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-export-i64-global.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm
new file mode 100644
index 0000000000..0ee948f96f
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-export-to-wasm.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm
new file mode 100644
index 0000000000..c27bcb068d
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-error-from-wasm.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm
new file mode 100644
index 0000000000..652ff14310
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-from-wasm.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-func.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-func.js
new file mode 100644
index 0000000000..78982c32dc
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-func.js
@@ -0,0 +1 @@
+export let f = 5;
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-func.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-func.wasm
new file mode 100644
index 0000000000..2f23c58520
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-func.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-global.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-global.js
new file mode 100644
index 0000000000..4258cd2d7d
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-global.js
@@ -0,0 +1 @@
+export let g = 5;
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-global.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-global.wasm
new file mode 100644
index 0000000000..2f8bd77940
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-global.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-memory.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-memory.js
new file mode 100644
index 0000000000..4cee889838
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-memory.js
@@ -0,0 +1 @@
+export let m = 5;
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm
new file mode 100644
index 0000000000..d9474047cd
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-memory.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-table.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-table.js
new file mode 100644
index 0000000000..ca823646cb
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-table.js
@@ -0,0 +1 @@
+export let t = 5;
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-table.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-table.wasm
new file mode 100644
index 0000000000..8ccc8be7f2
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-import-table.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-js-cycle.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-js-cycle.js
new file mode 100644
index 0000000000..161edab4f6
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm
new file mode 100644
index 0000000000..b700377b27
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/wasm-js-cycle.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-helper.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-helper.js
new file mode 100644
index 0000000000..277bb4c1ea
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-helper.js
@@ -0,0 +1 @@
+export function pm(x) { postMessage(x); }
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js
new file mode 100644
index 0000000000..c7a4f0d437
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js
@@ -0,0 +1,3 @@
+import source modSource from "./worker.wasm";
+assert_true(modSource instanceof WebAssembly.Module);
+assert_true(await import.source("./worker.wasm") === modSource);
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker.js
new file mode 100644
index 0000000000..1163e477fd
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker.js
@@ -0,0 +1,2 @@
+import * as mod from "./worker.wasm";
+assert_true(await import("./worker.wasm") === mod);
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker.wasm b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker.wasm
new file mode 100644
index 0000000000..e942dc54ac
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/source-phase.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/source-phase.tentative.html
new file mode 100644
index 0000000000..eb415a4c6f
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/source-phase.tentative.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>Source phase imports</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script type=module>
+setup({ single_test: true });
+
+import source exportedNamesSource from "./resources/exported-names.wasm";
+
+assert_true(exportedNamesSource instanceof WebAssembly.Module);
+const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module);
+assert_equals(AbstractModuleSource.name, "AbstractModuleSource");
+assert_true(exportedNamesSource instanceof AbstractModuleSource);
+
+assert_array_equals(WebAssembly.Module.exports(exportedNamesSource).sort(),
+ ["func", "glob", "mem", "tab"]);
+
+const wasmImportFromWasmSource = await import.source("./resources/wasm-import-from-wasm.wasm");
+
+assert_true(wasmImportFromWasmSource instanceof WebAssembly.Module);
+
+let logged = false;
+const instance = await WebAssembly.instantiate(wasmImportFromWasmSource, {
+ "./wasm-export-to-wasm.wasm": {
+ log () {
+ logged = true;
+ }
+ }
+});
+instance.exports.logExec();
+assert_true(logged);
+
+done();
+</script>
diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/wasm-import-wasm-export.tentative.html
new file mode 100644
index 0000000000..3761a22f21
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/wasm-import.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/wasm-import.tentative.html
new file mode 100644
index 0000000000..243cfd46e4
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/wasm-js-cycle.tentative.html
new file mode 100644
index 0000000000..298d4d40b0
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/wasm-to-wasm-link-error.tentative.html
new file mode 100644
index 0000000000..6c43e72b09
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/worker-import.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/worker-import.tentative.html
new file mode 100644
index 0000000000..739f2d3f28
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/esm-integration/worker.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/worker.tentative.html
new file mode 100644
index 0000000000..6145dd04ff
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/esm-integration/worker.tentative.html
@@ -0,0 +1,16 @@
+<!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();
+}
+worker.onerror = () => {
+ assert_unreached("worker got an error");
+}
+</script>
diff --git a/testing/web-platform/tests/wasm/webapi/historical.any.js b/testing/web-platform/tests/wasm/webapi/historical.any.js
new file mode 100644
index 0000000000..257112c416
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/idlharness.any.js b/testing/web-platform/tests/wasm/webapi/idlharness.any.js
new file mode 100644
index 0000000000..0c4669e6ca
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/instantiateStreaming-bad-imports.any.js b/testing/web-platform/tests/wasm/webapi/instantiateStreaming-bad-imports.any.js
new file mode 100644
index 0000000000..38ecc40252
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/instantiateStreaming.any.js b/testing/web-platform/tests/wasm/webapi/instantiateStreaming.any.js
new file mode 100644
index 0000000000..cf3a5e7331
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/invalid-args.any.js b/testing/web-platform/tests/wasm/webapi/invalid-args.any.js
new file mode 100644
index 0000000000..b27e018a98
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/invalid-code.any.js b/testing/web-platform/tests/wasm/webapi/invalid-code.any.js
new file mode 100644
index 0000000000..37373d4997
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/modified-contenttype.any.js b/testing/web-platform/tests/wasm/webapi/modified-contenttype.any.js
new file mode 100644
index 0000000000..354930517c
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/origin.sub.any.js b/testing/web-platform/tests/wasm/webapi/origin.sub.any.js
new file mode 100644
index 0000000000..bf7901eedd
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/rejected-arg.any.js b/testing/web-platform/tests/wasm/webapi/rejected-arg.any.js
new file mode 100644
index 0000000000..49018db5e8
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/resources/incrementer.no_mime_type.wasm b/testing/web-platform/tests/wasm/webapi/resources/incrementer.no_mime_type.wasm
new file mode 100644
index 0000000000..47afcdef2a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/resources/incrementer.no_mime_type.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/resources/incrementer.wasm b/testing/web-platform/tests/wasm/webapi/resources/incrementer.wasm
new file mode 100644
index 0000000000..47afcdef2a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/resources/incrementer.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/resources/incrementer.wasm.headers b/testing/web-platform/tests/wasm/webapi/resources/incrementer.wasm.headers
new file mode 100644
index 0000000000..76b9c163b6
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/resources/incrementer.wasm.headers
@@ -0,0 +1,2 @@
+Content-Type: application/wasm
+Cache-Control: max-age=3600
diff --git a/testing/web-platform/tests/wasm/webapi/resources/incrementer.wrong_mime_type.wasm b/testing/web-platform/tests/wasm/webapi/resources/incrementer.wrong_mime_type.wasm
new file mode 100644
index 0000000000..47afcdef2a
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/resources/incrementer.wrong_mime_type.wasm
Binary files differ
diff --git a/testing/web-platform/tests/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers b/testing/web-platform/tests/wasm/webapi/resources/incrementer.wrong_mime_type.wasm.headers
new file mode 100644
index 0000000000..833ee71634
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/status.any.js b/testing/web-platform/tests/wasm/webapi/status.any.js
new file mode 100644
index 0000000000..f3859646cc
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/status.py b/testing/web-platform/tests/wasm/webapi/status.py
new file mode 100644
index 0000000000..9f056678c1
--- /dev/null
+++ b/testing/web-platform/tests/wasm/webapi/status.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ status = int(request.GET[b"status"])
+ module = b"\0asm\1\0\0\0"
+ return status, [(b"Content-Type", b"application/wasm")], module
diff --git a/testing/web-platform/tests/wasm/webapi/wasm_stream_compile_test.html b/testing/web-platform/tests/wasm/webapi/wasm_stream_compile_test.html
new file mode 100644
index 0000000000..790410e425
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/wasm/webapi/wasm_stream_instantiate_test.html b/testing/web-platform/tests/wasm/webapi/wasm_stream_instantiate_test.html
new file mode 100644
index 0000000000..f39f650495
--- /dev/null
+++ b/testing/web-platform/tests/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>