| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -44,7 +44,7 @@ import.meta.exports[${nameStringLit}] = { | |||
| 44 | 44 | * @param {string} [url=''] - The URL of the module. | |
| 45 | 45 | * @param {(reflect: DynamicModuleReflect) => void} evaluate - The function to evaluate the module. | |
| 46 | 46 | * @typedef {object} DynamicModuleReflect | |
| 47 | - * @property {string[]} imports - The imports of the module. | ||
| 47 | + * @property {Record<string, Record<string, any>>} imports - The imports of the module. | ||
| 48 | 48 | * @property {string[]} exports - The exports of the module. | |
| 49 | 49 | * @property {(cb: (reflect: DynamicModuleReflect) => void) => void} onReady - Callback to evaluate the module. | |
| 50 | 50 | */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,16 +1,16 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | 3 | const { | |
| 4 | - ArrayPrototypeMap, | ||
| 5 | 4 | ArrayPrototypePush, | |
| 6 | 5 | FunctionPrototypeCall, | |
| 7 | 6 | JSONParse, | |
| 8 | - ObjectKeys, | ||
| 7 | + ObjectAssign, | ||
| 9 | 8 | ObjectPrototypeHasOwnProperty, | |
| 10 | 9 | ReflectApply, | |
| 11 | 10 | SafeArrayIterator, | |
| 12 | 11 | SafeMap, | |
| 13 | 12 | SafeSet, | |
| 13 | + SafeWeakMap, | ||
| 14 | 14 | StringPrototypeIncludes, | |
| 15 | 15 | StringPrototypeReplaceAll, | |
| 16 | 16 | StringPrototypeSlice, | |
@@ -484,6 +484,14 @@ translators.set('json', function jsonStrategy(url, source) { | |||
| 484 | 484 | }); | |
| 485 | 485 | ||
| 486 | 486 | // Strategy for loading a wasm module | |
| 487 | + // This logic should collapse into WebAssembly Module Record in future. | ||
| 488 | + /** | ||
| 489 | + * @type {WeakMap< | ||
| 490 | + * import('internal/modules/esm/utils').ModuleNamespaceObject, | ||
| 491 | + * WebAssembly.Instance | ||
| 492 | + * >} [[Instance]] slot proxy for WebAssembly Module Record | ||
| 493 | + */ | ||
| 494 | + const wasmInstances = new SafeWeakMap(); | ||
| 487 | 495 | translators.set('wasm', async function(url, source) { | |
| 488 | 496 | emitExperimentalWarning('Importing WebAssembly modules'); | |
| 489 | 497 | ||
@@ -502,19 +510,61 @@ translators.set('wasm', async function(url, source) { | |||
| 502 | 510 | throw err; | |
| 503 | 511 | } | |
| 504 | 512 | ||
| 505 | - const imports = | ||
| 506 | - ArrayPrototypeMap(WebAssembly.Module.imports(compiled), | ||
| 507 | - ({ module }) => module); | ||
| 508 | - const exports = | ||
| 509 | - ArrayPrototypeMap(WebAssembly.Module.exports(compiled), | ||
| 510 | - ({ name }) => name); | ||
| 513 | + const importsList = new SafeSet(); | ||
| 514 | + const wasmGlobalImports = []; | ||
| 515 | + for (const impt of WebAssembly.Module.imports(compiled)) { | ||
| 516 | + if (impt.kind === 'global') { | ||
| 517 | + ArrayPrototypePush(wasmGlobalImports, impt); | ||
| 518 | + } | ||
| 519 | + importsList.add(impt.module); | ||
| 520 | + } | ||
| 521 | + | ||
| 522 | + const exportsList = new SafeSet(); | ||
| 523 | + const wasmGlobalExports = new SafeSet(); | ||
| 524 | + for (const expt of WebAssembly.Module.exports(compiled)) { | ||
| 525 | + if (expt.kind === 'global') { | ||
| 526 | + wasmGlobalExports.add(expt.name); | ||
| 527 | + } | ||
| 528 | + exportsList.add(expt.name); | ||
| 529 | + } | ||
| 511 | 530 | ||
| 512 | - const createDynamicModule = require( | ||
| 513 | - 'internal/modules/esm/create_dynamic_module'); | ||
| 514 | - const { module } = createDynamicModule(imports, exports, url, (reflect) => { | ||
| 531 | + const createDynamicModule = require('internal/modules/esm/create_dynamic_module'); | ||
| 532 | + | ||
| 533 | + const { module } = createDynamicModule([...importsList], [...exportsList], url, (reflect) => { | ||
| 534 | + for (const impt of importsList) { | ||
| 535 | + const importNs = reflect.imports[impt]; | ||
| 536 | + const wasmInstance = wasmInstances.get(importNs); | ||
| 537 | + if (wasmInstance) { | ||
| 538 | + const wrappedModule = ObjectAssign({ __proto__: null }, reflect.imports[impt]); | ||
| 539 | + for (const { module, name } of wasmGlobalImports) { | ||
| 540 | + if (module !== impt) { | ||
| 541 | + continue; | ||
| 542 | + } | ||
| 543 | + // Import of Wasm module global -> get direct WebAssembly.Global wrapped value. | ||
| 544 | + // JS API validations otherwise remain the same. | ||
| 545 | + wrappedModule[name] = wasmInstance[name]; | ||
| 546 | + } | ||
| 547 | + reflect.imports[impt] = wrappedModule; | ||
| 548 | + } | ||
| 549 | + } | ||
| 550 | + // In cycles importing unexecuted Wasm, wasmInstance will be undefined, which will fail during | ||
| 551 | + // instantiation, since all bindings will be in the Temporal Deadzone (TDZ). | ||
| 515 | 552 | const { exports } = new WebAssembly.Instance(compiled, reflect.imports); | |
| 516 | - for (const expt of ObjectKeys(exports)) { | ||
| 517 | - reflect.exports[expt].set(exports[expt]); | ||
| 553 | + wasmInstances.set(module.getNamespace(), exports); | ||
| 554 | + for (const expt of exportsList) { | ||
| 555 | + let val = exports[expt]; | ||
| 556 | + // Unwrap WebAssembly.Global for JS bindings | ||
| 557 | + if (wasmGlobalExports.has(expt)) { | ||
| 558 | + try { | ||
| 559 | + // v128 will throw in GetGlobalValue, see: | ||
| 560 | + // https://webassembly.github.io/esm-integration/js-api/index.html#getglobalvalue | ||
| 561 | + val = val.value; | ||
| 562 | + } catch { | ||
| 563 | + // v128 doesn't support ToJsValue() -> use undefined (ideally should stay in TDZ) | ||
| 564 | + continue; | ||
| 565 | + } | ||
| 566 | + } | ||
| 567 | + reflect.exports[expt].set(val); | ||
| 518 | 568 | } | |
| 519 | 569 | }); | |
| 520 | 570 | // WebAssembly modules support source phase imports, to import the compiled module | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -52,7 +52,154 @@ describe('ESM: WASM modules', { concurrency: !process.env.TEST_PARALLEL }, () => | |||
| 52 | 52 | [ | |
| 53 | 53 | 'import { strictEqual } from "node:assert";', | |
| 54 | 54 | `import * as wasmExports from ${JSON.stringify(fixtures.fileURL('es-modules/export-name-syntax-error.wasm'))};`, | |
| 55 | - 'assert.strictEqual(wasmExports["?f!o:o<b>a[r]"]?.value, 12682);', | ||
| 55 | + 'assert.strictEqual(wasmExports["?f!o:o<b>a[r]"], 12682);', | ||
| 56 | + ].join('\n'), | ||
| 57 | + ]); | ||
| 58 | + | ||
| 59 | + strictEqual(stderr, ''); | ||
| 60 | + strictEqual(stdout, ''); | ||
| 61 | + strictEqual(code, 0); | ||
| 62 | + }); | ||
| 63 | + | ||
| 64 | + it('should properly handle all WebAssembly global types', async () => { | ||
| 65 | + const { code, stderr, stdout } = await spawnPromisified(execPath, [ | ||
| 66 | + '--no-warnings', | ||
| 67 | + '--experimental-wasm-modules', | ||
| 68 | + '--input-type=module', | ||
| 69 | + '--eval', | ||
| 70 | + [ | ||
| 71 | + 'import { strictEqual, deepStrictEqual, ok } from "node:assert";', | ||
| 72 | + | ||
| 73 | + // SIMD is not supported on rhel8-ppc64le | ||
| 74 | + 'let wasmExports;', | ||
| 75 | + 'try {', | ||
| 76 | + ` wasmExports = await import(${JSON.stringify(fixtures.fileURL('es-modules/globals.wasm'))});`, | ||
| 77 | + '} catch (e) {', | ||
| 78 | + ' ok(e instanceof WebAssembly.CompileError);', | ||
| 79 | + ' ok(e.message.includes("SIMD unsupported"));', | ||
| 80 | + '}', | ||
| 81 | + | ||
| 82 | + 'if (wasmExports) {', | ||
| 83 | + | ||
| 84 | + // Test imported globals using direct access | ||
| 85 | + ' strictEqual(wasmExports.importedI32, 42);', | ||
| 86 | + ' strictEqual(wasmExports.importedMutI32, 100);', | ||
| 87 | + ' strictEqual(wasmExports.importedI64, 9223372036854775807n);', | ||
| 88 | + ' strictEqual(wasmExports.importedMutI64, 200n);', | ||
| 89 | + ' strictEqual(Math.round(wasmExports.importedF32 * 100000) / 100000, 3.14159);', | ||
| 90 | + ' strictEqual(Math.round(wasmExports.importedMutF32 * 100000) / 100000, 2.71828);', | ||
| 91 | + ' strictEqual(wasmExports.importedF64, 3.141592653589793);', | ||
| 92 | + ' strictEqual(wasmExports.importedMutF64, 2.718281828459045);', | ||
| 93 | + ' strictEqual(wasmExports.importedExternref !== null, true);', | ||
| 94 | + ' strictEqual(wasmExports.importedMutExternref !== null, true);', | ||
| 95 | + ' strictEqual(wasmExports.importedNullExternref, null);', | ||
| 96 | + | ||
| 97 | + // Test local globals exported directly | ||
| 98 | + ' strictEqual(wasmExports[\'🚀localI32\'], 42);', | ||
| 99 | + ' strictEqual(wasmExports.localMutI32, 100);', | ||
| 100 | + ' strictEqual(wasmExports.localI64, 9223372036854775807n);', | ||
| 101 | + ' strictEqual(wasmExports.localMutI64, 200n);', | ||
| 102 | + ' strictEqual(Math.round(wasmExports.localF32 * 100000) / 100000, 3.14159);', | ||
| 103 | + ' strictEqual(Math.round(wasmExports.localMutF32 * 100000) / 100000, 2.71828);', | ||
| 104 | + ' strictEqual(wasmExports.localF64, 2.718281828459045);', | ||
| 105 | + ' strictEqual(wasmExports.localMutF64, 3.141592653589793);', | ||
| 106 | + | ||
| 107 | + // Test imported globals using getter functions | ||
| 108 | + ' strictEqual(wasmExports.getImportedMutI32(), 100);', | ||
| 109 | + ' strictEqual(wasmExports.getImportedMutI64(), 200n);', | ||
| 110 | + ' strictEqual(Math.round(wasmExports.getImportedMutF32() * 100000) / 100000, 2.71828);', | ||
| 111 | + ' strictEqual(wasmExports.getImportedMutF64(), 2.718281828459045);', | ||
| 112 | + ' strictEqual(wasmExports.getImportedMutExternref() !== null, true);', | ||
| 113 | + | ||
| 114 | + // Test local globals using getter functions | ||
| 115 | + ' strictEqual(wasmExports.getLocalMutI32(), 100);', | ||
| 116 | + ' strictEqual(wasmExports.getLocalMutI64(), 200n);', | ||
| 117 | + ' strictEqual(Math.round(wasmExports.getLocalMutF32() * 100000) / 100000, 2.71828);', | ||
| 118 | + ' strictEqual(wasmExports.getLocalMutF64(), 3.141592653589793);', | ||
| 119 | + ' strictEqual(wasmExports.getLocalMutExternref(), null);', | ||
| 120 | + | ||
| 121 | + ' assert.throws(wasmExports.getLocalMutV128);', | ||
| 122 | + | ||
| 123 | + // Pending TDZ support | ||
| 124 | + ' strictEqual(wasmExports.depV128, undefined);', | ||
| 125 | + | ||
| 126 | + // Test modifying mutable globals and reading the new values | ||
| 127 | + ' wasmExports.setImportedMutI32(999);', | ||
| 128 | + ' strictEqual(wasmExports.getImportedMutI32(), 999);', | ||
| 129 | + | ||
| 130 | + ' wasmExports.setImportedMutI64(888n);', | ||
| 131 | + ' strictEqual(wasmExports.getImportedMutI64(), 888n);', | ||
| 132 | + | ||
| 133 | + ' wasmExports.setImportedMutF32(7.77);', | ||
| 134 | + ' strictEqual(Math.round(wasmExports.getImportedMutF32() * 100) / 100, 7.77);', | ||
| 135 | + | ||
| 136 | + ' wasmExports.setImportedMutF64(6.66);', | ||
| 137 | + ' strictEqual(wasmExports.getImportedMutF64(), 6.66);', | ||
| 138 | + | ||
| 139 | + // Test modifying mutable externref | ||
| 140 | + ' const testObj = { test: "object" };', | ||
| 141 | + ' wasmExports.setImportedMutExternref(testObj);', | ||
| 142 | + ' strictEqual(wasmExports.getImportedMutExternref(), testObj);', | ||
| 143 | + | ||
| 144 | + // Test modifying local mutable globals | ||
| 145 | + ' wasmExports.setLocalMutI32(555);', | ||
| 146 | + ' strictEqual(wasmExports.getLocalMutI32(), 555);', | ||
| 147 | + | ||
| 148 | + ' wasmExports.setLocalMutI64(444n);', | ||
| 149 | + ' strictEqual(wasmExports.getLocalMutI64(), 444n);', | ||
| 150 | + | ||
| 151 | + ' wasmExports.setLocalMutF32(3.33);', | ||
| 152 | + ' strictEqual(Math.round(wasmExports.getLocalMutF32() * 100) / 100, 3.33);', | ||
| 153 | + | ||
| 154 | + ' wasmExports.setLocalMutF64(2.22);', | ||
| 155 | + ' strictEqual(wasmExports.getLocalMutF64(), 2.22);', | ||
| 156 | + | ||
| 157 | + // These mutating pending live bindings support | ||
| 158 | + ' strictEqual(wasmExports.localMutI32, 100);', | ||
| 159 | + ' strictEqual(wasmExports.localMutI64, 200n);', | ||
| 160 | + ' strictEqual(Math.round(wasmExports.localMutF32 * 100) / 100, 2.72);', | ||
| 161 | + ' strictEqual(wasmExports.localMutF64, 3.141592653589793);', | ||
| 162 | + | ||
| 163 | + // Test modifying local mutable externref | ||
| 164 | + ' const anotherTestObj = { another: "test object" };', | ||
| 165 | + ' wasmExports.setLocalMutExternref(anotherTestObj);', | ||
| 166 | + ' strictEqual(wasmExports.getLocalMutExternref(), anotherTestObj);', | ||
| 167 | + ' strictEqual(wasmExports.localMutExternref, null);', | ||
| 168 | + | ||
| 169 | + // Test dep.wasm imports | ||
| 170 | + ' strictEqual(wasmExports.depI32, 1001);', | ||
| 171 | + ' strictEqual(wasmExports.depMutI32, 2001);', | ||
| 172 | + ' strictEqual(wasmExports.getDepMutI32(), 2001);', | ||
| 173 | + ' strictEqual(wasmExports.depI64, 10000000001n);', | ||
| 174 | + ' strictEqual(wasmExports.depMutI64, 20000000001n);', | ||
| 175 | + ' strictEqual(wasmExports.getDepMutI64(), 20000000001n);', | ||
| 176 | + ' strictEqual(Math.round(wasmExports.depF32 * 100) / 100, 10.01);', | ||
| 177 | + ' strictEqual(Math.round(wasmExports.depMutF32 * 100) / 100, 20.01);', | ||
| 178 | + ' strictEqual(Math.round(wasmExports.getDepMutF32() * 100) / 100, 20.01);', | ||
| 179 | + ' strictEqual(wasmExports.depF64, 100.0001);', | ||
| 180 | + ' strictEqual(wasmExports.depMutF64, 200.0001);', | ||
| 181 | + ' strictEqual(wasmExports.getDepMutF64(), 200.0001);', | ||
| 182 | + | ||
| 183 | + // Test modifying dep.wasm mutable globals | ||
| 184 | + ' wasmExports.setDepMutI32(3001);', | ||
| 185 | + ' strictEqual(wasmExports.getDepMutI32(), 3001);', | ||
| 186 | + | ||
| 187 | + ' wasmExports.setDepMutI64(30000000001n);', | ||
| 188 | + ' strictEqual(wasmExports.getDepMutI64(), 30000000001n);', | ||
| 189 | + | ||
| 190 | + ' wasmExports.setDepMutF32(30.01);', | ||
| 191 | + ' strictEqual(Math.round(wasmExports.getDepMutF32() * 100) / 100, 30.01);', | ||
| 192 | + | ||
| 193 | + ' wasmExports.setDepMutF64(300.0001);', | ||
| 194 | + ' strictEqual(wasmExports.getDepMutF64(), 300.0001);', | ||
| 195 | + | ||
| 196 | + // These pending live bindings support | ||
| 197 | + ' strictEqual(wasmExports.depMutI32, 2001);', | ||
| 198 | + ' strictEqual(wasmExports.depMutI64, 20000000001n);', | ||
| 199 | + ' strictEqual(Math.round(wasmExports.depMutF32 * 100) / 100, 20.01);', | ||
| 200 | + ' strictEqual(wasmExports.depMutF64, 200.0001);', | ||
| 201 | + | ||
| 202 | + '}', | ||
| 56 | 203 | ].join('\n'), | |
| 57 | 204 | ]); | |
| 58 | 205 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,32 @@ | |||
| 1 | + (module | ||
| 2 | + (type (;0;) (func (result i32))) | ||
| 3 | + (type (;1;) (func (result i64))) | ||
| 4 | + (type (;2;) (func (result f32))) | ||
| 5 | + (type (;3;) (func (result f64))) | ||
| 6 | + (type (;4;) (func (result externref))) | ||
| 7 | + (type (;5;) (func (result v128))) | ||
| 8 | + (global $i32_value (;0;) i32 i32.const 1001) | ||
| 9 | + (global $i32_mut_value (;1;) (mut i32) i32.const 2001) | ||
| 10 | + (global $i64_value (;2;) i64 i64.const 10000000001) | ||
| 11 | + (global $i64_mut_value (;3;) (mut i64) i64.const 20000000001) | ||
| 12 | + (global $f32_value (;4;) f32 f32.const 0x1.4051ecp+3 (;=10.01;)) | ||
| 13 | + (global $f32_mut_value (;5;) (mut f32) f32.const 0x1.4028f6p+4 (;=20.01;)) | ||
| 14 | + (global $f64_value (;6;) f64 f64.const 0x1.90001a36e2eb2p+6 (;=100.0001;)) | ||
| 15 | + (global $f64_mut_value (;7;) (mut f64) f64.const 0x1.90000d1b71759p+7 (;=200.0001;)) | ||
| 16 | + (global $externref_value (;8;) externref ref.null extern) | ||
| 17 | + (global $externref_mut_value (;9;) (mut externref) ref.null extern) | ||
| 18 | + (global $v128_value (;10;) v128 v128.const i32x4 0x0000000a 0x00000014 0x0000001e 0x00000028) | ||
| 19 | + (global $v128_mut_value (;11;) (mut v128) v128.const i32x4 0x00000032 0x0000003c 0x00000046 0x00000050) | ||
| 20 | + (export "i32_value" (global $i32_value)) | ||
| 21 | + (export "i32_mut_value" (global $i32_mut_value)) | ||
| 22 | + (export "i64_value" (global $i64_value)) | ||
| 23 | + (export "i64_mut_value" (global $i64_mut_value)) | ||
| 24 | + (export "f32_value" (global $f32_value)) | ||
| 25 | + (export "f32_mut_value" (global $f32_mut_value)) | ||
| 26 | + (export "f64_value" (global $f64_value)) | ||
| 27 | + (export "f64_mut_value" (global $f64_mut_value)) | ||
| 28 | + (export "externref_value" (global $externref_value)) | ||
| 29 | + (export "externref_mut_value" (global $externref_mut_value)) | ||
| 30 | + (export "v128_value" (global $v128_value)) | ||
| 31 | + (export "v128_mut_value" (global $v128_mut_value)) | ||
| 32 | + ) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,18 @@ | |||
| 1 | + // globals.js - Direct global exports for WebAssembly imports | ||
| 2 | + | ||
| 3 | + // Immutable globals (simple values) | ||
| 4 | + const i32_value = 42; | ||
| 5 | + export { i32_value as '🚀i32_value' } | ||
| 6 | + export const i64_value = 9223372036854775807n; // Max i64 value | ||
| 7 | + export const f32_value = 3.14159; | ||
| 8 | + export const f64_value = 3.141592653589793; | ||
| 9 | + | ||
| 10 | + // Mutable globals with WebAssembly.Global wrapper | ||
| 11 | + export const i32_mut_value = new WebAssembly.Global({ value: 'i32', mutable: true }, 100); | ||
| 12 | + export const i64_mut_value = new WebAssembly.Global({ value: 'i64', mutable: true }, 200n); | ||
| 13 | + export const f32_mut_value = new WebAssembly.Global({ value: 'f32', mutable: true }, 2.71828); | ||
| 14 | + export const f64_mut_value = new WebAssembly.Global({ value: 'f64', mutable: true }, 2.718281828459045); | ||
| 15 | + | ||
| 16 | + export const externref_value = { hello: 'world' }; | ||
| 17 | + export const externref_mut_value = new WebAssembly.Global({ value: 'externref', mutable: true }, { mutable: 'global' }); | ||
| 18 | + export const null_externref_value = null; | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments