diff --git a/test/common/wpt.js b/test/common/wpt.js index 74b182e5b84296..bfdf6fa9054028 100644 --- a/test/common/wpt.js +++ b/test/common/wpt.js @@ -885,7 +885,7 @@ class WPTRunner { console.log(test.stack); } const command = `${process.execPath} ${process.execArgv}` + - ` ${require.main.filename} '${spec.filename}${spec.variant}'`; + ` ${require.main?.filename} '${spec.filename}${spec.variant}'`; console.log(`Command: ${command}\n`); reportResult?.addSubtest(test.name, 'FAIL', test.message); diff --git a/test/common/wpt/worker.js b/test/common/wpt/worker.js index 855ec7e91c394b..30585ecbe57f73 100644 --- a/test/common/wpt/worker.js +++ b/test/common/wpt/worker.js @@ -1,6 +1,10 @@ 'use strict'; -const { runInNewContext, runInThisContext } = require('vm'); +const { + runInNewContext, + runInThisContext, + constants: { USE_MAIN_CONTEXT_DEFAULT_LOADER }, +} = require('vm'); const { setFlagsFromString } = require('v8'); const { parentPort, workerData } = require('worker_threads'); @@ -28,11 +32,14 @@ globalThis.fetch = function fetch(file) { }; if (workerData.initScript) { - runInThisContext(workerData.initScript); + runInThisContext(workerData.initScript, { + importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER, + }); } runInThisContext(workerData.harness.code, { filename: workerData.harness.filename, + importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER, }); // eslint-disable-next-line no-undef @@ -66,5 +73,8 @@ add_completion_callback((_, status) => { }); for (const scriptToRun of workerData.scriptsToRun) { - runInThisContext(scriptToRun.code, { filename: scriptToRun.filename }); + runInThisContext(scriptToRun.code, { + filename: scriptToRun.filename, + importModuleDynamically: USE_MAIN_CONTEXT_DEFAULT_LOADER, + }); } diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/exports.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/exports.tentative.any.js new file mode 100644 index 00000000000000..9feaa283aaed26 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/exports.tentative.any.js @@ -0,0 +1,36 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +"use strict"; + +promise_test(async () => { + const mod = await import("./resources/exports.wasm"); + + assert_array_equals(Object.getOwnPropertyNames(mod).sort(), [ + "a\u200Bb\u0300c", + "func", + "glob", + "mem", + "tab", + "value with spaces", + "🎯test-func!", + ]); + assert_true(mod.func instanceof Function); + assert_true(mod.mem instanceof WebAssembly.Memory); + assert_true(mod.tab instanceof WebAssembly.Table); + + assert_false(mod.glob instanceof WebAssembly.Global); + assert_equals(typeof mod.glob, "number"); + + assert_throws_js(TypeError, () => { + mod.func = 2; + }); + + assert_equals(typeof mod["value with spaces"], "number"); + assert_equals(mod["value with spaces"], 123); + + assert_true(mod["🎯test-func!"] instanceof Function); + assert_equals(mod["🎯test-func!"](), 456); + + assert_equals(typeof mod["a\u200Bb\u0300c"], "number"); + assert_equals(mod["a\u200Bb\u0300c"], 789); +}, "Exported names from a WebAssembly module"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/global-exports-live-bindings.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/global-exports-live-bindings.tentative.any.js new file mode 100644 index 00000000000000..d80c30943c04c7 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/global-exports-live-bindings.tentative.any.js @@ -0,0 +1,46 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const wasmExports = await import("./resources/globals.wasm"); + + wasmExports.setLocalMutI32(555); + assert_equals(wasmExports.getLocalMutI32(), 555); + assert_equals(wasmExports.localMutI32, 555); + + wasmExports.setLocalMutI64(444n); + assert_equals(wasmExports.getLocalMutI64(), 444n); + assert_equals(wasmExports.localMutI64, 444n); + + wasmExports.setLocalMutF32(3.33); + assert_equals(Math.round(wasmExports.getLocalMutF32() * 100) / 100, 3.33); + assert_equals(Math.round(wasmExports.localMutF32 * 100) / 100, 3.33); + + wasmExports.setLocalMutF64(2.22); + assert_equals(wasmExports.getLocalMutF64(), 2.22); + assert_equals(wasmExports.localMutF64, 2.22); + + const anotherTestObj = { another: "test object" }; + wasmExports.setLocalMutExternref(anotherTestObj); + assert_equals(wasmExports.getLocalMutExternref(), anotherTestObj); + assert_equals(wasmExports.localMutExternref, anotherTestObj); +}, "Local mutable global exports should be live bindings"); + +promise_test(async () => { + const wasmExports = await import("./resources/globals.wasm"); + + wasmExports.setDepMutI32(3001); + assert_equals(wasmExports.getDepMutI32(), 3001); + assert_equals(wasmExports.depMutI32, 3001); + + wasmExports.setDepMutI64(30000000001n); + assert_equals(wasmExports.getDepMutI64(), 30000000001n); + assert_equals(wasmExports.depMutI64, 30000000001n); + + wasmExports.setDepMutF32(30.01); + assert_equals(Math.round(wasmExports.getDepMutF32() * 100) / 100, 30.01); + assert_equals(Math.round(wasmExports.depMutF32 * 100) / 100, 30.01); + + wasmExports.setDepMutF64(300.0001); + assert_equals(wasmExports.getDepMutF64(), 300.0001); + assert_equals(wasmExports.depMutF64, 300.0001); +}, "Dep module mutable global exports should be live bindings"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/global-exports.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/global-exports.tentative.any.js new file mode 100644 index 00000000000000..51ebcaf95a2ad9 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/global-exports.tentative.any.js @@ -0,0 +1,111 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const wasmModule = await import("./resources/globals.wasm"); + + assert_equals(wasmModule.importedI32, 42); + assert_equals(wasmModule.importedI64, 9223372036854775807n); + assert_equals(Math.round(wasmModule.importedF32 * 100000) / 100000, 3.14159); + assert_equals(wasmModule.importedF64, 3.141592653589793); + assert_not_equals(wasmModule.importedExternref, null); + assert_equals(wasmModule.importedNullExternref, null); +}, "WebAssembly module global values should be unwrapped when importing in ESM integration"); + +promise_test(async () => { + const wasmModule = await import("./resources/globals.wasm"); + + assert_equals(wasmModule.importedMutI32, 100); + assert_equals(wasmModule.importedMutI64, 200n); + assert_equals( + Math.round(wasmModule.importedMutF32 * 100000) / 100000, + 2.71828 + ); + assert_equals(wasmModule.importedMutF64, 2.718281828459045); + assert_not_equals(wasmModule.importedMutExternref, null); + assert_equals(wasmModule.importedMutExternref.mutable, "global"); +}, "WebAssembly mutable global values should be unwrapped when importing in ESM integration"); + +promise_test(async () => { + const wasmModule = await import("./resources/globals.wasm"); + + assert_equals(wasmModule["🚀localI32"], 42); + assert_equals(wasmModule.localMutI32, 100); + assert_equals(wasmModule.localI64, 9223372036854775807n); + assert_equals(wasmModule.localMutI64, 200n); + assert_equals(Math.round(wasmModule.localF32 * 100000) / 100000, 3.14159); + assert_equals(Math.round(wasmModule.localMutF32 * 100000) / 100000, 2.71828); + assert_equals(wasmModule.localF64, 2.718281828459045); + assert_equals(wasmModule.localMutF64, 3.141592653589793); +}, "WebAssembly local global values should be unwrapped when exporting in ESM integration"); + +promise_test(async () => { + const wasmModule = await import("./resources/globals.wasm"); + + assert_equals(wasmModule.depI32, 1001); + assert_equals(wasmModule.depMutI32, 2001); + assert_equals(wasmModule.depI64, 10000000001n); + assert_equals(wasmModule.depMutI64, 20000000001n); + assert_equals(Math.round(wasmModule.depF32 * 100) / 100, 10.01); + assert_equals(Math.round(wasmModule.depMutF32 * 100) / 100, 20.01); + assert_equals(wasmModule.depF64, 100.0001); + assert_equals(wasmModule.depMutF64, 200.0001); +}, "WebAssembly module globals from imported WebAssembly modules should be unwrapped"); + +promise_test(async () => { + const wasmModule = await import("./resources/globals.wasm"); + + assert_equals(wasmModule.importedI32, 42); + assert_equals(wasmModule.importedMutI32, 100); + assert_equals(wasmModule.importedI64, 9223372036854775807n); + assert_equals(wasmModule.importedMutI64, 200n); + assert_equals(Math.round(wasmModule.importedF32 * 100000) / 100000, 3.14159); + assert_equals( + Math.round(wasmModule.importedMutF32 * 100000) / 100000, + 2.71828 + ); + assert_equals(wasmModule.importedF64, 3.141592653589793); + assert_equals(wasmModule.importedMutF64, 2.718281828459045); + assert_equals(wasmModule.importedExternref !== null, true); + assert_equals(wasmModule.importedMutExternref !== null, true); + assert_equals(wasmModule.importedNullExternref, null); + + assert_equals(wasmModule["🚀localI32"], 42); + assert_equals(wasmModule.localMutI32, 100); + assert_equals(wasmModule.localI64, 9223372036854775807n); + assert_equals(wasmModule.localMutI64, 200n); + assert_equals(Math.round(wasmModule.localF32 * 100000) / 100000, 3.14159); + assert_equals(Math.round(wasmModule.localMutF32 * 100000) / 100000, 2.71828); + assert_equals(wasmModule.localF64, 2.718281828459045); + assert_equals(wasmModule.localMutF64, 3.141592653589793); + + assert_equals(wasmModule.getImportedMutI32(), 100); + assert_equals(wasmModule.getImportedMutI64(), 200n); + assert_equals( + Math.round(wasmModule.getImportedMutF32() * 100000) / 100000, + 2.71828 + ); + assert_equals(wasmModule.getImportedMutF64(), 2.718281828459045); + assert_equals(wasmModule.getImportedMutExternref() !== null, true); + + assert_equals(wasmModule.getLocalMutI32(), 100); + assert_equals(wasmModule.getLocalMutI64(), 200n); + assert_equals( + Math.round(wasmModule.getLocalMutF32() * 100000) / 100000, + 2.71828 + ); + assert_equals(wasmModule.getLocalMutF64(), 3.141592653589793); + assert_equals(wasmModule.getLocalMutExternref(), null); + + assert_equals(wasmModule.depI32, 1001); + assert_equals(wasmModule.depMutI32, 2001); + assert_equals(wasmModule.getDepMutI32(), 2001); + assert_equals(wasmModule.depI64, 10000000001n); + assert_equals(wasmModule.depMutI64, 20000000001n); + assert_equals(wasmModule.getDepMutI64(), 20000000001n); + assert_equals(Math.round(wasmModule.depF32 * 100) / 100, 10.01); + assert_equals(Math.round(wasmModule.depMutF32 * 100) / 100, 20.01); + assert_equals(Math.round(wasmModule.getDepMutF32() * 100) / 100, 20.01); + assert_equals(wasmModule.depF64, 100.0001); + assert_equals(wasmModule.depMutF64, 200.0001); + assert_equals(wasmModule.getDepMutF64(), 200.0001); +}, "WebAssembly should properly handle all global types"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/js-wasm-cycle.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/js-wasm-cycle.tentative.any.js new file mode 100644 index 00000000000000..ce71a33a24b043 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/js-wasm-cycle.tentative.any.js @@ -0,0 +1,7 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const { f } = await import("./resources/js-wasm-cycle.js"); + + assert_equals(f(), 24); +}, "Check bindings in JavaScript and WebAssembly cycle (JS higher)"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/mutable-global-sharing.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/mutable-global-sharing.tentative.any.js new file mode 100644 index 00000000000000..d76c69ad5d2333 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/mutable-global-sharing.tentative.any.js @@ -0,0 +1,80 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const exporterModule = await import("./resources/mutable-global-export.wasm"); + const reexporterModule = await import( + "./resources/mutable-global-reexport.wasm" + ); + + assert_equals(exporterModule.mutableValue, 100); + assert_equals(reexporterModule.reexportedMutableValue, 100); +}, "WebAssembly modules should export shared mutable globals with correct initial values"); + +promise_test(async () => { + const exporterModule = await import("./resources/mutable-global-export.wasm"); + const reexporterModule = await import( + "./resources/mutable-global-reexport.wasm" + ); + + exporterModule.setGlobal(500); + + assert_equals(exporterModule.getGlobal(), 500, "exporter should see 500"); + assert_equals(reexporterModule.getImportedGlobal(), 500); + + reexporterModule.setImportedGlobal(600); + + assert_equals(exporterModule.getGlobal(), 600); + assert_equals(reexporterModule.getImportedGlobal(), 600); + + exporterModule.setGlobal(700); + + assert_equals(exporterModule.getGlobal(), 700); + assert_equals(reexporterModule.getImportedGlobal(), 700); +}, "Wasm-to-Wasm mutable global sharing is live"); + +promise_test(async () => { + const module1 = await import("./resources/mutable-global-export.wasm"); + const module2 = await import("./resources/mutable-global-export.wasm"); + + assert_equals(module1, module2); + + module1.setGlobal(800); + assert_equals(module1.getGlobal(), 800, "module1 should see its own change"); + assert_equals(module2.getGlobal(), 800); +}, "Multiple JavaScript imports return the same WebAssembly module instance"); + +promise_test(async () => { + const exporterModule = await import("./resources/mutable-global-export.wasm"); + const reexporterModule = await import( + "./resources/mutable-global-reexport.wasm" + ); + + assert_equals(exporterModule.getV128Lane(0), 1); + assert_equals(exporterModule.getV128Lane(1), 2); + assert_equals(exporterModule.getV128Lane(2), 3); + assert_equals(exporterModule.getV128Lane(3), 4); + + assert_equals(reexporterModule.getImportedV128Lane(0), 1); + assert_equals(reexporterModule.getImportedV128Lane(1), 2); + assert_equals(reexporterModule.getImportedV128Lane(2), 3); + assert_equals(reexporterModule.getImportedV128Lane(3), 4); +}, "v128 globals should work correctly in WebAssembly-to-WebAssembly imports"); + +promise_test(async () => { + const exporterModule = await import("./resources/mutable-global-export.wasm"); + const reexporterModule = await import( + "./resources/mutable-global-reexport.wasm" + ); + + exporterModule.setV128Global(10, 20, 30, 40); + + assert_equals(exporterModule.getV128Lane(0), 10); + assert_equals(exporterModule.getV128Lane(1), 20); + assert_equals(exporterModule.getV128Lane(2), 30); + assert_equals(exporterModule.getV128Lane(3), 40); + + assert_equals(reexporterModule.getImportedV128Lane(0), 10); + assert_equals(reexporterModule.getImportedV128Lane(1), 20); + assert_equals(reexporterModule.getImportedV128Lane(2), 30); + assert_equals(reexporterModule.getImportedV128Lane(3), 40); +}, "v128 global mutations should work correctly between WebAssembly modules"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/namespace-instance.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/namespace-instance.tentative.any.js new file mode 100644 index 00000000000000..e5e4e0721034d8 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/namespace-instance.tentative.any.js @@ -0,0 +1,52 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const wasmNamespace = await import("./resources/mutable-global-export.wasm"); + const instance = WebAssembly.namespaceInstance(wasmNamespace); + + assert_true(instance instanceof WebAssembly.Instance); + + wasmNamespace.setGlobal(999); + assert_equals(instance.exports.getGlobal(), 999); + + instance.exports.setGlobal(888); + assert_equals(wasmNamespace.getGlobal(), 888); +}, "WebAssembly.namespaceInstance() should return the underlying instance with shared state"); + +promise_test(async () => { + assert_throws_js(TypeError, () => WebAssembly.namespaceInstance({})); + assert_throws_js(TypeError, () => WebAssembly.namespaceInstance(null)); + assert_throws_js(TypeError, () => WebAssembly.namespaceInstance(undefined)); + assert_throws_js(TypeError, () => WebAssembly.namespaceInstance(42)); + assert_throws_js(TypeError, () => + WebAssembly.namespaceInstance("not a namespace") + ); + assert_throws_js(TypeError, () => WebAssembly.namespaceInstance([])); + assert_throws_js(TypeError, () => + WebAssembly.namespaceInstance(function () {}) + ); + + const jsModule = await import("./resources/globals.js"); + assert_throws_js(TypeError, () => WebAssembly.namespaceInstance(jsModule)); +}, "WebAssembly.namespaceInstance() should throw TypeError for non-WebAssembly namespaces"); + +promise_test(async () => { + const exportsModule = await import("./resources/exports.wasm"); + const globalsModule = await import("./resources/globals.wasm"); + + const exportsInstance = WebAssembly.namespaceInstance(exportsModule); + const globalsInstance = WebAssembly.namespaceInstance(globalsModule); + + assert_not_equals(exportsInstance, globalsInstance); + assert_true(exportsInstance.exports.func instanceof Function); + assert_true(globalsInstance.exports.getLocalMutI32 instanceof Function); + + globalsModule.setLocalMutI32(12345); + assert_equals(globalsInstance.exports.getLocalMutI32(), 12345); + + globalsInstance.exports.setLocalMutI32(54321); + assert_equals(globalsModule.getLocalMutI32(), 54321); + + const exportsInstance2 = WebAssembly.namespaceInstance(exportsModule); + assert_equals(exportsInstance, exportsInstance2); +}, "WebAssembly.namespaceInstance() should work correctly with multiple modules"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/reserved-import-names.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/reserved-import-names.tentative.any.js new file mode 100644 index 00000000000000..aa17735b0206ff --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/reserved-import-names.tentative.any.js @@ -0,0 +1,41 @@ +// Test that wasm: and wasm-js: reserved cases should cause WebAssembly.LinkError + +promise_test(async (t) => { + await promise_rejects_js( + t, + WebAssembly.LinkError, + import("./resources/invalid-import-name.wasm") + ); +}, "wasm: reserved import names should cause WebAssembly.LinkError"); + +promise_test(async (t) => { + await promise_rejects_js( + t, + WebAssembly.LinkError, + import("./resources/invalid-import-name-wasm-js.wasm") + ); +}, "wasm-js: reserved import names should cause WebAssembly.LinkError"); + +promise_test(async (t) => { + await promise_rejects_js( + t, + WebAssembly.LinkError, + import("./resources/invalid-export-name.wasm") + ); +}, "wasm: reserved export names should cause WebAssembly.LinkError"); + +promise_test(async (t) => { + await promise_rejects_js( + t, + WebAssembly.LinkError, + import("./resources/invalid-export-name-wasm-js.wasm") + ); +}, "wasm-js: reserved export names should cause WebAssembly.LinkError"); + +promise_test(async (t) => { + await promise_rejects_js( + t, + WebAssembly.LinkError, + import("./resources/invalid-import-module.wasm") + ); +}, "wasm-js: reserved module names should cause WebAssembly.LinkError"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resolve-export.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/resolve-export.tentative.any.js new file mode 100644 index 00000000000000..86325d11b0bad5 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/resolve-export.tentative.any.js @@ -0,0 +1,9 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async (t) => { + await promise_rejects_js( + t, + SyntaxError, + import("./resources/resolve-export.js") + ); +}, "ResolveExport on invalid re-export from WebAssembly"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/dep.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/dep.wasm new file mode 100644 index 00000000000000..ad9abfaa66af53 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/dep.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/exports.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/exports.wasm new file mode 100644 index 00000000000000..7382eff0356128 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/exports.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/globals.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/globals.js new file mode 100644 index 00000000000000..fabf23faf38ae3 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/globals.js @@ -0,0 +1,29 @@ +const i32_value = 42; +export { i32_value as "🚀i32_value" }; +export const i64_value = 9223372036854775807n; +export const f32_value = 3.14159; +export const f64_value = 3.141592653589793; + +export const i32_mut_value = new WebAssembly.Global( + { value: "i32", mutable: true }, + 100 +); +export const i64_mut_value = new WebAssembly.Global( + { value: "i64", mutable: true }, + 200n +); +export const f32_mut_value = new WebAssembly.Global( + { value: "f32", mutable: true }, + 2.71828 +); +export const f64_mut_value = new WebAssembly.Global( + { value: "f64", mutable: true }, + 2.718281828459045 +); + +export const externref_value = { hello: "world" }; +export const externref_mut_value = new WebAssembly.Global( + { value: "externref", mutable: true }, + { mutable: "global" } +); +export const null_externref_value = null; diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/globals.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/globals.wasm new file mode 100644 index 00000000000000..c5e6dd9f1ded20 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/globals.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-export-name-wasm-js.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-export-name-wasm-js.wasm new file mode 100644 index 00000000000000..a6b9a7f7c5ad57 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-export-name-wasm-js.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-export-name.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-export-name.wasm new file mode 100644 index 00000000000000..e66fcebf15ba4d Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-export-name.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-module.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-module.wasm new file mode 100644 index 00000000000000..ead151ac0c84ad Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-module.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-name-wasm-js.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-name-wasm-js.wasm new file mode 100644 index 00000000000000..15131a28a89e11 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-name-wasm-js.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-name.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-name.wasm new file mode 100644 index 00000000000000..3c631418294584 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/invalid-import-name.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-string-builtins.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-string-builtins.wasm new file mode 100644 index 00000000000000..4c4ff4df7f62b1 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-string-builtins.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-wasm-cycle.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-wasm-cycle.js new file mode 100644 index 00000000000000..9f14dc04bde41f --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-wasm-cycle.js @@ -0,0 +1,17 @@ +function f() { + return 42; +} +export { f }; + +import { mem, tab, glob, func } from "./js-wasm-cycle.wasm"; +assert_false(glob instanceof WebAssembly.Global, "imported global should be unwrapped in ESM integration"); +assert_equals(glob, 1, "unwrapped global should have direct value"); +assert_true(mem instanceof WebAssembly.Memory); +assert_true(tab instanceof WebAssembly.Table); +assert_true(func instanceof Function); + +f = () => { + return 24; +}; + +assert_equals(func(), 42); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-wasm-cycle.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-wasm-cycle.wasm new file mode 100644 index 00000000000000..77a3b86ab67528 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/js-wasm-cycle.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/log.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/log.js new file mode 100644 index 00000000000000..0c4f5ed519b0fd --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/log.js @@ -0,0 +1 @@ +export function logExec() { log.push("executed"); } diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/mutable-global-export.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/mutable-global-export.wasm new file mode 100644 index 00000000000000..89c478b19151d0 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/mutable-global-export.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/mutable-global-reexport.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/mutable-global-reexport.wasm new file mode 100644 index 00000000000000..b06f94a75d3c48 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/mutable-global-reexport.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/resolve-export.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/resolve-export.js new file mode 100644 index 00000000000000..81145e037f474b --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/resolve-export.js @@ -0,0 +1 @@ +export { f } from "./resolve-export.wasm"; diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/resolve-export.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/resolve-export.wasm new file mode 100644 index 00000000000000..d8fc92d022fbf4 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/resolve-export.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/source-phase-identity.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/source-phase-identity.js new file mode 100644 index 00000000000000..643d15b2f8912e --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/source-phase-identity.js @@ -0,0 +1,6 @@ +import * as mod1 from './exports.wasm'; +import * as mod2 from './exports.wasm'; +import source mod3 from './exports.wasm'; +import source mod4 from './exports.wasm'; + +export { mod1, mod2, mod3, mod4 } diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/wasm-export-to-wasm.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/wasm-export-to-wasm.wasm new file mode 100644 index 00000000000000..0ee948f96fdfbb Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/wasm-export-to-wasm.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/wasm-import-from-wasm.wasm b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/wasm-import-from-wasm.wasm new file mode 100644 index 00000000000000..652ff143100f83 Binary files /dev/null and b/test/fixtures/wpt/wasm/jsapi/esm-integration/resources/wasm-import-from-wasm.wasm differ diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/source-phase-string-builtins.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/source-phase-string-builtins.tentative.any.js new file mode 100644 index 00000000000000..e79b457dd464fc --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/source-phase-string-builtins.tentative.any.js @@ -0,0 +1,39 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const wasmModuleSource = await import.source("./resources/js-string-builtins.wasm"); + + assert_true(wasmModuleSource instanceof WebAssembly.Module); + + const instance = new WebAssembly.Instance(wasmModuleSource, {}); + + assert_equals(instance.exports.getLength("hello"), 5); + assert_equals( + instance.exports.concatStrings("hello", " world"), + "hello world" + ); + assert_equals(instance.exports.compareStrings("test", "test"), 1); + assert_equals(instance.exports.compareStrings("test", "different"), 0); + assert_equals(instance.exports.testString("hello"), 1); + assert_equals(instance.exports.testString(42), 0); +}, "String builtins should be supported in source phase imports"); + +promise_test(async () => { + const wasmModuleSource = await import.source("./resources/js-string-builtins.wasm"); + + const exports = WebAssembly.Module.exports(wasmModuleSource); + const exportNames = exports.map((exp) => exp.name); + + assert_true(exportNames.includes("getLength")); + assert_true(exportNames.includes("concatStrings")); + assert_true(exportNames.includes("compareStrings")); + assert_true(exportNames.includes("testString")); +}, "Source phase import should properly expose string builtin exports"); + +promise_test(async () => { + const wasmModuleSource = await import.source("./resources/js-string-builtins.wasm"); + + const imports = WebAssembly.Module.imports(wasmModuleSource); + + assert_equals(imports.length, 0); +}, "Source phase import should handle string builtin import reflection correctly"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/source-phase.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/source-phase.tentative.any.js new file mode 100644 index 00000000000000..ad45391f7f9c9f --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/source-phase.tentative.any.js @@ -0,0 +1,49 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const exportedNamesSource = await import.source("./resources/exports.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) + .map(({ name }) => name) + .sort(), + [ + "a\u200Bb\u0300c", + "func", + "glob", + "mem", + "tab", + "value with spaces", + "🎯test-func!", + ] + ); + + 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, "WebAssembly instance should execute imported function"); +}, "Source phase imports"); + +promise_test(async () => { + const { mod1, mod2, mod3, mod4 } = await import('./resources/source-phase-identity.js'); + + assert_equals(mod1, mod2); + assert_equals(mod3, mod4); +}, "Source phase identities"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/string-builtins.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/string-builtins.tentative.any.js new file mode 100644 index 00000000000000..bac8fd92727437 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/string-builtins.tentative.any.js @@ -0,0 +1,12 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const wasmModule = await import("./resources/js-string-builtins.wasm"); + + assert_equals(wasmModule.getLength("hello"), 5); + assert_equals(wasmModule.concatStrings("hello", " world"), "hello world"); + assert_equals(wasmModule.compareStrings("test", "test"), 1); + assert_equals(wasmModule.compareStrings("test", "different"), 0); + assert_equals(wasmModule.testString("hello"), 1); + assert_equals(wasmModule.testString(42), 0); +}, "String builtins should be supported in imports in ESM integration"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/v128-tdz.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/v128-tdz.tentative.any.js new file mode 100644 index 00000000000000..5d11e590148851 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/v128-tdz.tentative.any.js @@ -0,0 +1,11 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + const exporterModule = await import("./resources/mutable-global-export.wasm"); + const reexporterModule = await import( + "./resources/mutable-global-reexport.wasm" + ); + + assert_throws_js(ReferenceError, () => exporterModule.v128Export); + assert_throws_js(ReferenceError, () => reexporterModule.reexportedV128Export); +}, "v128 global exports should cause TDZ errors"); diff --git a/test/fixtures/wpt/wasm/jsapi/esm-integration/wasm-import-wasm-export.tentative.any.js b/test/fixtures/wpt/wasm/jsapi/esm-integration/wasm-import-wasm-export.tentative.any.js new file mode 100644 index 00000000000000..2c1a1446ee4987 --- /dev/null +++ b/test/fixtures/wpt/wasm/jsapi/esm-integration/wasm-import-wasm-export.tentative.any.js @@ -0,0 +1,14 @@ +// META: global=window,dedicatedworker,jsshell,shadowrealm + +promise_test(async () => { + globalThis.log = []; + + const { logExec } = await import("./resources/wasm-import-from-wasm.wasm"); + logExec(); + + assert_equals(globalThis.log.length, 1, "log should have one entry"); + assert_equals(globalThis.log[0], "executed"); + + // Clean up + delete globalThis.log; +}, "Check import and export between WebAssembly modules"); diff --git a/test/fixtures/wpt/wasm/resources/load_wasm.js b/test/fixtures/wpt/wasm/resources/load_wasm.js new file mode 100644 index 00000000000000..7f280ac032f40a --- /dev/null +++ b/test/fixtures/wpt/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/test/wpt/status/wasm/jsapi.json b/test/wpt/status/wasm/jsapi.json new file mode 100644 index 00000000000000..4d7f5c266dde5b --- /dev/null +++ b/test/wpt/status/wasm/jsapi.json @@ -0,0 +1,56 @@ +{ + "esm-integration/global-exports-live-bindings.tentative.any.js": { + "skip": "Live bindings unsupported pending V8 WebAssemblyModuleRecord" + }, + "esm-integration/v128-tdz.tentative.any.js": { + "skip": "v128 undefined Wasm bindings not yet supported in V8" + }, + "esm-integration/namespace-instance.tentative.any.js": { + "skip": "pending https://github.com/nodejs/node/pull/59024" + }, + "esm-integration/reserved-import-names.tentative.any.js": { + "skip": "pending https://github.com/nodejs/node/pull/59020" + }, + "esm-integration/source-phase-string-builtins.tentative.any.js": { + "skip": "pending https://github.com/nodejs/node/pull/59020" + }, + "esm-integration/string-builtins.tentative.any.js": { + "skip": "pending https://github.com/nodejs/node/pull/59020" + }, + "exception/getArg.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "function/call.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "function/constructor.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "function/table.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "function/type.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "global/type.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "idlharness.any.js": { + "skip": "track - still tentative / unsupported" + }, + "memory/constructor-types.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "memory/type.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "table/constructor-types.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "table/type.tentative.any.js": { + "skip": "track - still tentative / unsupported" + }, + "tag/type.tentative.any.js": { + "skip": "track - still tentative / unsupported" + } +} diff --git a/test/wpt/test-wasm-jsapi.mjs b/test/wpt/test-wasm-jsapi.mjs new file mode 100644 index 00000000000000..e4ebb9c53e7373 --- /dev/null +++ b/test/wpt/test-wasm-jsapi.mjs @@ -0,0 +1,21 @@ +// Flags: --experimental-wasm-modules +import * as fixtures from '../common/fixtures.mjs'; +import { ok } from 'node:assert'; +import { WPTRunner } from '../common/wpt.js'; + +// Verify we have Wasm SIMD support by importing a Wasm with SIMD +// since Wasm SIMD is not supported on older architectures such as IBM Power8. +let supportsSimd = false; +try { + await import(fixtures.fileURL('es-modules/globals.wasm')); + supportsSimd = true; +} catch (e) { + ok(e instanceof WebAssembly.CompileError); + ok(e.message.includes('SIMD unsupported')); +} + +if (supportsSimd) { + const runner = new WPTRunner('wasm/jsapi'); + runner.setFlags(['--experimental-wasm-modules']); + runner.runJsTests(); +}