From c032d7af834f5b28d1defc649fb09e901e790858 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 16 Nov 2021 15:52:47 -0600 Subject: [PATCH 1/4] Nuance the handling of imports in ESM-integration as suggested in #32 --- design/proposals/module-linking/Explainer.md | 75 ++++++++++++++------ 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/design/proposals/module-linking/Explainer.md b/design/proposals/module-linking/Explainer.md index 9e149cb..b32d0f9 100644 --- a/design/proposals/module-linking/Explainer.md +++ b/design/proposals/module-linking/Explainer.md @@ -833,6 +833,10 @@ WebAssembly.instantiateStreaming(fetch('./a.wasm'), { where `instantiateStreaming` checks that the module created from `code` exports a function `six` (and *may* import a function `five`). +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]. + ### ESM-integration @@ -841,34 +845,60 @@ 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 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. -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. +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. + +For imports of type `(global externref)` (i.e., an immutable core WebAssembly +global storing a value of type `externref`), the imported Module Record is +first converted to a [Namespace Object] via [`GetModuleNamespace()`] and then +converted to a `(global externref)` as a normal JavaScript value. This allows +the importing adapter module to dynamically access the fields of the +Namespace Object by calling other imported functions (such as `Object.get()`). +In the future, other compatible types (e.g., `handle` Interface Types) could be +included in this case. + +Otherwise, if the import cannot accept a Namespace Object (and would error +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. @@ -880,7 +910,13 @@ 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 +[`GetModuleNamespace()`]: https://tc39.es/ecma262/multipage/ecmascript-language-scripts-and-modules.html#sec-getmodulenamespace +[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 @@ -910,6 +946,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 From 4d2d5f09e284a23b2102f550b4f627d137eb6de1 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 19 Nov 2021 17:25:54 -0600 Subject: [PATCH 2/4] Add note about Module reflection JS APIs --- design/proposals/module-linking/Explainer.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/design/proposals/module-linking/Explainer.md b/design/proposals/module-linking/Explainer.md index b32d0f9..bd88125 100644 --- a/design/proposals/module-linking/Explainer.md +++ b/design/proposals/module-linking/Explainer.md @@ -833,6 +833,10 @@ 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]. From 8fb91abca5f8ea0617efd1fb9fda9c631728d03c Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 22 Nov 2021 11:55:27 -0600 Subject: [PATCH 3/4] Remove the special case for (global externref) --- design/proposals/module-linking/Explainer.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/design/proposals/module-linking/Explainer.md b/design/proposals/module-linking/Explainer.md index bd88125..8bfb3d4 100644 --- a/design/proposals/module-linking/Explainer.md +++ b/design/proposals/module-linking/Explainer.md @@ -878,17 +878,7 @@ 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. -For imports of type `(global externref)` (i.e., an immutable core WebAssembly -global storing a value of type `externref`), the imported Module Record is -first converted to a [Namespace Object] via [`GetModuleNamespace()`] and then -converted to a `(global externref)` as a normal JavaScript value. This allows -the importing adapter module to dynamically access the fields of the -Namespace Object by calling other imported functions (such as `Object.get()`). -In the future, other compatible types (e.g., `handle` Interface Types) could be -included in this case. - -Otherwise, if the import cannot accept a Namespace Object (and would error -otherwise), the import is treated like a JavaScript [Imported Default Binding] +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 From c9d5995f8b274050542acfd28d63ede2b66c0dfc Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 22 Nov 2021 11:57:28 -0600 Subject: [PATCH 4/4] Remove unused reference --- design/proposals/module-linking/Explainer.md | 1 - 1 file changed, 1 deletion(-) diff --git a/design/proposals/module-linking/Explainer.md b/design/proposals/module-linking/Explainer.md index 8bfb3d4..aaeefba 100644 --- a/design/proposals/module-linking/Explainer.md +++ b/design/proposals/module-linking/Explainer.md @@ -909,7 +909,6 @@ objects for exports as described above for the JS API. [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 -[`GetModuleNamespace()`]: https://tc39.es/ecma262/multipage/ecmascript-language-scripts-and-modules.html#sec-getmodulenamespace [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