Skip to content
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

Should ESM integration provide Modules rather than Instances? #63

Closed
takikawa opened this issue May 24, 2022 · 10 comments
Closed

Should ESM integration provide Modules rather than Instances? #63

takikawa opened this issue May 24, 2022 · 10 comments

Comments

@takikawa
Copy link
Collaborator

There's been some feedback provided recently that toolchains and bundlers wouldn't be able to take advantage of the current Wasm/ESM integration proposal design, because it focuses on providing instantiated Wasm modules to interoperate in the JS module graph.

Instead, it could be possible for the default mode of exporting Wasm modules into the JS module graph to be an uninstantiated module (a related idea was suggested in #14, which was about optionally exposing a special export for the module object). In that case, importing a module and instantiating a Wasm module using ESM might look like:

import mod from "foo.wasm" assert { type: "webassembly" };
WebAssembly.instantiate(mod, importObject);

This uses an import assertion requiring that the import is an uninstantiated "webassembly" module (this would be required for the same reason that it's required for JSON and CSS modules; they have different execution behavior than JS modules).

Alternatively, this could be done using the import reflection proposal (https://github.com/tc39/proposal-import-reflection), which would provide more flexibility to provide both (instantiated and uninstantiated) behaviors by using different reflection types. This would allow the current Wasm/ESM behavior to be either kept or deferred to the future.

I'd be interested in feedback from people working in toolchains and bundlers what would work better, and if there are cases where the original behavior of instantiating modules would be more useful.

@devsnek
Copy link
Member

devsnek commented May 24, 2022

I think the default should definitely be instantiated, that's sort of the point of hooking up the esm graph in the first place. The reflection proposal feels like the correct solution to this to me (if a solution is needed at all? I would personally use compileStreaming instead of import if i wanted a module and not an instance)

@Pauan
Copy link

Pauan commented May 24, 2022

There's been some feedback provided recently that toolchains and bundlers wouldn't be able to take advantage of the current Wasm/ESM integration proposal design

Do you have a link to that feedback? It would be good to get more specifics.

@lukewagner
Copy link
Member

lukewagner commented May 26, 2022

I think the feedback is not different than what was discussed in #44. However, as folks are starting to implement ESM-integration natively in engines and embeddings, it's important to ask: would this feature be practically adoptable by toolchains today and, if not, what would be adoptable today to get us out of the current state of using fetch() and/or base64-encoded strings (which are bundler/import-maps/toolchain-unfriendly). I think the root observation in #44, which I've come around to agreeing with, is that: every core wasm module is, in practice, wrapped by JS glue code that wants to control the WebAssembly.instantiate() call itself and thus only the ESM-import-a-WebAssembly.Module feature that would be immediately practically adoptable.

In the future, extensions to core wasm and/or the component-model should make the currently-proposed ESM-integration semantics (where instantiation is performed automatically by the ESM loader) more-imminently adoptable, so you could think of the proposed change of plan here as more of a sequencing change than a directional change.

Also, practically speaking, I think the actual spec and implementation work needed for ESM-importing a WebAssembly.Module is roughly a subset of ESM-integration, so much of the existing work and thought (viz., how the wasm gets loaded via the ESM loader) can be reused if we made this plan adjustment.

@kripken
Copy link
Member

kripken commented Jun 6, 2022

@takikawa

import mod from "foo.wasm" assert { type: "webassembly" }; WebAssembly.instantiate(mod, importObject);

Is this example intentionally using instantiate over instantiateStreaming? If so, would there be a downside to doing so compared to current toolchain code that uses instantiateStreaming today?

@lukewagner

I think the root observation in #44, which I've come around to agreeing with, is that: every core wasm module is, in practice, wrapped by JS glue code that wants to control the WebAssembly.instantiate() call itself and thus only the ESM-import-a-WebAssembly.Module feature that would be immediately practically adoptable.

+1, that is the case in Emscripten. We could easily adopt ESM integration if we still instantiate, but not otherwise.

@littledan
Copy link
Collaborator

Do we have any information about what Wasm toolchains who want modules rather than instances would gain vs using compileStreaming?

@Pauan
Copy link

Pauan commented Jun 6, 2022

Ideally toolchains would use instantiateStreaming, since that's more efficient than compileStreaming.

In that case, perhaps toolchains want something even more general, a way to fetch arbitrary resources:

import response from "./foo.wasm" assert { type: "fetch" };
WebAssembly.instantiateStreaming(response, importObject);

The semantics would be identical to fetch (it returns a Response object), except the URL is resolved according to the ESM machinery (instead of being global like fetch).

This would be useful for more than just Wasm: it can be used to fetch images, text, JSON, etc.

The benefit of this over fetch is that it is statically analyzable (so bundlers know which files need to be included in the bundle, and it can be integrated with HTTP/2 server push), and URLs are resolved relative to the JS module.

@devsnek
Copy link
Member

devsnek commented Jun 6, 2022

i don't think this is a bad idea but it is definitely an increase in the scope of esm and this proposal. you might be interested in stuff like https://github.com/tc39/proposal-asset-references. i think there may have been another proposal similar to that one but i don't remember the specifics.

@Pauan
Copy link

Pauan commented Jun 6, 2022

i don't think this is a bad idea but it is definitely an increase in the scope of esm and this proposal

I agree that it is outside of the scope of esm-integration. However, if a more general mechanism is created (in a different proposal), is there still a use case for "import as Module"? If not, then we can leave esm-integration as-is and toolchains can rely on the more general fetch mechanism instead.

@carlopi
Copy link

carlopi commented Jul 20, 2022

Sorry if I am late, sort of lost track of the discussion.

I think the root observation in #44, which I've come around to agreeing with, is that: every core wasm module is, in practice, wrapped by JS glue code that wants to control the WebAssembly.instantiate() call itself and thus only the ESM-import-a-WebAssembly.Module feature that would be immediately practically adoptable.

+1, that is the case in Emscripten. We could easily adopt ESM integration if we still instantiate, but not otherwise.

Same in the case of Cheerp, WebAssembly modules' instances are in the general case stateful and tightly coupled to some JS state, so it would be handy only controlling instantiation (via import reflections for example).

@guybedford
Copy link
Collaborator

This is now supported by the source phase in the current Phase 3 proposal.

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

No branches or pull requests

8 participants