Skip to content

Wasm re-export bindings support #105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed

Conversation

guybedford
Copy link
Collaborator

@guybedford guybedford commented Mar 18, 2025

This is an addition to #104, split out from and based to that branch.

With this change, it is possible for Wasm modules to participate in reexports (export { binding } from './other.wasm') exactly the same as JS does, while supporting cycles for those reexports (although the hoisting of cyclic Wasm bindings is not included here).

In particular we classify Wasm exports that export an imported address as "indirect exports" (reexports), distinct from direct exports which are exports of bindings defined by that Wasm module's own instantiation.

With this, we then fully support the JS reexports logic through a complete ResolveExport implementation including resolving export * exports to JS with ambiguous logic and handling cycles, while still retaining the import snapshotting behaviour defined for Wasm imports.

Only direct exports and indirect exports to snapshotted JS imports then need to be defined in the environment record for the Wasm module record, avoiding unnecessary snapshotting and reducing the size of the environment record. A Wasm module reexporting a binding from another Wasm module can avoid an intermediate snapshot. When resolving an import from Wasm through a JS export { binding } from './other.wasm' module, we resolve that Wasm binding through JS reexports directly to the source binding in the other Wasm module, without the wrapping and unwrapping through JS requiring performing an intermediate wrapping of the value at the time the JS module was inspected.

This change also benefits the direct global unwrapping in #104 since in this case, a mutable global binding could be supported when reexported through JS per the example above, as resolving directly to its mutable global address in the final Wasm binding resolution per the example, whereas without this change it wouldn't be supported and would throw a linking error when trying to make a snapshot of the JS module exports (since Wasm wouldn't trace the reexport without the first-class ResolveExport implementation added in ~12 lines of spec text here).

While not completely trivial, it is fairly simple and falls out very naturally from the JS module integration semantics if we want to support it as a closer ESM integration implementation, so seems worth a discussion at least to me.

@rossberg
Copy link
Member

rossberg commented Apr 9, 2025

Yeah, as mentioned in our 1:1 chat today, the ability to depend on a module's internals like this would introduce a serious abstraction and encapsulation breach. In particular, it invalidates the existing design of modules-as-imports in the component model as well any similar future feature in core Wasm (which in turn is a prerequisite for eventually supporting module instantiation inside Wasm).

The problem is that module interface types as we have them today would become incomplete descriptions of a module's linking constraints — supplying a perfectly type-matching module to an import might still cause a link failure internally or downstream. That of course breaks the entire link-time checking discipline as intended for Wasm.

Any form of dependency on, or static observability of, the identity of exports for linking can only be allowed if that was reflected in module interface types somehow. That is, the necessary information must come from the module's interface type alone, not from inspecting a module's implementation internals. That in turn would require the introduction of a feature known in some of the module literature as "sharing constraints". Not impossible, and languages with SML/OCaml-style module systems feature that kind of thing, but it's a fairly significant complication that I think would require a much broader justification than just "nice to have for JS".

Edit: To summarise it more concisely, this breaks Wasm's module-level type system.

Base automatically changed from module-bindings to main April 10, 2025 16:32
@guybedford
Copy link
Collaborator Author

Thanks for giving some direct feedback here @rossberg I really appreciate it.

I completely agree that if this does not add anything, but potentially changes design constraints from a Wasm perspective, then it should not be landed, so I will go ahead and close this one.

@guybedford guybedford closed this Apr 10, 2025
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