From c6acc6e5f2d42c18b82093a2159f2d1476caf19a Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 3 Mar 2025 23:22:07 -0800 Subject: [PATCH 01/21] Support direct global bindings --- document/core/appendix/embedding.rst | 91 ++++++++++++++++ document/core/syntax/modules.rst | 28 ++++- document/js-api/index.bs | 150 +++++++++++++++++++++----- proposals/esm-integration/EXAMPLES.md | 27 +++-- proposals/esm-integration/README.md | 30 +++--- 5 files changed, 273 insertions(+), 53 deletions(-) diff --git a/document/core/appendix/embedding.rst b/document/core/appendix/embedding.rst index 96fa6b2f..52415fe1 100644 --- a/document/core/appendix/embedding.rst +++ b/document/core/appendix/embedding.rst @@ -137,6 +137,37 @@ Modules \F{module\_validate}(m) &=& \ERROR && (\otherwise) \\ \end{array} +.. index:: matching, external type +.. _embed-extern-subtype: + +:math:`\F{module\_extern\_subtype}(\externtype_1, \externtype_2) : \bool` +....................................................................... + +1. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETFUNC~\functype_1` and :math:`\ETFUNC~\functype_2` respectively: + + a. Return true if and only if :math:`\vdashexterntypematch \ETFUNC~\functype_1 \matchesexterntype \ETFUNC~\functype_2`. + +2. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETTABLE~\tabletype_1` and :math:`\ETTABLE~\tabletype_2` respectively: + + a. Return true if and only if :math:`\vdashexterntypematch \ETTABLE~\tabletype_1 \matchesexterntype \ETTABLE~\tabletype_2`. + +3. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETMEM~\memtype_1` and :math:`\ETMEM~\memtype_2` respectively: + + a. Return true if and only if :math:`\vdashexterntypematch \ETMEM~\memtype_1 \matchesexterntype \ETMEM~\memtype_2`. + +4. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETGLOBAL~\globaltype_1` and :math:`\ETGLOBAL~\globaltype_2` respectively: + + a. Return true if and only if :math:`\vdashexterntypematch \ETGLOBAL~\globaltype_1 \matchesexterntype \ETGLOBAL~\globaltype_2`. + +5. Return false. + +.. math:: + \begin{array}{lclll} + \F{module\_extern\_subtype}(\externtype_1, \externtype_2) &=& \TRUE && (\iff \vdashexterntypematch \externtype_1 \matchesexterntype \externtype_2) \\ + \end{array} + +.. note:: + This function encapsulates the external type matching relation defined in the core specification. It allows for checking compatibility of external types when linking modules or validating imports against exports. The current implementation uses the exact matching rules from the core specification, but this function provides a single point for potential future extensions to the type system. .. index:: instantiation, module instance .. _embed-module-instantiate: @@ -218,6 +249,66 @@ Modules \end{array} +.. index:: direct export +.. _embed-direct-exports: + +:math:`\F{module\_direct\_exports}(\module) : (\name, \externtype)^\ast` +....................................................................... + +1. Pre-condition: :math:`\module` is :ref:`valid ` with external import types :math:`\externtype^\ast` and external export types :math:`{\externtype'}^\ast`. + +2. Let :math:`\export^\ast` be the :ref:`exports ` :math:`\module.\MEXPORTS`. + +3. Let :math:`\X{result}` be the empty sequence. + +4. For each :math:`\export_i` in :math:`\export^\ast` and corresponding :math:`\externtype'_i` in :math:`{\externtype'}^\ast`, do: + + a. If :math:`\isdirectexport(\module, \export_i.\EDESC)`, then append the pair :math:`(\export_i.\ENAME, \externtype'_i)` to :math:`\X{result}`. + +5. Return :math:`\X{result}`. + +.. math:: + ~ \\ + \begin{array}{lclll} + \F{module\_direct\_exports}(m) &=& (\X{ex}.\ENAME, \externtype')^\ast \\ + && \qquad (\iff \X{ex} \in m.\MEXPORTS \wedge \externtype' \in {\externtype'}^\ast \wedge {} \vdashmodule m : \externtype^\ast \to {\externtype'}^\ast \\ + && \qquad\quad \wedge \isdirectexport(m, \X{ex}.\EDESC)) \\ + \end{array} + + +.. index:: indirect export, re-export +.. _embed-indirect-exports: + +:math:`\F{module\_indirect\_exports}(\module) : (\name, \name, \name)^\ast` +................................................................ + +1. Pre-condition: :math:`\module` is :ref:`valid ` with external import types :math:`\externtype^\ast` and external export types :math:`{\externtype'}^\ast`. + +2. Let :math:`\import^\ast` be the :ref:`imports ` :math:`\module.\MIMPORTS`. + +3. Let :math:`\export^\ast` be the :ref:`exports ` :math:`\module.\MEXPORTS`. + +4. Let :math:`\X{result}` be the empty sequence. + +5. For each :math:`\export_i` in :math:`\export^\ast`, do: + + a. If :math:`\isindirectexport(\module, \export_i.\EDESC)`, then: + + i. Let :math:`\import_j` be the import corresponding to the index in :math:`\export_i.\EDESC`. + + ii. Append the triple :math:`(\export_i.\ENAME, \import_j.\IMODULE, \import_j.\INAME)` to :math:`\X{result}`. + +6. Return :math:`\X{result}`. + +.. math:: + ~ \\ + \begin{array}{lclll} + \F{module\_indirect\_exports}(m) &=& (\X{ex}.\ENAME, \X{im}.\IMODULE, \X{im}.\INAME)^\ast \\ + && \qquad (\iff \X{ex} \in m.\MEXPORTS \wedge \X{im} \in m.\MIMPORTS \wedge {} \vdashmodule m : \externtype^\ast \to {\externtype'}^\ast \\ + && \qquad\quad \wedge \isindirectexport(m, \X{ex}.\EDESC) \wedge \X{im} = \importforexport(m, \X{ex}.\EDESC)) \\ + \end{array} + + .. index:: module, module instance .. _embed-instance: diff --git a/document/core/syntax/modules.rst b/document/core/syntax/modules.rst index ec32935c..3ec300fb 100644 --- a/document/core/syntax/modules.rst +++ b/document/core/syntax/modules.rst @@ -327,7 +327,7 @@ The |MSTART| component of a module declares the :ref:`function index `. Exportable definitions are :ref:`functions `, :ref:`tables `, :ref:`memories `, and :ref:`globals `, which are referenced through a respective descriptor. +A *direct export* is one where the export references a function, table, memory, or global instance that is defined within the module itself rather than being imported. + +An *indirect export* (or *re-export*) is one where the export references a function, table, memory, or global that the module imports. + +The import corresponding to an export description is given by: + +.. math:: + \begin{array}{lllll} + \production{import for export} & \importforexport(m, \exportdesc) &=& + \begin{cases} + m.\MIMPORTS[\X{idx}] & (\iff \exportdesc = \EDFUNC~\X{idx} \wedge \X{idx} < |\etfuncs(m.\MIMPORTS)|) \\ + m.\MIMPORTS[\X{fidx} + \X{idx}] & (\iff \exportdesc = \EDTABLE~\X{idx} \wedge \X{idx} < |\ettables(m.\MIMPORTS)| \\ + & \quad \wedge \X{fidx} = |\etfuncs(m.\MIMPORTS)|) \\ + m.\MIMPORTS[\X{fidx} + \X{tidx} + \X{idx}] & (\iff \exportdesc = \EDMEM~\X{idx} \wedge \X{idx} < |\etmems(m.\MIMPORTS)| \\ + & \quad \wedge \X{fidx} = |\etfuncs(m.\MIMPORTS)| \wedge \X{tidx} = |\ettables(m.\MIMPORTS)|) \\ + m.\MIMPORTS[\X{fidx} + \X{tidx} + \X{midx} + \X{idx}] & (\iff \exportdesc = \EDGLOBAL~\X{idx} \wedge \X{idx} < |\etglobals(m.\MIMPORTS)| \\ + & \quad \wedge \X{fidx} = |\etfuncs(m.\MIMPORTS)| \wedge \X{tidx} = |\ettables(m.\MIMPORTS)| \\ + & \quad \wedge \X{midx} = |\etmems(m.\MIMPORTS)|) \\ + \epsilon & (\otherwise) \\ + \end{cases} \\ + \end{array} + +For embedder convenience, we also define: + +* :math:``\isdirectexport(m, \exportdesc) = (\importforexport(m, \exportdesc) = \epsilon)` +* :math:``\isindirectexport(m, \exportdesc) = (\importforexport(m, \exportdesc) \neq \epsilon)` Conventions ........... diff --git a/document/js-api/index.bs b/document/js-api/index.bs index eeb9ab38..c7283995 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -123,9 +123,12 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df text: store_init; url: appendix/embedding.html#embed-store-init text: module_decode; url: appendix/embedding.html#embed-module-decode text: module_validate; url: appendix/embedding.html#embed-module-validate + text: module_extern_subtype; url: appendix/embedding.html#embed-module-extern-subtype text: module_instantiate; url: appendix/embedding.html#embed-module-instantiate text: module_imports; url: appendix/embedding.html#embed-module-imports text: module_exports; url: appendix/embedding.html#embed-module-exports + text: module_direct_exports; url: appendix/embedding.html#embed-module-direct-exports + text: module_inirect_exports; url: appendix/embedding.html#embed-module-indirect-exports text: instance_export; url: appendix/embedding.html#embed-instance-export text: func_alloc; url: appendix/embedding.html#embed-func-alloc text: func_type; url: appendix/embedding.html#embed-func-type @@ -1163,7 +1166,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. Let |values| be [=?=] [$IteratorToList$]([=?=] [$GetIteratorFromMethod$](|ret|, |method|)). 1. Let |wasmValues| be a new, empty [=list=]. 1. If |values|'s [=list/size=] is not |resultsSize|, throw a {{TypeError}} exception. - 1. For each |value| and |resultType| in |values| and |results|, paired linearly, + 1. [=list/iterate|For each=] |value| and |resultType| in |values| and |results|, paired linearly, 1. [=list/Append=] [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmValues|. 1. Return |wasmValues|. @@ -1388,9 +1391,11 @@ To parse a WebAssembly module given a byte sequence |by 1. If |module| is [=error=], throw a {{CompileError}} exception. 1. [=Construct a WebAssembly module object=] from |module| and |bytes|, and let |module| be the result. 1. Let |requestedModules| be a set. -1. For each (|moduleName|, name, type) in [=module_imports=](|module|.\[[Module]]), +1. [=list/iterate|For each=] (|moduleName|, name, type) in [=module_imports=](|module|.\[[Module]]), 1. [=set/Append=] |moduleName| to |requestedModules|. 1. Let |moduleRecord| be { + + \[[Instance]]: ~empty~, \[[Realm]]: |realm|, \[[Environment]]: ~empty~, @@ -1422,7 +1427,7 @@ The export name list of a WebAssembly Module Record |record| is defin 1. Let |module| be |record|'s \[[ModuleSource]] internal slot. 1. Let |exports| be an empty [=list=]. -1. For each (|name|, type) in [=module_exports=](|module|.\[[Module]]) +1. [=list/iterate|For each=] (|name|, type) in [=module_exports=](|module|.\[[Module]]) 1. [=list/Append=] |name| to the end of |exports|. 1. Return |exports|. @@ -1440,8 +1445,24 @@ WebAssembly Module Records have the following methods:
-

ResolveExport ( |exportName|, resolveSet ) Concrete Method

+

ResolveExport ( |exportName|, |resolveSet| ) Concrete Method

+1. If |resolveSet| is not present, set |resolveSet| to « ». 1. Let |record| be this WebAssembly Module Record. +1. Let |module| be |record|.\[[ModuleSource]].\[[Module]]. +1. [=list/iterate|For each=] Record |r| of |resolveSet|, + 1. If |record| and |r|.\[[Module]] are the same Module Record and |exportName| is |r|.\[[ExportName]], + 1. Return null. +1. Append the record { \[[Module]]: |record|, [[ExportName]]: |exportName| } to |resolveSet|. +1. [=list/iterate|For each=] (|name|, |importedModuleName|, |importName|, type) in [=module_indirect_exports=](|module|), + 1. If |name| is equal to |exportName|, + 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). + 1. Let |resolved| be [=?=] |importedModule|.ResolveExport(|importName|, |resolveSet|). + 1. If |resolved| is null or ~AMBIGUOUS~, + 1. Return |resolved|. + 1. If |resolved|.\[[Module]] is a WebAssembly Module Record, + 1. Return |resolved|. + 1. Note: This fall-through case allows indirect exports referencing JS values to be treated as captured direct bindings in the environment record, + with live bindings unsupported. Only live bindings between Wasm globals are supported for Wasm exports. 1. If the [=export name list=] of |record| contains |exportName|, return { \[[Module]]: |record|, \[[BindingName]]: |exportName| }. 1. Otherwise, return null. @@ -1460,8 +1481,16 @@ WebAssembly Module Records have the following methods: 1. Let |record| be this WebAssembly Module Record. 1. Let |env| be [$NewModuleEnvironment$](null). 1. Set |record|.\[[Environment]] to |env|. -1. For each |name| in the [=export name list=] of |record|, - 1. Perform ! |env|.CreateImmutableBinding(|name|, true). +1. Let |module| be |record|.\[[ModuleSource]].\[[Module]]. +1. [=list/iterate|For each=] (|importedModuleName|, |name|, |type|) in [=module_imports=](|module|), + 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). + 1. Let |resolution| be |importedModule|.ResolveExport(|name|). + 1. If |resolution| is null or ~AMBIGUOUS~, throw a {{SyntaxError}} exception. +1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_direct_exports=](|module|), + 1. If |type| is of the form [=global=] [=var=] |valtype|, + 1. Perform [=!=] |env|.CreateMutableBinding(|name|, false). + 1. Otherwise, + 1. Perform [=!=] |env|.CreateImmutableBinding(|name|, true).
@@ -1470,36 +1499,105 @@ WebAssembly Module Records have the following methods:

ExecuteModule ( [ |promiseCapability| ] ) Concrete Method

1. Assert: |promiseCapability| was not provided. 1. Let |record| be this WebAssembly Module Record. -1. Let |module| be |record|.\[[ModuleSource]]. -1. Let |imports| be a new, empty [=map=]. -1. For each (|importedModuleName|, |name|, type) in [=module_imports=](|module|.\[[Module]]), - 1. If |imports|[|importedModuleName|] does not exist, set |imports|[|importedModuleName|] to a new, empty [=map=]. +1. Let |module| be |record|.\[[ModuleSource]]\.[[Module]]. +1. Let |imports| be « ». +1. [=list/iterate|For each=] (|importedModuleName|, |name|, |importtype|) in [=module_imports=](|module|), 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). - 1. Let |value| be ? |importedModule|.\[[Environment]].GetBindingValue(|name|, true). - 1. Set |imports|[|importedModuleName|][|name|] to |value|. -1. Let |importObject| be ! [$OrdinaryObjectCreate$](null). -1. For each |key| → |value| of |imports|, - 1. Let |moduleImportsObject| be ! [$OrdinaryObjectCreate$](null). - 1. For each |importedName| → |importedValue| of |value|, - 1. Perform ! [$CreateDataPropertyOrThrow$](|moduleImportsObject|, |importedName|, |importedValue|). - 1. Perform ! [$CreateDataPropertyOrThrow$](|importObject|, |key|, |moduleImportsObject|). -1. [=Read the imports=] of |module| with imports |importObject|, and let |imports| be the result. + 1. Let |resolution| be |importedModule|.ResolveExport(|name|). + 1. If |resolution|.\[[Module]] is a WebAssembly Module Record, + 1. Let |resolutionInstance| be |resolution|.\[[Module]].\[[Instance]]. + 1. Let |resolutionModule| be |resolution|.\[[Module]].\[[ModuleSource]]\.[[Module]]. + 1. Let |resolutionName| be |resolution|.\[[BindingName]]. + 1. Let |externval| be [=instance_export=](|resolutionInstance|, |resolutionName|). + 1. Assert: |externval| is not [=error=]. + 1. Assert: [=module_direct_exports=](|resolutionModule|) contains an element (|resolutionName|, type). + 1. Let |externtype| be the value of |type| for the element (|resolutionName|, |type|) in [=module_direct_exports=](|resolutionModule|). + 1. If [=module_extern_subtype=](|externtype|, |importtype|) is false, throw a {{LinkError}} exception. + 1. [=list/Append=] |externval| to |imports|. + 1. Otherwise, + 1. Let |env| be |resolution|.\[[Module]].\[[Environment]]. + 1. Let |v| be [=?=] |env|.GetBindingValue(|resolution|.\[[BindingName]], true). + 1. If |importtype| is of the form [=func=] |functype|, + 1. If [$IsCallable$](|v|) is false, throw a {{LinkError}} exception. + 1. If |v| has a \[[FunctionAddress]] internal slot, and therefore is an [=Exported Function=], + 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. + 1. Otherwise, + 1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result. + 1. Let |index| be the number of external functions in |imports|. This value |index| is known as the index of the host function |funcaddr|. + 1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|. + 1. [=list/Append=] |externfunc| to |imports|. + 1. If |importtype| is of the form [=global=] |mut| |valtype|, + 1. If |v| [=implements=] {{Global}}, + 1. Let |globaladdr| be |v|.\[[Global]]. + 1. Otherwise, + 1. If |valtype| is [=v128=], + 1. Throw a {{LinkError}} exception. + 1. Let |value| be [=?=] [=ToWebAssemblyValue=](|v|, |valtype|). + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, |mut| |valtype|, |value|). + 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. + 1. Let |externglobal| be [=external value|global=] |globaladdr|. + 1. [=list/Append=] |externglobal| to |imports|. + 1. If |importtype| is of the form [=mem=] memtype, + 1. If |v| does not [=implement=] {{Memory}}, throw a {{LinkError}} exception. + 1. Let |externmem| be the [=external value=] [=external value|mem=] |v|.\[[Memory]]. + 1. [=list/Append=] |externmem| to |imports|. + 1. If |importtype| is of the form [=table=] tabletype, + 1. If |v| does not [=implement=] {{Table}}, throw a {{LinkError}} exception. + 1. Let |tableaddr| be |v|.\[[Table]]. + 1. Let |externtable| be the [=external value=] [=external value|table=] |tableaddr|. + 1. [=list/Append=] |externtable| to |imports|. 1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |instance| be the result. -1. For each |name| in the [=export name list=] of |record|, - 1. Perform ! |record|.\[[Environment]].InitializeBinding(|name|, ! Get(|instance|.\[[Exports]], |name|)). - - Note: exported bindings are left uninitialized, i.e., in TDZ. +1. Set |record|.\[[Instance]] to |instance|. +1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_direct_exports=](|module|), + 1. Let |externval| be [=instance_export=](|instance|, |name|). + 1. Assert: |externval| is not [=error=]. + 1. If |externtype| is of the form [=func=] functype, + 1. Assert: |externval| is of the form [=external value|func=] |funcaddr|. + 1. Let [=external value|func=] |funcaddr| be |externval|. + 1. Let |func| be the result of creating [=a new Exported Function=] from |funcaddr|. + 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |func|). + 1. If |externtype| is of the form [=global=] |mut| |globaltype|, + 1. Assert: |externval| is of the form [=external value|global=] |globaladdr|. + 1. Let [=external value|global=] |globaladdr| be |externval|. + 1. Let |global_value| be [=global_read=](|store|, |globaladdr|). + 1. If |globaltype| is not [=v128=], + 1. Note: When integrating with shared globals, they will be excluded here similarly to v128 above. + 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, [=ToJSValue=](|global_value|)). + 1. Associate all future mutations to the mutable value at |globaladdr| with the ECMA-262 binding record for |name| in + |record|.\[[Environment]], such that |record|.\[[Environment]].GetBindingValue(|resolution|.\[[BindingName]], true) + always returns [=ToJSValue=]([=global_read=](|store|, |globaladdr|)) for the current [=surrounding agent=]'s + [=associated store=] |store|. + 1. If |externtype| is of the form [=mem=] memtype, + 1. Assert: |externval| is of the form [=external value|mem=] |memaddr|. + 1. Let [=external value|mem=] |memaddr| be |externval|. + 1. Let |memory| be [=create a memory object|a new Memory object=] created from |memaddr|. + 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |memory|). + 1. If |externtype| is of the form [=table=] tabletype, + 1. Assert: |externval| is of the form [=external value|table=] |tableaddr|. + 1. Let [=external value|table=] |tableaddr| be |externval|. + 1. Let |table| be [=create a Table object|a new Table object=] created from |tableaddr|. + 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |table|). + +Note: The export bindings for a WebAssembly Module Record are designed to mirror the live bindings of all directly exported values. Values not +supported in JS are left uninitialized and in TDZ, while still being accesible to Wasm importers. Indirect exports (re-exports) referencing +JavaScript values will capture the [=ToWebAssemblyValue=] interpretation at the time of execution, while indirect exports resolving to other Wasm +modules will support Wasm live bindings. For exported mutable globals that are supported in JavaScript, the live bindings on the environment record +will always reflect the current mutable global export value as interpreted through the infallible [=ToJSValue=] interpretation.

HostGetModuleSourceModuleRecord ( |specifier| )

1. If |specifier| is a WebAssembly {{Module}} object with a \[[Module]] internal slot, - 1. If |specifier|.\[[Module]].\[[ModuleRecord]] is null, + 1. Let |module| be |specifier|.\[[Module]]. + 1. If |module|.\[[ModuleRecord]] is null, 1. Let |realm| be the current agent's realm record. 1. Let |requestedModules| be a set. - 1. For each (|moduleName|, name, type) in [=module_imports=](|specifier|.\[[Module]]), + 1. [=list/iterate|For each=] (|moduleName|, name, type) in [=module_imports=](|module|), 1. [=set/Append=] |moduleName| to |requestedModules|. 1. Let |moduleRecord| be { + + \[[Instance]]: ~empty~, \[[Realm]]: |realm|, \[[Environment]]: ~empty~, @@ -1522,7 +1620,7 @@ WebAssembly Module Records have the following methods: }. 1. Set |module|.\[[ModuleRecord]] to |moduleRecord|. 1. Return |moduleRecord|. - 1. Return |specifier|.\[[Module]].\[[ModuleRecord]]. + 1. Return |module|.\[[ModuleRecord]]. 1. Return ~not-a-source~. Note: See corresponding modifications to HTML in PR #10380. diff --git a/proposals/esm-integration/EXAMPLES.md b/proposals/esm-integration/EXAMPLES.md index 64a6b879..b3600d58 100644 --- a/proposals/esm-integration/EXAMPLES.md +++ b/proposals/esm-integration/EXAMPLES.md @@ -81,6 +81,15 @@ export const count = new WebAssembly.Global({ }, 42); ``` +Note that counter.js could equivalently be written: + +```js +// counter.js +export let count = 42; +``` + +Which would still be picked up as a global import value, although changes to the JS value would not affect the WebAssembly global value (live bindings are only supported for Wasm module exports). + ##### External type imports ```wasm @@ -109,13 +118,11 @@ for (let index = 0; index < length; index++) | export type | imported value | |-------------|---------------------------| -| global | `WebAssembly.Global` object | +| global | The corresponding JS value for the global | | memory | `WebAssembly.Memory` object | | table | `WebAssembly.Table` object | | function | WebAssembly exported function | -Wasm bindings cannot be reassigned as it can in JS, so the exported value will not change in their object identity. But the value that it points to (e.g. `.value` in the case of `WebAssembly.Global`) can change. - 1. JS module is parsed 1. wasm module is parsed 1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ. @@ -123,16 +130,14 @@ Wasm bindings cannot be reassigned as it can in JS, so the exported value will n 1. wasm module is instantiated evaluated. Functions are initialized. Memories and tables are initialized and filled with data/elem sections. Globals are initialized and initializer expressions are evaluated. The start function runs. 1. JS module is evaluated. All values are available. -Currently, the value of the export for something like `WebAssembly.Global` would be accessed using the `.value` property on the JS object. However, when host bindings are in place, these could be annotated with a host binding that turns it into a real live binding that points directly to the value's address. - #### Example ```js // main.js import {count, increment} from "./counter.wasm"; -console.log(count.value); // logs 5 +console.log(count); // logs 5 increment(); -console.log(count.value); // logs 6 +console.log(count); // logs 6 ``` ```wasm ;; counter.wat --> counter.wasm @@ -172,7 +177,7 @@ Wasm exports can be imported as accurate, immutable bindings to other wasm modul ### wasm imports <- JS re-exports <- wasm exports -Any wasm exports that are re-exported via a JS module will be available to the other wasm module as accurate, immutable bindings. The wasm export gets wrapped into a JS object (e.g. `WebAssembly.Global`) and then unwrapped to the wasm import in the importing wasm module. When imported from JS, the first and second Wasm modules will yield the same object identities for the multiply exported Wasm objects. +Any wasm exports that are re-exported via a JS module will be available to the other wasm module as accurate bindings. If the binding is a mutable global, then that binding will be a live export binding in JS reflecting changes to the Wasm global value. When imported from JS, the first and second Wasm modules will yield the same Wasm identities for the multiply exported Wasm objects. #### Example @@ -208,7 +213,7 @@ export {memoryExport} from "./b.wasm"; 1. JS module is parsed 1. wasm module is parsed -1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ. +1. wasm module has a lexical environment created for its exports. All direct exports are initially in TDZ, indirect exports are available as they resolve. 1. JS module is instantiated. All imports (including functions) from the wasm module are memory locations holding undefined. 1. wasm module is instantiated and evaluated. Snapshots of imports are taken. Export bindings are initialized. 1. JS module is evaluated. @@ -246,8 +251,8 @@ export function functionExport() { 1. wasm module is parsed 1. JS module is parsed 1. JS module is instantiated. -1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ. -1. JS module is evaluated. wasm exports lead to a ReferenceError if used. +1. wasm module has a lexical environment created for its exports. All direct exports are initially in TDZ, indirect exports are available as they resolve. +1. JS module is evaluated. wasm exports in TDZ lead to a ReferenceError if used. 1. wasm module is instantiated and evaluated; wasm-exported bindings are updated to their appropriate JS API-exposed values. #### Examples diff --git a/proposals/esm-integration/README.md b/proposals/esm-integration/README.md index 2695f27f..4a403843 100644 --- a/proposals/esm-integration/README.md +++ b/proposals/esm-integration/README.md @@ -94,23 +94,22 @@ In the proposed HTML integration of WebAssembly modules, the [module name](https ### "Snapshotting" imports -When imports are provided to WebAssembly modules in the host instance linking model, they are provided directly upfront. +When imports are provided to WebAssembly modules in the host instance linking model from JavaScript, they are provided directly upfront. - This handling of imports could be called a "snapshotting" process: Later updates to the imported values won't be reflected within the WebAssembly module. -- Circular WebAssembly modules are not supported: One of them will run first, and that one will find that the exports of the other aren't yet initialized, leading to a ReferenceError. +- Circular WebAssembly module bindings are not supported for direct exports: One of them will run first, and that one will find that the exports of the other aren't yet initialized, leading to a ReferenceError. Indirect exports (reexports) can be supported early in Wasm cycles, where a WebAssembly module exports the same binding it imports. See the FAQ for more explanation of the rationale for this design decision, and what features it enables which would be difficult or impossible otherwise. -### Progressive Implementation Support +### Live binding exports -It is possible to implement the Wasm-ESM integration in two stages: +WebAssembly modules support live exports bindings for mutable globals by reflecting the infallible ToJsValue() representation of their mutable global exports on their JavaScript environment bindings record. -1. In the first stage only source phase imports of Wasm are supported (`import source fibModule from "./fib.wasm"`). -2. In the second stage, evaluation phase imports would be supported too (`import { fib } from "./fib.wasm"`). +For bindings that are exported from bindings that were initially imported i.e. indirect exports or reexports: -If initially implementing just source phase imports, the `GetExportedNames`, `ResolveExport`, `InitializeEnvironment`, and `ExecuteModule` abstract operations can be implemented as abstract operations unconditionally throwing a `SyntaxError` exception. In this case, module fetch and CSP integration is still required to be implemented as specified in this proposal. +1. WebAssembly modules that have indirect exports to JavaScript modules, these values are snapshotted at the time of execution of the WebAssembly module and reflected as captured bindings and not live bindings on the exports, even if the underlying JavaScript module might have modifications to the binding later on. -Implementers are encouraged to ship both stages at once, but it is deemed OK for implementers to initially ship the first stage and then quickly follow up with the second stage, if this aids "time to ship" in implementations. +2. WebAssembly modules that have indirect exports to WebAssembly modules, where those modules directly export mutable globals, support the live mutable global reference without capturing the import. ## FAQ @@ -120,7 +119,7 @@ Originally the ESM integration only provided the direct host instance linking mo Supporting the [source phase](https://github.com/tc39/proposal-source-phase-imports) ESM integration is therefore a more general form of the ESM integration that shares the host resolver, while retaining linking flexibility for Js host embedding of Wasm. -While the source phase does not replace the instance linking model, is does offer a more general and flexible ESM integration. +While the source phase does not replace the instance linking model, is does offer a more general and flexible ESM integration as an addition. ### How does this relate to the Component Model? @@ -150,7 +149,7 @@ The conversion is based on the type that the import and export was declared as, ### When one WebAssembly module imports another one, will there be overhead due to converting back and forth to JS values? -Note that exports of ES Module Records always have values that can be directly treated as JavaScript values. Although we're talking about conversions to and from JavaScript for these exports, it's expected that, in native implementations, the conversion to and from Javascript would "cancel out" and not lead to the use of wrappers in practice. +Bindings between WebAssembly modules, even those that are indirect through indirect exports (reexports) are directly linked, without needing to go through JS wrapping and unwrapping. ### Why are WebAssembly modules instantiated during the "Evaluation" phase? @@ -172,9 +171,11 @@ Instead of including this check in the default semantics of functions, a trampol ### What does snapshotting imports mean for importing globals? -When a WebAssembly module imports a Global, there are two possible modes of operation: -- If the Global type is immutable (as declared in the importing module), then the exporting module may either export a numeric value or an immutable Global. -- If the Global type is mutable, then the exporting module must export a mutable Global. The snapshot here is "shallow" in the sense that modifications *within* this particular mutable Global object *will* be visible in the importing module (but, if the exporting module overwrites the entire binding with some unrelated value, this will not be noticed by the importing module). +Imports are only snapshotted when they resolve to a JavaScript module. For imports that resolve to WebAssembly modules, these are always directly bound. + +When a WebAssembly module imports a Global, that resolves to a module after resolving the binding through any indirect exports (reexports): +- If the resolved module is a JavaScript module, then the exporting module may either export a direct value or a `WebAssembly.Global` of the same type. +- If the resolved module is a WebAssembly module, then the exporting module must export a global that is an extern subtype of the importing global. ### Can Web APIs be imported via modules? @@ -192,5 +193,4 @@ If custom compilation options are needed or if custom streams need to be provide ### Where is the specification for this proposal? -If you want to dig into the details, see [the updated WebAssembly JS API](https://webassembly.github.io/esm-integration/js-api/index.html#esm-integration) and [the proposed HTML integration PR](https://github.com/whatwg/html/pull/4372). - +If you want to dig into the details, see [the updated WebAssembly JS API](https://webassembly.github.io/esm-integration/js-api/index.html#esm-integration) and [th HTML integration PR](https://github.com/whatwg/html/pull/10380). From 88ef520f9e2efcb43e263b1507378978cf275b7a Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 13 Mar 2025 14:40:54 -0700 Subject: [PATCH 02/21] fixup --- document/core/appendix/embedding.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/document/core/appendix/embedding.rst b/document/core/appendix/embedding.rst index 52415fe1..3b22650e 100644 --- a/document/core/appendix/embedding.rst +++ b/document/core/appendix/embedding.rst @@ -141,7 +141,7 @@ Modules .. _embed-extern-subtype: :math:`\F{module\_extern\_subtype}(\externtype_1, \externtype_2) : \bool` -....................................................................... +......................................................................... 1. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETFUNC~\functype_1` and :math:`\ETFUNC~\functype_2` respectively: @@ -253,7 +253,7 @@ Modules .. _embed-direct-exports: :math:`\F{module\_direct\_exports}(\module) : (\name, \externtype)^\ast` -....................................................................... +........................................................................ 1. Pre-condition: :math:`\module` is :ref:`valid ` with external import types :math:`\externtype^\ast` and external export types :math:`{\externtype'}^\ast`. @@ -280,7 +280,7 @@ Modules .. _embed-indirect-exports: :math:`\F{module\_indirect\_exports}(\module) : (\name, \name, \name)^\ast` -................................................................ +........................................................................... 1. Pre-condition: :math:`\module` is :ref:`valid ` with external import types :math:`\externtype^\ast` and external export types :math:`{\externtype'}^\ast`. From 3ff289168f40e8f34ef2e8c92dd23b329fbc79c3 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 13 Mar 2025 14:58:21 -0700 Subject: [PATCH 03/21] fixup --- document/js-api/index.bs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index c7283995..e6b3f601 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1452,7 +1452,7 @@ WebAssembly Module Records have the following methods: 1. [=list/iterate|For each=] Record |r| of |resolveSet|, 1. If |record| and |r|.\[[Module]] are the same Module Record and |exportName| is |r|.\[[ExportName]], 1. Return null. -1. Append the record { \[[Module]]: |record|, [[ExportName]]: |exportName| } to |resolveSet|. +1. Append the record { \[[Module]]: |record|, \[[ExportName]]: |exportName| } to |resolveSet|. 1. [=list/iterate|For each=] (|name|, |importedModuleName|, |importName|, type) in [=module_indirect_exports=](|module|), 1. If |name| is equal to |exportName|, 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). @@ -1482,12 +1482,19 @@ WebAssembly Module Records have the following methods: 1. Let |env| be [$NewModuleEnvironment$](null). 1. Set |record|.\[[Environment]] to |env|. 1. Let |module| be |record|.\[[ModuleSource]].\[[Module]]. -1. [=list/iterate|For each=] (|importedModuleName|, |name|, |type|) in [=module_imports=](|module|), +1. [=list/iterate|For each=] (|importedModuleName|, |name|, type) in [=module_imports=](|module|), 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). 1. Let |resolution| be |importedModule|.ResolveExport(|name|). 1. If |resolution| is null or ~AMBIGUOUS~, throw a {{SyntaxError}} exception. +1. [=list/iterate|For each=] (|name|, |importedModuleName|, |importName|, type) in [=module_indirect_exports=](|module|), + 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). + 1. Let |resolved| be [=?=] |importedModule|.ResolveExport(|importName|). + 1. Assert |resolved| is not null or ~AMBIGUOUS~. + 1. If |resolved|.\[[Module]] is not a WebAssembly Module Record, + 1. Note: This case corresponds to indirect exports to non-WebAssembly bindings, which are always snapshotted. + 1. Perform [=!=] |env|.CreateImmutableBinding(|name|, true). 1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_direct_exports=](|module|), - 1. If |type| is of the form [=global=] [=var=] |valtype|, + 1. If |externtype| is of the form [=global=] [=var=] valtype, 1. Perform [=!=] |env|.CreateMutableBinding(|name|, false). 1. Otherwise, 1. Perform [=!=] |env|.CreateImmutableBinding(|name|, true). @@ -1499,14 +1506,14 @@ WebAssembly Module Records have the following methods:

ExecuteModule ( [ |promiseCapability| ] ) Concrete Method

1. Assert: |promiseCapability| was not provided. 1. Let |record| be this WebAssembly Module Record. -1. Let |module| be |record|.\[[ModuleSource]]\.[[Module]]. +1. Let |module| be |record|.\[[ModuleSource]].\[[Module]]. 1. Let |imports| be « ». 1. [=list/iterate|For each=] (|importedModuleName|, |name|, |importtype|) in [=module_imports=](|module|), 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). 1. Let |resolution| be |importedModule|.ResolveExport(|name|). 1. If |resolution|.\[[Module]] is a WebAssembly Module Record, 1. Let |resolutionInstance| be |resolution|.\[[Module]].\[[Instance]]. - 1. Let |resolutionModule| be |resolution|.\[[Module]].\[[ModuleSource]]\.[[Module]]. + 1. Let |resolutionModule| be |resolution|.\[[Module]].\[[ModuleSource]].\[[Module]]. 1. Let |resolutionName| be |resolution|.\[[BindingName]]. 1. Let |externval| be [=instance_export=](|resolutionInstance|, |resolutionName|). 1. Assert: |externval| is not [=error=]. @@ -1523,7 +1530,7 @@ WebAssembly Module Records have the following methods: 1. Let |funcaddr| be the value of |v|'s \[[FunctionAddress]] internal slot. 1. Otherwise, 1. [=Create a host function=] from |v| and |functype|, and let |funcaddr| be the result. - 1. Let |index| be the number of external functions in |imports|. This value |index| is known as the index of the host function |funcaddr|. + 1. Let index be the number of external functions in |imports|, defining the [=index of the host function=] |funcaddr|. 1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|. 1. [=list/Append=] |externfunc| to |imports|. 1. If |importtype| is of the form [=global=] |mut| |valtype|, @@ -1565,9 +1572,9 @@ WebAssembly Module Records have the following methods: 1. Note: When integrating with shared globals, they will be excluded here similarly to v128 above. 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, [=ToJSValue=](|global_value|)). 1. Associate all future mutations to the mutable value at |globaladdr| with the ECMA-262 binding record for |name| in - |record|.\[[Environment]], such that |record|.\[[Environment]].GetBindingValue(|resolution|.\[[BindingName]], true) - always returns [=ToJSValue=]([=global_read=](|store|, |globaladdr|)) for the current [=surrounding agent=]'s - [=associated store=] |store|. + |record|.\[[Environment]], such that |record|.\[[Environment]].GetBindingValue(|resolution|.\[[BindingName]], true) + always returns [=ToJSValue=]([=global_read=](|store|, |globaladdr|)) for the current [=surrounding agent=]'s + [=associated store=] |store|. 1. If |externtype| is of the form [=mem=] memtype, 1. Assert: |externval| is of the form [=external value|mem=] |memaddr|. 1. Let [=external value|mem=] |memaddr| be |externval|. From 60130509ade6864113708e473cf0399070d34a92 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 13 Mar 2025 18:57:01 -0700 Subject: [PATCH 04/21] fixup macro --- document/core/util/macros.def | 1 + 1 file changed, 1 insertion(+) diff --git a/document/core/util/macros.def b/document/core/util/macros.def index 06b60fd6..e90097d3 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -346,6 +346,7 @@ .. |edtables| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{tables}} .. |edmems| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{mems}} .. |edglobals| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{globals}} +.. |importforexport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{import\_for\_export}} .. Instructions, terminals From aeeba9bc76c1770bfd848d69a8207660296c8e73 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 13 Mar 2025 19:00:30 -0700 Subject: [PATCH 05/21] fixup definitions --- document/core/syntax/modules.rst | 4 ++-- document/core/util/macros.def | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/document/core/syntax/modules.rst b/document/core/syntax/modules.rst index 3ec300fb..d2c2e05b 100644 --- a/document/core/syntax/modules.rst +++ b/document/core/syntax/modules.rst @@ -380,8 +380,8 @@ The import corresponding to an export description is given by: For embedder convenience, we also define: -* :math:``\isdirectexport(m, \exportdesc) = (\importforexport(m, \exportdesc) = \epsilon)` -* :math:``\isindirectexport(m, \exportdesc) = (\importforexport(m, \exportdesc) \neq \epsilon)` +* :math:`\isdirectexport(m, \exportdesc) = (\importforexport(m, \exportdesc) = \epsilon)` +* :math:`\isindirectexport(m, \exportdesc) = (\importforexport(m, \exportdesc) \neq \epsilon)` Conventions ........... diff --git a/document/core/util/macros.def b/document/core/util/macros.def index e90097d3..eac2d256 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -347,6 +347,8 @@ .. |edmems| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{mems}} .. |edglobals| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{globals}} .. |importforexport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{import\_for\_export}} +.. |isdirectexport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{is\_direct\_export}} +.. |isindirectexport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{is\_indirect\_export}} .. Instructions, terminals From 8daa1653be63dfead09bebdc66aba427bef03296 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 13 Mar 2025 21:05:35 -0700 Subject: [PATCH 06/21] rework definitions --- document/core/appendix/embedding.rst | 26 +++++++++++++--------- document/core/syntax/modules.rst | 32 +++++++++++----------------- document/core/util/macros.def | 4 +--- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/document/core/appendix/embedding.rst b/document/core/appendix/embedding.rst index 3b22650e..95bf7870 100644 --- a/document/core/appendix/embedding.rst +++ b/document/core/appendix/embedding.rst @@ -163,11 +163,11 @@ Modules .. math:: \begin{array}{lclll} - \F{module\_extern\_subtype}(\externtype_1, \externtype_2) &=& \TRUE && (\iff \vdashexterntypematch \externtype_1 \matchesexterntype \externtype_2) \\ + \F{module\_extern\_subtype}(\externtype_1, \externtype_2) &\iff& \vdashexterntypematch \externtype_1 \matchesexterntype \externtype_2 \\ \end{array} .. note:: - This function encapsulates the external type matching relation defined in the core specification. It allows for checking compatibility of external types when linking modules or validating imports against exports. The current implementation uses the exact matching rules from the core specification, but this function provides a single point for potential future extensions to the type system. + This function encapsulates the external type matching relation defined in the :ref:`Import Subtyping ` of the validation section, where the :math:`\vdashexterntypematch \externtype_1 \matchesexterntype \externtype_2` judgment establishes compatibility between external types. This allows for explicit checking of type compatibility when linking modules or validating imports against exports. .. index:: instantiation, module instance .. _embed-module-instantiate: @@ -263,7 +263,9 @@ Modules 4. For each :math:`\export_i` in :math:`\export^\ast` and corresponding :math:`\externtype'_i` in :math:`{\externtype'}^\ast`, do: - a. If :math:`\isdirectexport(\module, \export_i.\EDESC)`, then append the pair :math:`(\export_i.\ENAME, \externtype'_i)` to :math:`\X{result}`. + a. Let :math:`\import_j = \exportimport(\module, \export_i.\EDESC)`. + + b. If :math:`\import_j = \epsilon`, then append the pair :math:`(\export_i.\ENAME, \externtype'_i)` to :math:`\X{result}`. 5. Return :math:`\X{result}`. @@ -271,8 +273,10 @@ Modules ~ \\ \begin{array}{lclll} \F{module\_direct\_exports}(m) &=& (\X{ex}.\ENAME, \externtype')^\ast \\ - && \qquad (\iff \X{ex} \in m.\MEXPORTS \wedge \externtype' \in {\externtype'}^\ast \wedge {} \vdashmodule m : \externtype^\ast \to {\externtype'}^\ast \\ - && \qquad\quad \wedge \isdirectexport(m, \X{ex}.\EDESC)) \\ + && \qquad (\iff \X{ex}^\ast = m.\MEXPORTS \\ + && \qquad\quad \wedge~\exportimport(m, \X{ex}.\EDESC) = \epsilon \\ + && \qquad\quad \wedge~\vdashmodule m : \externtype^\ast \to {\externtype'}^\ast \\ + && \qquad\quad \wedge~\externtype' = \X{ex}.\EDESC) \\ \end{array} @@ -292,11 +296,11 @@ Modules 5. For each :math:`\export_i` in :math:`\export^\ast`, do: - a. If :math:`\isindirectexport(\module, \export_i.\EDESC)`, then: + a. Let :math:`\import_j = \exportimport(\module, \export_i.\EDESC)`. - i. Let :math:`\import_j` be the import corresponding to the index in :math:`\export_i.\EDESC`. + b. If :math:`\import_j \neq \epsilon`, then: - ii. Append the triple :math:`(\export_i.\ENAME, \import_j.\IMODULE, \import_j.\INAME)` to :math:`\X{result}`. + i. Append the triple :math:`(\export_i.\ENAME, \import_j.\IMODULE, \import_j.\INAME)` to :math:`\X{result}`. 6. Return :math:`\X{result}`. @@ -304,8 +308,10 @@ Modules ~ \\ \begin{array}{lclll} \F{module\_indirect\_exports}(m) &=& (\X{ex}.\ENAME, \X{im}.\IMODULE, \X{im}.\INAME)^\ast \\ - && \qquad (\iff \X{ex} \in m.\MEXPORTS \wedge \X{im} \in m.\MIMPORTS \wedge {} \vdashmodule m : \externtype^\ast \to {\externtype'}^\ast \\ - && \qquad\quad \wedge \isindirectexport(m, \X{ex}.\EDESC) \wedge \X{im} = \importforexport(m, \X{ex}.\EDESC)) \\ + && \qquad (\iff \X{ex}^\ast = m.\MEXPORTS \\ + && \qquad\quad \wedge~\X{im} = \exportimport(m, \X{ex}.\EDESC) \\ + && \qquad\quad \wedge~\X{im} \neq \epsilon \\ + && \qquad\quad \wedge~\vdashmodule m : \externtype^\ast \to {\externtype'}^\ast) \\ \end{array} diff --git a/document/core/syntax/modules.rst b/document/core/syntax/modules.rst index d2c2e05b..a89a6665 100644 --- a/document/core/syntax/modules.rst +++ b/document/core/syntax/modules.rst @@ -356,32 +356,26 @@ Each export is labeled by a unique :ref:`name `. Exportable definitions are :ref:`functions `, :ref:`tables `, :ref:`memories `, and :ref:`globals `, which are referenced through a respective descriptor. -A *direct export* is one where the export references a function, table, memory, or global instance that is defined within the module itself rather than being imported. +A *direct export* is one where the export references a function, table, memory, or global instance that is defined within the module itself rather than being imported. An *indirect export* (or *re-export*) is one where the export references a function, table, memory, or global that the module imports. -The import corresponding to an export description is given by: +For an export :math:`\export` in module :math:`m`, the export is direct when :math:`\exportimport(m, \export.\EDESC) = \epsilon` and indirect otherwise, where +the import corresponding to an export description is defined by: .. math:: - \begin{array}{lllll} - \production{import for export} & \importforexport(m, \exportdesc) &=& - \begin{cases} - m.\MIMPORTS[\X{idx}] & (\iff \exportdesc = \EDFUNC~\X{idx} \wedge \X{idx} < |\etfuncs(m.\MIMPORTS)|) \\ - m.\MIMPORTS[\X{fidx} + \X{idx}] & (\iff \exportdesc = \EDTABLE~\X{idx} \wedge \X{idx} < |\ettables(m.\MIMPORTS)| \\ - & \quad \wedge \X{fidx} = |\etfuncs(m.\MIMPORTS)|) \\ - m.\MIMPORTS[\X{fidx} + \X{tidx} + \X{idx}] & (\iff \exportdesc = \EDMEM~\X{idx} \wedge \X{idx} < |\etmems(m.\MIMPORTS)| \\ - & \quad \wedge \X{fidx} = |\etfuncs(m.\MIMPORTS)| \wedge \X{tidx} = |\ettables(m.\MIMPORTS)|) \\ - m.\MIMPORTS[\X{fidx} + \X{tidx} + \X{midx} + \X{idx}] & (\iff \exportdesc = \EDGLOBAL~\X{idx} \wedge \X{idx} < |\etglobals(m.\MIMPORTS)| \\ - & \quad \wedge \X{fidx} = |\etfuncs(m.\MIMPORTS)| \wedge \X{tidx} = |\ettables(m.\MIMPORTS)| \\ - & \quad \wedge \X{midx} = |\etmems(m.\MIMPORTS)|) \\ - \epsilon & (\otherwise) \\ - \end{cases} \\ + \begin{array}{lclll} + \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDFUNC~\funcidx \\ + &&&& \quad \wedge~\exists~i~\colon \ m.\MIMPORTS[i].\IDESC = \IDFUNC~\typeidx \\ + \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDTABLE~\tableidx \\ + &&&& \quad \wedge~\exists~i~\colon \ m.\MIMPORTS[i].\IDESC = \IDTABLE~\tabletype \\ + \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDMEM~\memidx \\ + &&&& \quad \wedge~\exists~i~\colon \ m.\MIMPORTS[i].\IDESC = \IDMEM~\memtype \\ + \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDGLOBAL~\globalidx \\ + &&&& \quad \wedge~\exists~i~\colon \ m.\MIMPORTS[i].\IDESC = \IDGLOBAL~\globaltype \\ + \F{exportimport}(m, \exportdesc) &=& \epsilon && (\otherwise) \\ \end{array} -For embedder convenience, we also define: - -* :math:`\isdirectexport(m, \exportdesc) = (\importforexport(m, \exportdesc) = \epsilon)` -* :math:`\isindirectexport(m, \exportdesc) = (\importforexport(m, \exportdesc) \neq \epsilon)` Conventions ........... diff --git a/document/core/util/macros.def b/document/core/util/macros.def index eac2d256..a6d21b2c 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -346,9 +346,7 @@ .. |edtables| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{tables}} .. |edmems| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{mems}} .. |edglobals| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{globals}} -.. |importforexport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{import\_for\_export}} -.. |isdirectexport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{is\_direct\_export}} -.. |isindirectexport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{is\_indirect\_export}} +.. |exportimport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{import\_for\_export}} .. Instructions, terminals From 19fac541c892a663b0f41a1a1d9fa7ca2b4bfcd1 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 14 Mar 2025 11:22:31 -0700 Subject: [PATCH 07/21] fix exportimport descriptor --- document/core/appendix/embedding.rst | 8 ++++---- document/core/syntax/modules.rst | 16 ++++++++++------ document/core/util/macros.def | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/document/core/appendix/embedding.rst b/document/core/appendix/embedding.rst index 95bf7870..674336d8 100644 --- a/document/core/appendix/embedding.rst +++ b/document/core/appendix/embedding.rst @@ -263,7 +263,7 @@ Modules 4. For each :math:`\export_i` in :math:`\export^\ast` and corresponding :math:`\externtype'_i` in :math:`{\externtype'}^\ast`, do: - a. Let :math:`\import_j = \exportimport(\module, \export_i.\EDESC)`. + a. Let :math:`\import_j = \edexportimport(\module, \export_i.\EDESC)`. b. If :math:`\import_j = \epsilon`, then append the pair :math:`(\export_i.\ENAME, \externtype'_i)` to :math:`\X{result}`. @@ -274,7 +274,7 @@ Modules \begin{array}{lclll} \F{module\_direct\_exports}(m) &=& (\X{ex}.\ENAME, \externtype')^\ast \\ && \qquad (\iff \X{ex}^\ast = m.\MEXPORTS \\ - && \qquad\quad \wedge~\exportimport(m, \X{ex}.\EDESC) = \epsilon \\ + && \qquad\quad \wedge~\edexportimport(m, \X{ex}.\EDESC) = \epsilon \\ && \qquad\quad \wedge~\vdashmodule m : \externtype^\ast \to {\externtype'}^\ast \\ && \qquad\quad \wedge~\externtype' = \X{ex}.\EDESC) \\ \end{array} @@ -296,7 +296,7 @@ Modules 5. For each :math:`\export_i` in :math:`\export^\ast`, do: - a. Let :math:`\import_j = \exportimport(\module, \export_i.\EDESC)`. + a. Let :math:`\import_j = \edexportimport(\module, \export_i.\EDESC)`. b. If :math:`\import_j \neq \epsilon`, then: @@ -309,7 +309,7 @@ Modules \begin{array}{lclll} \F{module\_indirect\_exports}(m) &=& (\X{ex}.\ENAME, \X{im}.\IMODULE, \X{im}.\INAME)^\ast \\ && \qquad (\iff \X{ex}^\ast = m.\MEXPORTS \\ - && \qquad\quad \wedge~\X{im} = \exportimport(m, \X{ex}.\EDESC) \\ + && \qquad\quad \wedge~\X{im} = \edexportimport(m, \X{ex}.\EDESC) \\ && \qquad\quad \wedge~\X{im} \neq \epsilon \\ && \qquad\quad \wedge~\vdashmodule m : \externtype^\ast \to {\externtype'}^\ast) \\ \end{array} diff --git a/document/core/syntax/modules.rst b/document/core/syntax/modules.rst index a89a6665..f5307a32 100644 --- a/document/core/syntax/modules.rst +++ b/document/core/syntax/modules.rst @@ -360,19 +360,23 @@ A *direct export* is one where the export references a function, table, memory, An *indirect export* (or *re-export*) is one where the export references a function, table, memory, or global that the module imports. -For an export :math:`\export` in module :math:`m`, the export is direct when :math:`\exportimport(m, \export.\EDESC) = \epsilon` and indirect otherwise, where -the import corresponding to an export description is defined by: +For an export :math:`\export` in module :math:`m`, the export is direct when :math:`\edexportimport(m, \export.\EDESC) = \epsilon` and indirect otherwise, where +the import corresponding to an export descriptor is defined by: .. math:: \begin{array}{lclll} \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDFUNC~\funcidx \\ - &&&& \quad \wedge~\exists~i~\colon \ m.\MIMPORTS[i].\IDESC = \IDFUNC~\typeidx \\ + &&&& \quad \wedge~\exists~i~\colon~\funcidx = |\{j ~|~ j < i \\ + &&&& \quad \quad \wedge~m.\MIMPORTS[j].\IDESC = \IDFUNC~\typeidx' \}|) \\ \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDTABLE~\tableidx \\ - &&&& \quad \wedge~\exists~i~\colon \ m.\MIMPORTS[i].\IDESC = \IDTABLE~\tabletype \\ + &&&& \quad \wedge~\exists~i~\colon~\tableidx = |\{j ~|~ j < i \\ + &&&& \quad \quad \wedge~m.\MIMPORTS[j].\IDESC = \IDTABLE~\tabletype' \}|) \\ \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDMEM~\memidx \\ - &&&& \quad \wedge~\exists~i~\colon \ m.\MIMPORTS[i].\IDESC = \IDMEM~\memtype \\ + &&&& \quad \wedge~\exists~i~\colon~\memidx = |\{j ~|~ j < i \\ + &&&& \quad \quad \wedge~m.\MIMPORTS[j].\IDESC = \IDMEM~\memtype' \}|) \\ \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDGLOBAL~\globalidx \\ - &&&& \quad \wedge~\exists~i~\colon \ m.\MIMPORTS[i].\IDESC = \IDGLOBAL~\globaltype \\ + &&&& \quad \wedge~\exists~i~\colon~\globalidx = |\{j ~|~ j < i \\ + &&&& \quad \quad \wedge~m.\MIMPORTS[j].\IDESC = \IDGLOBAL~\globaltype' \}|) \\ \F{exportimport}(m, \exportdesc) &=& \epsilon && (\otherwise) \\ \end{array} diff --git a/document/core/util/macros.def b/document/core/util/macros.def index a6d21b2c..3ee1edab 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -346,7 +346,7 @@ .. |edtables| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{tables}} .. |edmems| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{mems}} .. |edglobals| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{globals}} -.. |exportimport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{import\_for\_export}} +.. |edexportimport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{exportimport}} .. Instructions, terminals From e639f62be3cac775d4a8ecd4cb3d06d0b88d7fb3 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 14 Mar 2025 13:41:00 -0700 Subject: [PATCH 08/21] wording --- document/core/syntax/modules.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/document/core/syntax/modules.rst b/document/core/syntax/modules.rst index f5307a32..8c95451c 100644 --- a/document/core/syntax/modules.rst +++ b/document/core/syntax/modules.rst @@ -356,12 +356,12 @@ Each export is labeled by a unique :ref:`name `. Exportable definitions are :ref:`functions `, :ref:`tables `, :ref:`memories `, and :ref:`globals `, which are referenced through a respective descriptor. -A *direct export* is one where the export references a function, table, memory, or global instance that is defined within the module itself rather than being imported. +A *direct export* is one where the export references a function, table, memory, or global that is defined within the module itself rather than being imported. An *indirect export* (or *re-export*) is one where the export references a function, table, memory, or global that the module imports. For an export :math:`\export` in module :math:`m`, the export is direct when :math:`\edexportimport(m, \export.\EDESC) = \epsilon` and indirect otherwise, where -the import corresponding to an export descriptor is defined by: +the import corresponding to an export descriptor :math:`\export.\EDESC` is defined by: .. math:: \begin{array}{lclll} From 1641b98549158c47d114e926733494c7c325b2f0 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 14 Mar 2025 14:25:36 -0700 Subject: [PATCH 09/21] fixup wasm cycles --- document/js-api/index.bs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index e6b3f601..d0c6776a 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1513,6 +1513,8 @@ WebAssembly Module Records have the following methods: 1. Let |resolution| be |importedModule|.ResolveExport(|name|). 1. If |resolution|.\[[Module]] is a WebAssembly Module Record, 1. Let |resolutionInstance| be |resolution|.\[[Module]].\[[Instance]]. + 1. If |resolutionInstance| is ~empty~ then, + 1. Throw a {ReferenceError}. 1. Let |resolutionModule| be |resolution|.\[[Module]].\[[ModuleSource]].\[[Module]]. 1. Let |resolutionName| be |resolution|.\[[BindingName]]. 1. Let |externval| be [=instance_export=](|resolutionInstance|, |resolutionName|). From 6e393ee15db68aa566013e33bc8f841f3fef180b Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 16 Mar 2025 12:01:32 -0700 Subject: [PATCH 10/21] wip --- document/core/appendix/embedding.rst | 97 ---------------------------- document/core/syntax/modules.rst | 26 +------- document/core/util/macros.def | 1 - document/js-api/index.bs | 44 +------------ 4 files changed, 4 insertions(+), 164 deletions(-) diff --git a/document/core/appendix/embedding.rst b/document/core/appendix/embedding.rst index 674336d8..96fa6b2f 100644 --- a/document/core/appendix/embedding.rst +++ b/document/core/appendix/embedding.rst @@ -137,37 +137,6 @@ Modules \F{module\_validate}(m) &=& \ERROR && (\otherwise) \\ \end{array} -.. index:: matching, external type -.. _embed-extern-subtype: - -:math:`\F{module\_extern\_subtype}(\externtype_1, \externtype_2) : \bool` -......................................................................... - -1. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETFUNC~\functype_1` and :math:`\ETFUNC~\functype_2` respectively: - - a. Return true if and only if :math:`\vdashexterntypematch \ETFUNC~\functype_1 \matchesexterntype \ETFUNC~\functype_2`. - -2. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETTABLE~\tabletype_1` and :math:`\ETTABLE~\tabletype_2` respectively: - - a. Return true if and only if :math:`\vdashexterntypematch \ETTABLE~\tabletype_1 \matchesexterntype \ETTABLE~\tabletype_2`. - -3. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETMEM~\memtype_1` and :math:`\ETMEM~\memtype_2` respectively: - - a. Return true if and only if :math:`\vdashexterntypematch \ETMEM~\memtype_1 \matchesexterntype \ETMEM~\memtype_2`. - -4. If :math:`\externtype_1` and :math:`\externtype_2` are both of the form :math:`\ETGLOBAL~\globaltype_1` and :math:`\ETGLOBAL~\globaltype_2` respectively: - - a. Return true if and only if :math:`\vdashexterntypematch \ETGLOBAL~\globaltype_1 \matchesexterntype \ETGLOBAL~\globaltype_2`. - -5. Return false. - -.. math:: - \begin{array}{lclll} - \F{module\_extern\_subtype}(\externtype_1, \externtype_2) &\iff& \vdashexterntypematch \externtype_1 \matchesexterntype \externtype_2 \\ - \end{array} - -.. note:: - This function encapsulates the external type matching relation defined in the :ref:`Import Subtyping ` of the validation section, where the :math:`\vdashexterntypematch \externtype_1 \matchesexterntype \externtype_2` judgment establishes compatibility between external types. This allows for explicit checking of type compatibility when linking modules or validating imports against exports. .. index:: instantiation, module instance .. _embed-module-instantiate: @@ -249,72 +218,6 @@ Modules \end{array} -.. index:: direct export -.. _embed-direct-exports: - -:math:`\F{module\_direct\_exports}(\module) : (\name, \externtype)^\ast` -........................................................................ - -1. Pre-condition: :math:`\module` is :ref:`valid ` with external import types :math:`\externtype^\ast` and external export types :math:`{\externtype'}^\ast`. - -2. Let :math:`\export^\ast` be the :ref:`exports ` :math:`\module.\MEXPORTS`. - -3. Let :math:`\X{result}` be the empty sequence. - -4. For each :math:`\export_i` in :math:`\export^\ast` and corresponding :math:`\externtype'_i` in :math:`{\externtype'}^\ast`, do: - - a. Let :math:`\import_j = \edexportimport(\module, \export_i.\EDESC)`. - - b. If :math:`\import_j = \epsilon`, then append the pair :math:`(\export_i.\ENAME, \externtype'_i)` to :math:`\X{result}`. - -5. Return :math:`\X{result}`. - -.. math:: - ~ \\ - \begin{array}{lclll} - \F{module\_direct\_exports}(m) &=& (\X{ex}.\ENAME, \externtype')^\ast \\ - && \qquad (\iff \X{ex}^\ast = m.\MEXPORTS \\ - && \qquad\quad \wedge~\edexportimport(m, \X{ex}.\EDESC) = \epsilon \\ - && \qquad\quad \wedge~\vdashmodule m : \externtype^\ast \to {\externtype'}^\ast \\ - && \qquad\quad \wedge~\externtype' = \X{ex}.\EDESC) \\ - \end{array} - - -.. index:: indirect export, re-export -.. _embed-indirect-exports: - -:math:`\F{module\_indirect\_exports}(\module) : (\name, \name, \name)^\ast` -........................................................................... - -1. Pre-condition: :math:`\module` is :ref:`valid ` with external import types :math:`\externtype^\ast` and external export types :math:`{\externtype'}^\ast`. - -2. Let :math:`\import^\ast` be the :ref:`imports ` :math:`\module.\MIMPORTS`. - -3. Let :math:`\export^\ast` be the :ref:`exports ` :math:`\module.\MEXPORTS`. - -4. Let :math:`\X{result}` be the empty sequence. - -5. For each :math:`\export_i` in :math:`\export^\ast`, do: - - a. Let :math:`\import_j = \edexportimport(\module, \export_i.\EDESC)`. - - b. If :math:`\import_j \neq \epsilon`, then: - - i. Append the triple :math:`(\export_i.\ENAME, \import_j.\IMODULE, \import_j.\INAME)` to :math:`\X{result}`. - -6. Return :math:`\X{result}`. - -.. math:: - ~ \\ - \begin{array}{lclll} - \F{module\_indirect\_exports}(m) &=& (\X{ex}.\ENAME, \X{im}.\IMODULE, \X{im}.\INAME)^\ast \\ - && \qquad (\iff \X{ex}^\ast = m.\MEXPORTS \\ - && \qquad\quad \wedge~\X{im} = \edexportimport(m, \X{ex}.\EDESC) \\ - && \qquad\quad \wedge~\X{im} \neq \epsilon \\ - && \qquad\quad \wedge~\vdashmodule m : \externtype^\ast \to {\externtype'}^\ast) \\ - \end{array} - - .. index:: module, module instance .. _embed-instance: diff --git a/document/core/syntax/modules.rst b/document/core/syntax/modules.rst index 8c95451c..ec32935c 100644 --- a/document/core/syntax/modules.rst +++ b/document/core/syntax/modules.rst @@ -327,7 +327,7 @@ The |MSTART| component of a module declares the :ref:`function index `. Exportable definitions are :ref:`functions `, :ref:`tables `, :ref:`memories `, and :ref:`globals `, which are referenced through a respective descriptor. -A *direct export* is one where the export references a function, table, memory, or global that is defined within the module itself rather than being imported. - -An *indirect export* (or *re-export*) is one where the export references a function, table, memory, or global that the module imports. - -For an export :math:`\export` in module :math:`m`, the export is direct when :math:`\edexportimport(m, \export.\EDESC) = \epsilon` and indirect otherwise, where -the import corresponding to an export descriptor :math:`\export.\EDESC` is defined by: - -.. math:: - \begin{array}{lclll} - \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDFUNC~\funcidx \\ - &&&& \quad \wedge~\exists~i~\colon~\funcidx = |\{j ~|~ j < i \\ - &&&& \quad \quad \wedge~m.\MIMPORTS[j].\IDESC = \IDFUNC~\typeidx' \}|) \\ - \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDTABLE~\tableidx \\ - &&&& \quad \wedge~\exists~i~\colon~\tableidx = |\{j ~|~ j < i \\ - &&&& \quad \quad \wedge~m.\MIMPORTS[j].\IDESC = \IDTABLE~\tabletype' \}|) \\ - \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDMEM~\memidx \\ - &&&& \quad \wedge~\exists~i~\colon~\memidx = |\{j ~|~ j < i \\ - &&&& \quad \quad \wedge~m.\MIMPORTS[j].\IDESC = \IDMEM~\memtype' \}|) \\ - \F{exportimport}(m, \exportdesc) &=& m.\MIMPORTS[i] && (\iff \exportdesc = \EDGLOBAL~\globalidx \\ - &&&& \quad \wedge~\exists~i~\colon~\globalidx = |\{j ~|~ j < i \\ - &&&& \quad \quad \wedge~m.\MIMPORTS[j].\IDESC = \IDGLOBAL~\globaltype' \}|) \\ - \F{exportimport}(m, \exportdesc) &=& \epsilon && (\otherwise) \\ - \end{array} - Conventions ........... diff --git a/document/core/util/macros.def b/document/core/util/macros.def index 3ee1edab..06b60fd6 100644 --- a/document/core/util/macros.def +++ b/document/core/util/macros.def @@ -346,7 +346,6 @@ .. |edtables| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{tables}} .. |edmems| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{mems}} .. |edglobals| mathdef:: \xref{syntax/modules}{syntax-exportdesc}{\F{globals}} -.. |edexportimport| mathdef:: \xref{syntax/modules}{syntax-export}{\F{exportimport}} .. Instructions, terminals diff --git a/document/js-api/index.bs b/document/js-api/index.bs index d0c6776a..3481c191 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -123,12 +123,9 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df text: store_init; url: appendix/embedding.html#embed-store-init text: module_decode; url: appendix/embedding.html#embed-module-decode text: module_validate; url: appendix/embedding.html#embed-module-validate - text: module_extern_subtype; url: appendix/embedding.html#embed-module-extern-subtype text: module_instantiate; url: appendix/embedding.html#embed-module-instantiate text: module_imports; url: appendix/embedding.html#embed-module-imports text: module_exports; url: appendix/embedding.html#embed-module-exports - text: module_direct_exports; url: appendix/embedding.html#embed-module-direct-exports - text: module_inirect_exports; url: appendix/embedding.html#embed-module-indirect-exports text: instance_export; url: appendix/embedding.html#embed-instance-export text: func_alloc; url: appendix/embedding.html#embed-func-alloc text: func_type; url: appendix/embedding.html#embed-func-type @@ -1394,8 +1391,6 @@ To parse a WebAssembly module given a byte sequence |by 1. [=list/iterate|For each=] (|moduleName|, name, type) in [=module_imports=](|module|.\[[Module]]), 1. [=set/Append=] |moduleName| to |requestedModules|. 1. Let |moduleRecord| be { - - \[[Instance]]: ~empty~, \[[Realm]]: |realm|, \[[Environment]]: ~empty~, @@ -1445,24 +1440,8 @@ WebAssembly Module Records have the following methods:
-

ResolveExport ( |exportName|, |resolveSet| ) Concrete Method

-1. If |resolveSet| is not present, set |resolveSet| to « ». +

ResolveExport ( |exportName|, resolveSet ) Concrete Method

1. Let |record| be this WebAssembly Module Record. -1. Let |module| be |record|.\[[ModuleSource]].\[[Module]]. -1. [=list/iterate|For each=] Record |r| of |resolveSet|, - 1. If |record| and |r|.\[[Module]] are the same Module Record and |exportName| is |r|.\[[ExportName]], - 1. Return null. -1. Append the record { \[[Module]]: |record|, \[[ExportName]]: |exportName| } to |resolveSet|. -1. [=list/iterate|For each=] (|name|, |importedModuleName|, |importName|, type) in [=module_indirect_exports=](|module|), - 1. If |name| is equal to |exportName|, - 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). - 1. Let |resolved| be [=?=] |importedModule|.ResolveExport(|importName|, |resolveSet|). - 1. If |resolved| is null or ~AMBIGUOUS~, - 1. Return |resolved|. - 1. If |resolved|.\[[Module]] is a WebAssembly Module Record, - 1. Return |resolved|. - 1. Note: This fall-through case allows indirect exports referencing JS values to be treated as captured direct bindings in the environment record, - with live bindings unsupported. Only live bindings between Wasm globals are supported for Wasm exports. 1. If the [=export name list=] of |record| contains |exportName|, return { \[[Module]]: |record|, \[[BindingName]]: |exportName| }. 1. Otherwise, return null. @@ -1481,23 +1460,8 @@ WebAssembly Module Records have the following methods: 1. Let |record| be this WebAssembly Module Record. 1. Let |env| be [$NewModuleEnvironment$](null). 1. Set |record|.\[[Environment]] to |env|. -1. Let |module| be |record|.\[[ModuleSource]].\[[Module]]. -1. [=list/iterate|For each=] (|importedModuleName|, |name|, type) in [=module_imports=](|module|), - 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). - 1. Let |resolution| be |importedModule|.ResolveExport(|name|). - 1. If |resolution| is null or ~AMBIGUOUS~, throw a {{SyntaxError}} exception. -1. [=list/iterate|For each=] (|name|, |importedModuleName|, |importName|, type) in [=module_indirect_exports=](|module|), - 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). - 1. Let |resolved| be [=?=] |importedModule|.ResolveExport(|importName|). - 1. Assert |resolved| is not null or ~AMBIGUOUS~. - 1. If |resolved|.\[[Module]] is not a WebAssembly Module Record, - 1. Note: This case corresponds to indirect exports to non-WebAssembly bindings, which are always snapshotted. - 1. Perform [=!=] |env|.CreateImmutableBinding(|name|, true). -1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_direct_exports=](|module|), - 1. If |externtype| is of the form [=global=] [=var=] valtype, - 1. Perform [=!=] |env|.CreateMutableBinding(|name|, false). - 1. Otherwise, - 1. Perform [=!=] |env|.CreateImmutableBinding(|name|, true). +1. For each |name| in the [=export name list=] of |record|, + 1. Perform ! |env|.CreateImmutableBinding(|name|, true).
@@ -1605,8 +1569,6 @@ will always reflect the current mutable global export value as interpreted throu 1. [=list/iterate|For each=] (|moduleName|, name, type) in [=module_imports=](|module|), 1. [=set/Append=] |moduleName| to |requestedModules|. 1. Let |moduleRecord| be { - - \[[Instance]]: ~empty~, \[[Realm]]: |realm|, \[[Environment]]: ~empty~, From fd6814043b6512d1abf0414665016ea018af792a Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 16 Mar 2025 13:27:08 -0700 Subject: [PATCH 11/21] simplify: remove cyclic binding handling --- document/js-api/index.bs | 64 +++++++++++---------------- proposals/esm-integration/EXAMPLES.md | 12 +++-- proposals/esm-integration/README.md | 27 ++++++----- 3 files changed, 46 insertions(+), 57 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 3481c191..bcc6a5df 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -156,6 +156,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df text: memory address; url: exec/runtime.html#syntax-memaddr text: global address; url: exec/runtime.html#syntax-globaladdr text: extern address; url: exec/runtime.html#syntax-externaddr + text: extern subtype; url: valid/types.html#match-externtype text: page size; url: exec/runtime.html#page-size url: syntax/types.html#syntax-numtype text: i32 @@ -1163,7 +1164,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not 1. Let |values| be [=?=] [$IteratorToList$]([=?=] [$GetIteratorFromMethod$](|ret|, |method|)). 1. Let |wasmValues| be a new, empty [=list=]. 1. If |values|'s [=list/size=] is not |resultsSize|, throw a {{TypeError}} exception. - 1. [=list/iterate|For each=] |value| and |resultType| in |values| and |results|, paired linearly, + 1. For each |value| and |resultType| in |values| and |results|, paired linearly, 1. [=list/Append=] [=ToWebAssemblyValue=](|value|, |resultType|) to |wasmValues|. 1. Return |wasmValues|. @@ -1388,9 +1389,11 @@ To parse a WebAssembly module given a byte sequence |by 1. If |module| is [=error=], throw a {{CompileError}} exception. 1. [=Construct a WebAssembly module object=] from |module| and |bytes|, and let |module| be the result. 1. Let |requestedModules| be a set. -1. [=list/iterate|For each=] (|moduleName|, name, type) in [=module_imports=](|module|.\[[Module]]), +1. For each (|moduleName|, name, type) in [=module_imports=](|module|.\[[Module]]), 1. [=set/Append=] |moduleName| to |requestedModules|. 1. Let |moduleRecord| be { + + \[[Instance]]: ~empty~, \[[Realm]]: |realm|, \[[Environment]]: ~empty~, @@ -1422,7 +1425,7 @@ The export name list of a WebAssembly Module Record |record| is defin 1. Let |module| be |record|'s \[[ModuleSource]] internal slot. 1. Let |exports| be an empty [=list=]. -1. [=list/iterate|For each=] (|name|, type) in [=module_exports=](|module|.\[[Module]]) +1. For each (|name|, type) in [=module_exports=](|module|.\[[Module]]) 1. [=list/Append=] |name| to the end of |exports|. 1. Return |exports|. @@ -1483,9 +1486,9 @@ WebAssembly Module Records have the following methods: 1. Let |resolutionName| be |resolution|.\[[BindingName]]. 1. Let |externval| be [=instance_export=](|resolutionInstance|, |resolutionName|). 1. Assert: |externval| is not [=error=]. - 1. Assert: [=module_direct_exports=](|resolutionModule|) contains an element (|resolutionName|, type). - 1. Let |externtype| be the value of |type| for the element (|resolutionName|, |type|) in [=module_direct_exports=](|resolutionModule|). - 1. If [=module_extern_subtype=](|externtype|, |importtype|) is false, throw a {{LinkError}} exception. + 1. Assert: [=module_exports=](|resolutionModule|) contains an element (|resolutionName|, type). + 1. Let |externtype| be the value of |type| for the element (|resolutionName|, |type|) in [=module_exports=](|resolutionModule|). + 1. If |importtype| is not an [=extern subtype=] of |externtype|, throw a {{LinkError}} exception. 1. [=list/Append=] |externval| to |imports|. 1. Otherwise, 1. Let |env| be |resolution|.\[[Module]].\[[Environment]]. @@ -1500,13 +1503,15 @@ WebAssembly Module Records have the following methods: 1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|. 1. [=list/Append=] |externfunc| to |imports|. 1. If |importtype| is of the form [=global=] |mut| |valtype|, + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. If |v| [=implements=] {{Global}}, 1. Let |globaladdr| be |v|.\[[Global]]. + 1. Let |targetmut| valuetype be [=global_type=](|store|, |globaladdr|). + 1. If |mut| is [=const=] and |targetmut| is [=var=], throw a {{TypeError}}. 1. Otherwise, - 1. If |valtype| is [=v128=], - 1. Throw a {{LinkError}} exception. + 1. If |valtype| is [=v128=], throw a {{LinkError}} exception. + 1. If |mut| is [=var=], throw a {{TypeError}}. 1. Let |value| be [=?=] [=ToWebAssemblyValue=](|v|, |valtype|). - 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, |mut| |valtype|, |value|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. 1. Let |externglobal| be [=external value|global=] |globaladdr|. @@ -1522,53 +1527,34 @@ WebAssembly Module Records have the following methods: 1. [=list/Append=] |externtable| to |imports|. 1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |instance| be the result. 1. Set |record|.\[[Instance]] to |instance|. -1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_direct_exports=](|module|), - 1. Let |externval| be [=instance_export=](|instance|, |name|). - 1. Assert: |externval| is not [=error=]. - 1. If |externtype| is of the form [=func=] functype, - 1. Assert: |externval| is of the form [=external value|func=] |funcaddr|. - 1. Let [=external value|func=] |funcaddr| be |externval|. - 1. Let |func| be the result of creating [=a new Exported Function=] from |funcaddr|. - 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |func|). +1. [=list/iterate|For each=] (|name|, |externtype|) of [=module_exports=](|module|), 1. If |externtype| is of the form [=global=] |mut| |globaltype|, 1. Assert: |externval| is of the form [=external value|global=] |globaladdr|. 1. Let [=external value|global=] |globaladdr| be |externval|. 1. Let |global_value| be [=global_read=](|store|, |globaladdr|). 1. If |globaltype| is not [=v128=], - 1. Note: When integrating with shared globals, they will be excluded here similarly to v128 above. + 1. Note: The condition above leaves unsupported JS values as uninitialized in TDZ and therefore as a reference error on + access. When integrating with shared globals, they may be excluded here similarly to v128 above. 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, [=ToJSValue=](|global_value|)). - 1. Associate all future mutations to the mutable value at |globaladdr| with the ECMA-262 binding record for |name| in + 1. If |mut| is [=var=], then associate all future mutations of |globaladdr| with the ECMA-262 binding record for |name| in |record|.\[[Environment]], such that |record|.\[[Environment]].GetBindingValue(|resolution|.\[[BindingName]], true) always returns [=ToJSValue=]([=global_read=](|store|, |globaladdr|)) for the current [=surrounding agent=]'s [=associated store=] |store|. - 1. If |externtype| is of the form [=mem=] memtype, - 1. Assert: |externval| is of the form [=external value|mem=] |memaddr|. - 1. Let [=external value|mem=] |memaddr| be |externval|. - 1. Let |memory| be [=create a memory object|a new Memory object=] created from |memaddr|. - 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |memory|). - 1. If |externtype| is of the form [=table=] tabletype, - 1. Assert: |externval| is of the form [=external value|table=] |tableaddr|. - 1. Let [=external value|table=] |tableaddr| be |externval|. - 1. Let |table| be [=create a Table object|a new Table object=] created from |tableaddr|. - 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, |table|). - -Note: The export bindings for a WebAssembly Module Record are designed to mirror the live bindings of all directly exported values. Values not -supported in JS are left uninitialized and in TDZ, while still being accesible to Wasm importers. Indirect exports (re-exports) referencing -JavaScript values will capture the [=ToWebAssemblyValue=] interpretation at the time of execution, while indirect exports resolving to other Wasm -modules will support Wasm live bindings. For exported mutable globals that are supported in JavaScript, the live bindings on the environment record -will always reflect the current mutable global export value as interpreted through the infallible [=ToJSValue=] interpretation. + 1. Otherwise, + 1. Perform ! |record|.\[[Environment]].InitializeBinding(|name|, ! Get(|instance|.\[[Exports]], |name|)).

HostGetModuleSourceModuleRecord ( |specifier| )

1. If |specifier| is a WebAssembly {{Module}} object with a \[[Module]] internal slot, - 1. Let |module| be |specifier|.\[[Module]]. - 1. If |module|.\[[ModuleRecord]] is null, + 1. If |specifier|.\[[Module]].\[[ModuleRecord]] is null, 1. Let |realm| be the current agent's realm record. 1. Let |requestedModules| be a set. - 1. [=list/iterate|For each=] (|moduleName|, name, type) in [=module_imports=](|module|), + 1. For each (|moduleName|, name, type) in [=module_imports=](|specifier|.\[[Module]]), 1. [=set/Append=] |moduleName| to |requestedModules|. 1. Let |moduleRecord| be { + + \[[Instance]]: ~empty~, \[[Realm]]: |realm|, \[[Environment]]: ~empty~, @@ -1591,7 +1577,7 @@ will always reflect the current mutable global export value as interpreted throu }. 1. Set |module|.\[[ModuleRecord]] to |moduleRecord|. 1. Return |moduleRecord|. - 1. Return |module|.\[[ModuleRecord]]. + 1. Return |specifier|.\[[Module]].\[[ModuleRecord]]. 1. Return ~not-a-source~. Note: See corresponding modifications to HTML in PR #10380. diff --git a/proposals/esm-integration/EXAMPLES.md b/proposals/esm-integration/EXAMPLES.md index b3600d58..bc002b02 100644 --- a/proposals/esm-integration/EXAMPLES.md +++ b/proposals/esm-integration/EXAMPLES.md @@ -123,6 +123,8 @@ for (let index = 0; index < length; index++) | table | `WebAssembly.Table` object | | function | WebAssembly exported function | +Wasm bindings cannot be reassigned as it can in JS, so the exported value will not change in their object identity. But the value that it points to (e.g. `.value` in the case of `WebAssembly.Global`) can change. + 1. JS module is parsed 1. wasm module is parsed 1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ. @@ -130,6 +132,8 @@ for (let index = 0; index < length; index++) 1. wasm module is instantiated evaluated. Functions are initialized. Memories and tables are initialized and filled with data/elem sections. Globals are initialized and initializer expressions are evaluated. The start function runs. 1. JS module is evaluated. All values are available. +The value of the export for `WebAssembly.Global` is provided directly on the JS namespace object, with mutable globals reflected as live bindings to JS. + #### Example ```js @@ -177,7 +181,7 @@ Wasm exports can be imported as accurate, immutable bindings to other wasm modul ### wasm imports <- JS re-exports <- wasm exports -Any wasm exports that are re-exported via a JS module will be available to the other wasm module as accurate bindings. If the binding is a mutable global, then that binding will be a live export binding in JS reflecting changes to the Wasm global value. When imported from JS, the first and second Wasm modules will yield the same Wasm identities for the multiply exported Wasm objects. +Any wasm exports that are re-exported via a JS module will be available to the other wasm module as accurate, immutable bindings. The wasm export gets wrapped into a JS object (e.g. `WebAssembly.Global`) and then unwrapped to the wasm import in the importing wasm module. When imported from JS, the first and second Wasm modules will yield the same object identities for the multiply exported Wasm objects. #### Example @@ -213,7 +217,7 @@ export {memoryExport} from "./b.wasm"; 1. JS module is parsed 1. wasm module is parsed -1. wasm module has a lexical environment created for its exports. All direct exports are initially in TDZ, indirect exports are available as they resolve. +1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ. 1. JS module is instantiated. All imports (including functions) from the wasm module are memory locations holding undefined. 1. wasm module is instantiated and evaluated. Snapshots of imports are taken. Export bindings are initialized. 1. JS module is evaluated. @@ -251,8 +255,8 @@ export function functionExport() { 1. wasm module is parsed 1. JS module is parsed 1. JS module is instantiated. -1. wasm module has a lexical environment created for its exports. All direct exports are initially in TDZ, indirect exports are available as they resolve. -1. JS module is evaluated. wasm exports in TDZ lead to a ReferenceError if used. +1. wasm module has a lexical environment created for its exports. All exports are initially in TDZ. +1. JS module is evaluated. wasm exports lead to a ReferenceError if used. 1. wasm module is instantiated and evaluated; wasm-exported bindings are updated to their appropriate JS API-exposed values. #### Examples diff --git a/proposals/esm-integration/README.md b/proposals/esm-integration/README.md index 4a403843..b82fcf13 100644 --- a/proposals/esm-integration/README.md +++ b/proposals/esm-integration/README.md @@ -94,22 +94,23 @@ In the proposed HTML integration of WebAssembly modules, the [module name](https ### "Snapshotting" imports -When imports are provided to WebAssembly modules in the host instance linking model from JavaScript, they are provided directly upfront. +When imports are provided to WebAssembly modules in the host instance linking model, they are provided directly upfront. - This handling of imports could be called a "snapshotting" process: Later updates to the imported values won't be reflected within the WebAssembly module. -- Circular WebAssembly module bindings are not supported for direct exports: One of them will run first, and that one will find that the exports of the other aren't yet initialized, leading to a ReferenceError. Indirect exports (reexports) can be supported early in Wasm cycles, where a WebAssembly module exports the same binding it imports. +- Circular WebAssembly modules are not supported: One of them will run first, and that one will find that the exports of the other aren't yet initialized, leading to a ReferenceError. See the FAQ for more explanation of the rationale for this design decision, and what features it enables which would be difficult or impossible otherwise. -### Live binding exports +### Progressive Implementation Support -WebAssembly modules support live exports bindings for mutable globals by reflecting the infallible ToJsValue() representation of their mutable global exports on their JavaScript environment bindings record. +It is possible to implement the Wasm-ESM integration in two stages: -For bindings that are exported from bindings that were initially imported i.e. indirect exports or reexports: +1. In the first stage only source phase imports of Wasm are supported (`import source fibModule from "./fib.wasm"`). +2. In the second stage, evaluation phase imports would be supported too (`import { fib } from "./fib.wasm"`). -1. WebAssembly modules that have indirect exports to JavaScript modules, these values are snapshotted at the time of execution of the WebAssembly module and reflected as captured bindings and not live bindings on the exports, even if the underlying JavaScript module might have modifications to the binding later on. +If initially implementing just source phase imports, the `GetExportedNames`, `ResolveExport`, `InitializeEnvironment`, and `ExecuteModule` abstract operations can be implemented as abstract operations unconditionally throwing a `SyntaxError` exception. In this case, module fetch and CSP integration is still required to be implemented as specified in this proposal. -2. WebAssembly modules that have indirect exports to WebAssembly modules, where those modules directly export mutable globals, support the live mutable global reference without capturing the import. +Implementers are encouraged to ship both stages at once, but it is deemed OK for implementers to initially ship the first stage and then quickly follow up with the second stage, if this aids "time to ship" in implementations. ## FAQ @@ -119,7 +120,7 @@ Originally the ESM integration only provided the direct host instance linking mo Supporting the [source phase](https://github.com/tc39/proposal-source-phase-imports) ESM integration is therefore a more general form of the ESM integration that shares the host resolver, while retaining linking flexibility for Js host embedding of Wasm. -While the source phase does not replace the instance linking model, is does offer a more general and flexible ESM integration as an addition. +While the source phase does not replace the instance linking model, is does offer a more general and flexible ESM integration. ### How does this relate to the Component Model? @@ -149,7 +150,7 @@ The conversion is based on the type that the import and export was declared as, ### When one WebAssembly module imports another one, will there be overhead due to converting back and forth to JS values? -Bindings between WebAssembly modules, even those that are indirect through indirect exports (reexports) are directly linked, without needing to go through JS wrapping and unwrapping. +Note that exports of ES Module Records always have values that can be directly treated as JavaScript values. Although we're talking about conversions to and from JavaScript for these exports, it's expected that, in native implementations, the conversion to and from Javascript would "cancel out" and not lead to the use of wrappers in practice. ### Why are WebAssembly modules instantiated during the "Evaluation" phase? @@ -171,11 +172,9 @@ Instead of including this check in the default semantics of functions, a trampol ### What does snapshotting imports mean for importing globals? -Imports are only snapshotted when they resolve to a JavaScript module. For imports that resolve to WebAssembly modules, these are always directly bound. - -When a WebAssembly module imports a Global, that resolves to a module after resolving the binding through any indirect exports (reexports): -- If the resolved module is a JavaScript module, then the exporting module may either export a direct value or a `WebAssembly.Global` of the same type. -- If the resolved module is a WebAssembly module, then the exporting module must export a global that is an extern subtype of the importing global. +When a WebAssembly module imports a Global, there are two possible modes of operation: +- If the Global type is immutable (as declared in the importing module), then the exporting module may either export a numeric value or an immutable Global. +- If the Global type is mutable, then the exporting module must export a mutable Global. The snapshot here is "shallow" in the sense that modifications *within* this particular mutable Global object *will* be visible in the importing module (but, if the exporting module overwrites the entire binding with some unrelated value, this will not be noticed by the importing module). ### Can Web APIs be imported via modules? From 8ec8b4b093397c690fd6e28adeb460bdd6a6b92d Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 17 Mar 2025 10:33:30 -0700 Subject: [PATCH 12/21] fixups, linking note --- document/js-api/index.bs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index bcc6a5df..391b8464 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1507,10 +1507,10 @@ WebAssembly Module Records have the following methods: 1. If |v| [=implements=] {{Global}}, 1. Let |globaladdr| be |v|.\[[Global]]. 1. Let |targetmut| valuetype be [=global_type=](|store|, |globaladdr|). - 1. If |mut| is [=const=] and |targetmut| is [=var=], throw a {{TypeError}}. + 1. If |mut| is [=const=] and |targetmut| is [=var=], throw a {{LinkError}} exception. 1. Otherwise, 1. If |valtype| is [=v128=], throw a {{LinkError}} exception. - 1. If |mut| is [=var=], throw a {{TypeError}}. + 1. If |mut| is [=var=], throw a {{LinkError}} exception. 1. Let |value| be [=?=] [=ToWebAssemblyValue=](|v|, |valtype|). 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, |mut| |valtype|, |value|). 1. Set the [=surrounding agent=]'s [=associated store=] to |store|. @@ -1543,6 +1543,11 @@ WebAssembly Module Records have the following methods: 1. Otherwise, 1. Perform ! |record|.\[[Environment]].InitializeBinding(|name|, ! Get(|instance|.\[[Exports]], |name|)). +Note: The linking semantics here for Wasm to Wasm modules are identical to the WebAssembly JS API semantics as if passing the +the exports object as the imports object in instantiation. When linking Wasm module imports to JS module exports, the JS API semantics +are exactly followed as well. It is only in the case of importing Wasm from JS that WebAssembly.Global unwrapping is observable on the +WebAssembly Module Record Environment Record. +

HostGetModuleSourceModuleRecord ( |specifier| )

From 7a122d65e0745f03e25325939ce96cd50e3666c8 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 18 Mar 2025 16:24:47 -0700 Subject: [PATCH 13/21] cyclic error as link error --- document/js-api/index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 391b8464..ec224142 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1481,7 +1481,7 @@ WebAssembly Module Records have the following methods: 1. If |resolution|.\[[Module]] is a WebAssembly Module Record, 1. Let |resolutionInstance| be |resolution|.\[[Module]].\[[Instance]]. 1. If |resolutionInstance| is ~empty~ then, - 1. Throw a {ReferenceError}. + 1. Throw a {LinkError} exception. 1. Let |resolutionModule| be |resolution|.\[[Module]].\[[ModuleSource]].\[[Module]]. 1. Let |resolutionName| be |resolution|.\[[BindingName]]. 1. Let |externval| be [=instance_export=](|resolutionInstance|, |resolutionName|). @@ -1534,7 +1534,7 @@ WebAssembly Module Records have the following methods: 1. Let |global_value| be [=global_read=](|store|, |globaladdr|). 1. If |globaltype| is not [=v128=], 1. Note: The condition above leaves unsupported JS values as uninitialized in TDZ and therefore as a reference error on - access. When integrating with shared globals, they may be excluded here similarly to v128 above. + access. When integrating with shared globals, they may be excluded here similarly to v128 above. 1. Perform [=!=] |record|.\[[Environment]].InitializeBinding(|name|, [=ToJSValue=](|global_value|)). 1. If |mut| is [=var=], then associate all future mutations of |globaladdr| with the ECMA-262 binding record for |name| in |record|.\[[Environment]], such that |record|.\[[Environment]].GetBindingValue(|resolution|.\[[BindingName]], true) From 3e70ac4d164d8363850b1863e766d1395b6dbe6f Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 30 Mar 2025 14:28:16 -0700 Subject: [PATCH 14/21] support separate instance lookup function --- document/js-api/index.bs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index ec224142..e86d9cae 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -80,6 +80,7 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT text: Cyclic Module Record; url: cyclic-module-record text: GetMethod; url: sec-getmethod text: ToBigInt64; url: #sec-tobigint64 + text: Module Namespace exotic object; url: #sec-module-namespace-exotic-objects type: abstract-op text: CreateDataPropertyOrThrow; url: sec-createdatapropertyorthrow text: CreateMethodProperty; url: sec-createmethodproperty @@ -318,6 +319,8 @@ namespace WebAssembly { Promise<Instance> instantiate( Module moduleObject, optional object importObject); + + Instance moduleInstance(ModuleNamespace namespace); }; @@ -534,6 +537,14 @@ The verification of WebAssembly type requirements is deferred to the 1. [=asynchronously instantiate a WebAssembly module|Asynchronously instantiate the WebAssembly module=] |moduleObject| importing |importObject|, and return the result. +
+ The moduleInstance(|namespace|) method, when invoked, performs the following steps: + 1. If |namespace| is not a [=is a Module Namespace exotic object=], [=throw=] a {{TypeError}} exception. + 1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception. + 1. Let |module| be |namespace|.\[[Module]]. + 1. Return |module|.\[[Instance]]. +
+ Note: A follow-on streaming API is documented in the WebAssembly Web API.

Modules

From 61e6abd4ed5eef178eba2a533a6d51a33e032bf3 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 30 Mar 2025 14:43:14 -0700 Subject: [PATCH 15/21] moduleInstance -> namespaceInstance --- document/js-api/index.bs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index e86d9cae..da386a17 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -320,7 +320,7 @@ namespace WebAssembly { Promise<Instance> instantiate( Module moduleObject, optional object importObject); - Instance moduleInstance(ModuleNamespace namespace); + Instance namespaceInstance(ModuleNamespace namespace); }; @@ -538,7 +538,7 @@ The verification of WebAssembly type requirements is deferred to the
- The moduleInstance(|namespace|) method, when invoked, performs the following steps: + The namespaceInstance(|namespace|) method, when invoked, performs the following steps: 1. If |namespace| is not a [=is a Module Namespace exotic object=], [=throw=] a {{TypeError}} exception. 1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception. 1. Let |module| be |namespace|.\[[Module]]. From 09b4432fbc31f3cb0dba33f9bf5401a3aef37170 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 30 Mar 2025 14:50:35 -0700 Subject: [PATCH 16/21] fixup reference --- document/js-api/index.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index da386a17..f0e17ae5 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -539,7 +539,7 @@ The verification of WebAssembly type requirements is deferred to the
The namespaceInstance(|namespace|) method, when invoked, performs the following steps: - 1. If |namespace| is not a [=is a Module Namespace exotic object=], [=throw=] a {{TypeError}} exception. + 1. If |namespace| is not a [=Module Namespace exotic object=], [=throw=] a {{TypeError}} exception. 1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception. 1. Let |module| be |namespace|.\[[Module]]. 1. Return |module|.\[[Instance]]. From 8307913759a327cd01778510954120b7498c8e85 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Sun, 30 Mar 2025 15:03:25 -0700 Subject: [PATCH 17/21] move to Instance, assume IDL integration --- document/js-api/index.bs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index f0e17ae5..a45c891a 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -319,8 +319,6 @@ namespace WebAssembly { Promise<Instance> instantiate( Module moduleObject, optional object importObject); - - Instance namespaceInstance(ModuleNamespace namespace); }; @@ -537,14 +535,6 @@ The verification of WebAssembly type requirements is deferred to the 1. [=asynchronously instantiate a WebAssembly module|Asynchronously instantiate the WebAssembly module=] |moduleObject| importing |importObject|, and return the result.
-
- The namespaceInstance(|namespace|) method, when invoked, performs the following steps: - 1. If |namespace| is not a [=Module Namespace exotic object=], [=throw=] a {{TypeError}} exception. - 1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception. - 1. Let |module| be |namespace|.\[[Module]]. - 1. Return |module|.\[[Instance]]. -
- Note: A follow-on streaming API is documented in the WebAssembly Web API.

Modules

@@ -642,6 +632,7 @@ Note: Some implementations enforce a size limitation on |bytes|. Use of this API interface Instance { constructor(Module module, optional object importObject); readonly attribute object exports; + static Instance namespaceInstance(ModuleNamespace moduleNamespace); }; @@ -659,6 +650,14 @@ Note: The use of this synchronous API is discouraged, as some implementations so The getter of the exports attribute of {{Instance}} returns **this**.\[[Exports]].
+
+ The namespaceInstance(|moduleNamespace|) method, when invoked, performs the following steps: + 1. Assert: |namespace| is a [=Module Namespace exotic object=]. + 1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception. + 1. Let |module| be |namespace|.\[[Module]]. + 1. Return |module|.\[[Instance]]. +
+

Memories


From 87df70936cb8faa42320a5313c45419cf2f29677 Mon Sep 17 00:00:00 2001
From: Guy Bedford 
Date: Tue, 8 Apr 2025 09:33:44 -0700
Subject: [PATCH 18/21] Update document/js-api/index.bs

Co-authored-by: Andreas Rossberg 
---
 document/js-api/index.bs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/document/js-api/index.bs b/document/js-api/index.bs
index a45c891a..bcf302bd 100644
--- a/document/js-api/index.bs
+++ b/document/js-api/index.bs
@@ -651,7 +651,7 @@ Note: The use of this synchronous API is discouraged, as some implementations so
 
 
 
- The namespaceInstance(|moduleNamespace|) method, when invoked, performs the following steps: + The namespaceInstance(|namespace|) method, when invoked, performs the following steps: 1. Assert: |namespace| is a [=Module Namespace exotic object=]. 1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception. 1. Let |module| be |namespace|.\[[Module]]. From fa3da3bb04a2b728fda92f0527d36dcd0cc4114c Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 8 Apr 2025 09:58:27 -0700 Subject: [PATCH 19/21] clarify resolution --- document/js-api/index.bs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index bcf302bd..ab627f73 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -79,8 +79,9 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT text: CreateArrayFromList; url: sec-createarrayfromlist text: Cyclic Module Record; url: cyclic-module-record text: GetMethod; url: sec-getmethod - text: ToBigInt64; url: #sec-tobigint64 - text: Module Namespace exotic object; url: #sec-module-namespace-exotic-objects + text: ToBigInt64; url: sec-tobigint64 + text: Module Namespace exotic object; url: sec-module-namespace-exotic-objects + text: ResolvedBinding Record; url: resolvedbinding-record type: abstract-op text: CreateDataPropertyOrThrow; url: sec-createdatapropertyorthrow text: CreateMethodProperty; url: sec-createmethodproperty @@ -1488,21 +1489,24 @@ WebAssembly Module Records have the following methods: 1. [=list/iterate|For each=] (|importedModuleName|, |name|, |importtype|) in [=module_imports=](|module|), 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). 1. Let |resolution| be |importedModule|.ResolveExport(|name|). - 1. If |resolution|.\[[Module]] is a WebAssembly Module Record, - 1. Let |resolutionInstance| be |resolution|.\[[Module]].\[[Instance]]. - 1. If |resolutionInstance| is ~empty~ then, + 1. Assert: |resolution| is a [=ResolvedBinding Record=], as validated during environment initialization. + 1. Let |resolvedModule| be |resolution|.\\[[Module]]. + 1. Let |resolvedName| be |resolution|.\[[BindingName]]. + 1. If |resolvedModule| is a WebAssembly Module Record, + 1. If |resolvedModule|.\[[Instance]] is ~empty~ then, 1. Throw a {LinkError} exception. - 1. Let |resolutionModule| be |resolution|.\[[Module]].\[[ModuleSource]].\[[Module]]. - 1. Let |resolutionName| be |resolution|.\[[BindingName]]. - 1. Let |externval| be [=instance_export=](|resolutionInstance|, |resolutionName|). + 1. Assert: |resolvedModule|.\[[Instance]] is a WebAssembly {{Instance}} object. + 1. Assert: |resolvedModule|.\[[ModuleSource]] is a WebAssembly {{Module}} object. + 1. Let |module| be |resolvedModule|.\[[ModuleSource]].\[[Module]]. + 1. Let |externval| be [=instance_export=](|resolvedModule|.\[[Instance]], |resolvedName|). 1. Assert: |externval| is not [=error=]. - 1. Assert: [=module_exports=](|resolutionModule|) contains an element (|resolutionName|, type). - 1. Let |externtype| be the value of |type| for the element (|resolutionName|, |type|) in [=module_exports=](|resolutionModule|). + 1. Assert: [=module_exports=](|module|) contains an element (|resolvedName|, type). + 1. Let |externtype| be the value of |type| for the element (|resolvedName|, |type|) in [=module_exports=](|module|). 1. If |importtype| is not an [=extern subtype=] of |externtype|, throw a {{LinkError}} exception. 1. [=list/Append=] |externval| to |imports|. 1. Otherwise, - 1. Let |env| be |resolution|.\[[Module]].\[[Environment]]. - 1. Let |v| be [=?=] |env|.GetBindingValue(|resolution|.\[[BindingName]], true). + 1. Let |env| be |resolvedModule|.\[[Environment]]. + 1. Let |v| be [=?=] |env|.GetBindingValue(|resolvedName|, true). 1. If |importtype| is of the form [=func=] |functype|, 1. If [$IsCallable$](|v|) is false, throw a {{LinkError}} exception. 1. If |v| has a \[[FunctionAddress]] internal slot, and therefore is an [=Exported Function=], From 19d763589b8bc89ca20c1c028b7f5fea83e9bd2f Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 8 Apr 2025 10:00:02 -0700 Subject: [PATCH 20/21] fixup --- document/js-api/index.bs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index ab627f73..e7080e3f 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -1493,8 +1493,7 @@ WebAssembly Module Records have the following methods: 1. Let |resolvedModule| be |resolution|.\\[[Module]]. 1. Let |resolvedName| be |resolution|.\[[BindingName]]. 1. If |resolvedModule| is a WebAssembly Module Record, - 1. If |resolvedModule|.\[[Instance]] is ~empty~ then, - 1. Throw a {LinkError} exception. + 1. If |resolvedModule|.\[[Instance]] is ~empty~, throw a {LinkError} exception. 1. Assert: |resolvedModule|.\[[Instance]] is a WebAssembly {{Instance}} object. 1. Assert: |resolvedModule|.\[[ModuleSource]] is a WebAssembly {{Module}} object. 1. Let |module| be |resolvedModule|.\[[ModuleSource]].\[[Module]]. From ef477aaabb729f0b4aae5957ee434a14994edc50 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 9 Apr 2025 09:56:22 -0700 Subject: [PATCH 21/21] use base-level namespaceInstance --- document/js-api/index.bs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index e7080e3f..e85da2d5 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -320,6 +320,8 @@ namespace WebAssembly { Promise<Instance> instantiate( Module moduleObject, optional object importObject); + + Instance namespaceInstance(ModuleNamespace moduleNamespace); };
@@ -331,6 +333,7 @@ Note: WebAssembly.compile(|bytes|) asynchronously validates and complies bytes of WebAssembly into a Module. WebAssembly.instantiate(|bytes|, |importObject|) asynchronously compiles and instantiates a WebAssembly module from bytes of source. The WebAssembly.instantiate(|moduleObject|, |importObject|) asynchronously instantiates a compiled module. + WebAssembly.namespaceInstance(|namespace|) obtains the WebAssembly Instance from the Module Namespace Exotic Object for a WebAssembly Module Record. -->
@@ -538,6 +541,14 @@ The verification of WebAssembly type requirements is deferred to the Note: A follow-on streaming API is documented in the WebAssembly Web API. +
+ The namespaceInstance(|namespace|) method, when invoked, performs the following steps: + 1. Assert: |namespace| is a [=Module Namespace exotic object=]. + 1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception. + 1. Let |module| be |namespace|.\[[Module]]. + 1. Return |module|.\[[Instance]]. +
+

Modules

@@ -633,7 +644,6 @@ Note: Some implementations enforce a size limitation on |bytes|. Use of this API
 interface Instance {
   constructor(Module module, optional object importObject);
   readonly attribute object exports;
-  static Instance namespaceInstance(ModuleNamespace moduleNamespace);
 };
 
@@ -651,14 +661,6 @@ Note: The use of this synchronous API is discouraged, as some implementations so The getter of the exports attribute of {{Instance}} returns **this**.\[[Exports]].
-
- The namespaceInstance(|namespace|) method, when invoked, performs the following steps: - 1. Assert: |namespace| is a [=Module Namespace exotic object=]. - 1. If |namespace|.\[[Module]] is not a [=WebAssembly Module Record=], [=throw=] a {{TypeError}} exception. - 1. Let |module| be |namespace|.\[[Module]]. - 1. Return |module|.\[[Instance]]. -
-

Memories