Skip to content
This repository was archived by the owner on Aug 17, 2022. It is now read-only.

Nuance the handling of imports in ESM-integration #37

Merged
merged 4 commits into from
Nov 22, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 48 additions & 20 deletions design/proposals/module-linking/Explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,14 @@ WebAssembly.instantiateStreaming(fetch('./a.wasm'), {
where `instantiateStreaming` checks that the module created from `code` exports
a function `six` (and *may* import a function `five`).

Additionally, the JS API `WebAssembly.Module.imports()` and `exports()`
functions would need to be extended to include the new instance and module
types in the `kind` fields.

Lastly, considering the new exportable types, a module export would naturally
produce a `WebAssembly.Module` object. For an instance export, the JavaScript
correspondence already established by [ESM-integration] is a [Namespace Object].
Copy link
Collaborator

Choose a reason for hiding this comment

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

Interesting point, this implies the possibility of namespace construction for all WebAssembly.Instance objects right? This might require an ECMA262 spec change / WebAssembly JS API change to reflect all WebAssembly.Instance objects as having a Namespace Exotic Object representation that can be used / constructed.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good point. Technically, it would be the "exports object" (i.e., WebAssembly.Instance.exports) that we might want to reframe as a Namespace Object. IIRC, I think this was something we explicitly considered when defining the JS API in the first place and it's the reason for the final freezing step of creating the exports object (so that we could change it to a Namespace Object in the future without breaking existing code that depended on mutating the POJO). The reason for not doing this sooner was, again IIRC, that Namespace Objects weren't implemented at the point in time we were shipping the JS API.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I think it would be important to unify the interface now to ensure consistency with these representations. Was the plan to transparently upgrade "exports" or introduce a new property? Since the exports object is frozen it seems that should be possible, but potential edge cases include the prototype difference of the Namespace Exotic object (eg no exports.hasOwnProperty() so users doing that wouldn't work if exports changes to a namespace). It might still be safer to construct a new Instance.namespace in the JS API. Would it be worth getting an issue going for that?

Copy link
Member Author

Choose a reason for hiding this comment

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

My preference would be to try to upgrade exports in-place and see if anything breaks (say, by having a browser ship the upgrade in a pre-release channel for a while as an experiment). Filing an issue makes sense but, to be clear, I think this would be a general JS API change, independent of Module Linking. Since it's ESM-related, maybe the right repo is ESM-integration?



### ESM-integration

Expand All @@ -841,34 +849,50 @@ binaries in all the same places where core module binaries can be loaded today,
branching on the `layer` field in the binary format to determine the kind of
module to decode.

As with the JS API, the main question is how ESM-integration should deal with
single-level imports, instance imports and module imports. Going through these
one at a time:
As with the JS API, the main question for ESM-integration is how to deal with
all imports having a single string as well as the new instance and module
import/export types. Going through these:

For single-level imports of non-instance type, the natural analogue in an ESM
context is the [default export]. Thus, a single-level non-instance import would
receive whatever the ESM loader says the default value would be.
For adapter module imports of module type, we need a fundamentally new way to
request that the ESM loader parse or decode a module without *also*
instantiating that module. Recognizing this same need from JavaScript, there is
already a TC39 proposal called [Import Reflection] that adds the ability to
write, in JavaScript:
```js
import Foo from "./foo.wasm" as "wasm-module";
assert(Foo instanceof WebAssembly.Module);
```
With this extension to JavaScript and the ESM loader, an adapter module import
of module type can be treated like an `as "wasm-module"` import by
ESM-integration.

In all other cases, the (single) string imported by an adapter module import is
first resolved to a [Module Record] using the same process as resolving the
[Module Specifier] of a JavaScript `import`. After this, the handling of the
imported Module Record is determined by the import type:

For imports of instance type, the ESM loader would treat the exports of the
instance type as the imported field names. In the normal two-level
import-an-instance case, this corresponds exactly to existing ESM-integration
behavior. However, for more-deeply nested instances, the ESM loader would need
to recursively extract fields.

Lastly, for imports of modules, ESM-integration would need the ESM loader to
provide a fundamentally new way to parse/decode a module without *also*
instantiating that module. Since this functionality is also useful for JS,
there is already a proposal to add this to ESM called [Import Reflection].
With this proposal, a module import:
instance type as if they were the [Named Imports] of a JavaScript `import`.
Thus, single-level imports of instance type mirror the behavior of core
two-level imports with existing ESM-integration. Since the exports of an
instance type can themselves be instance types, this process must be performed
recursively.

Otherwise, the import is treated like a JavaScript [Imported Default Binding]
and the Module Record is converted to its default value. This allows, for
example, a single level import of a function:
```wasm
(adapter module
(import "./foo.wasm" (module $Foo))
(import "./foo.js" (func (result i32)))
)
```
could be handled by ESM-integration as-if loaded by:
to be satisfied by a JavaScript module via ESM-integration:
```js
import Foo from "./foo.wasm" as "wasm-module";
// foo.js
export default () => 42;
```
Lastly, for exports, ESM-integration would produce the same JavaScript
objects for exports as described above for the JS API.



Expand All @@ -880,7 +904,12 @@ import Foo from "./foo.wasm" as "wasm-module";
[Interface Types]: https://github.com/WebAssembly/interface-types

[ESM-integration]: https://github.com/WebAssembly/esm-integration
[Namespace Object]: https://tc39.es/ecma262/multipage/reflection.html#sec-module-namespace-objects
[Import Reflection]: https://github.com/tc39-transfer/proposal-import-reflection
[Module Record]: https://tc39.es/ecma262/#sec-abstract-module-records
[Module Specifier]: https://tc39.es/ecma262/multipage/ecmascript-language-scripts-and-modules.html#prod-ModuleSpecifier
[Named Imports]: https://tc39.es/ecma262/multipage/ecmascript-language-scripts-and-modules.html#prod-NamedImports
[Imported Default Binding]: https://tc39.es/ecma262/multipage/ecmascript-language-scripts-and-modules.html#prod-ImportedDefaultBinding

[Core Concepts]: https://webassembly.github.io/spec/core/intro/overview.html#concepts
[`typeuse`]: https://webassembly.github.io/spec/core/text/modules.html#type-uses
Expand Down Expand Up @@ -910,6 +939,5 @@ import Foo from "./foo.wasm" as "wasm-module";

[Figma plugins]: https://www.figma.com/blog/an-update-on-plugin-security/
[Attenuate]: http://cap-lore.com/CapTheory/Patterns/Attenuation.html
[Default Export]: https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export#Description

[Issue-30]: https://github.com/WebAssembly/module-linking/issues/30