Skip to content

Accept WebAssembly.Module values in the dynamic worker loader - #6997

Open
guybedford wants to merge 1 commit into
mainfrom
gbedford/wasm-module-serialization
Open

Accept WebAssembly.Module values in the dynamic worker loader#6997
guybedford wants to merge 1 commit into
mainfrom
gbedford/wasm-module-serialization

Conversation

@guybedford

@guybedford guybedford commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

This implements support for passing compiled WebAssembly.Module objects into the dynamic worker loader modules bag, allowing compiled Wasm modules to be shared with loaded workers without recompilation.

The motivating flow is source phase imports into dynamic workers:

import source mod from './lib.wasm';

const worker = env.LOADER.load({
  compatibilityDate: "2026-08-13",
  mainModule: "index.js",
  modules: {
    "index.js": `
      import source mod from './lib.wasm';
      export default { /* ... WebAssembly.instantiate(mod) ... */ };
    `,
    "lib.wasm": mod,
  },
});

What was implemented:

  • The worker loader modules bag accepts a WebAssembly.Module as a module value, or { wasm: module } alongside the existing { wasm: bytes } form.
  • WorkerSource::WasmModule carries an optional v8::CompiledWasmModule; both the legacy and new module registries use FromCompiledModule when present (the new registry seeds its existing compile cache), so the loaded worker shares compiled code with zero recompilation. The loader and loaded worker are always in the same process, which is exactly the boundary v8::CompiledWasmModule supports; the module wire bytes remain available as the module body.
  • jsg::V8Ref<v8::WasmModuleObject> is now unwrappable, with rtti and TypeScript type updates (Record<string, string | WebAssembly.Module | WorkerLoaderModule>).

Test coverage: end-to-end loader tests (source phase import → loader → import source + default import in the child, against both module registries, covering both accepted forms).

In future, dynamic import() could work directly against these module objects as well, per the ESM Phase Imports proposal.

@guybedford
guybedford requested review from a team as code owners August 13, 2026 23:19
@guybedford
guybedford requested a review from emily-shen August 13, 2026 23:19
@ask-bonk

This comment was marked as outdated.

2 similar comments
@ask-bonk

This comment was marked as outdated.

@ask-bonk

This comment was marked as outdated.

@ask-bonk

This comment was marked as outdated.

@codspeed-hq

codspeed-hq Bot commented Aug 14, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 72 untouched benchmarks
⏩ 129 skipped benchmarks1


Comparing gbedford/wasm-module-serialization (caf8765) with main (b6424d1)

Open in CodSpeed

Footnotes

  1. 129 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

A module in the worker loader modules bag may now be provided as a
WebAssembly.Module directly (e.g. obtained via a source phase import),
either as the module value itself or as { wasm: module }. The compiled
code is shared with the loaded worker via v8::CompiledWasmModule rather
than recompiling the wire bytes, in both the legacy and new module
registries. Inside the loaded worker the module can be imported with
import source, matching the ESM phase imports proposal semantics.

Includes jsg support for unwrapping v8::WasmModuleObject handles so
that jsg::V8Ref<v8::WasmModuleObject> can be used as a parameter and
struct field type, with rtti and TypeScript type updates.
@guybedford
guybedford force-pushed the gbedford/wasm-module-serialization branch from 7112e10 to caf8765 Compare August 17, 2026 21:57
@guybedford guybedford changed the title Wasm module serialization: structured clone + dynamic worker loader support Accept WebAssembly.Module values in the dynamic worker loader Aug 17, 2026
KJ_SWITCH_ONEOF(wasm) {
KJ_CASE_ONEOF(bytes, kj::Array<const byte>) {
// Same as `data` above: copy out of the V8 BackingStore before going async.
bytes = kj::heapArray<const kj::byte>(bytes.asPtr());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know these are fine but they do require a double take to verify they are safe... at first glance it looks like bytes ends up dangling. Maybe worth expanding the comment more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants