From 652de3d5ab5f411702ad37abaac14dc7c99b87af Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 24 Sep 2026 13:26:34 -0700 Subject: [PATCH 1/3] [js-api] Share import reading between the JS API and ESM integration ExecuteModule duplicated the per-import value coercion of read the imports and had drifted from it: TypeError from ToWebAssemblyValue was not converted to LinkError, the i64 / number kind checks were missing, and there was no tag case at all. It also created a fresh builtin function address per import, whereas read the imports instantiates each builtin set once, so importing the same builtin twice gave distinct Exported Function identities under ESM only. Factor read the imports into two helpers, instantiate the builtin and string imports and read an import value, and use them from ExecuteModule so both instantiation paths share the same semantics. Only the Wasm to Wasm instance_export linking remains ESM specific. --- document/js-api/index.bs | 161 ++++++++++++++++----------------------- 1 file changed, 65 insertions(+), 96 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 6d5633b40..8a5298be3 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -509,9 +509,8 @@ To instantiate imported strings with module |module| and |importedStr -
- To read the imports from a WebAssembly module |module| from imports object |importObject|, enabled builtins |builtinSetNames|, and |importedStringModule|, perform the following steps: - 1. If |module|.[=imports=] [=list/is empty|is not empty=], and |importObject| is undefined, throw a {{TypeError}} exception. +
+ To instantiate the builtin and string imports of a WebAssembly module |module| with enabled builtins |builtinSetNames| and |importedStringModule|, perform the following steps: 1. Let |builtinOrStringImports| be the ordered map « ». 1. [=list/iterate|For each=] |builtinSetName| of |builtinSetNames|, 1. Let |builtinSetQualifiedName| be |builtinSetName| prefixed with "wasm:" @@ -522,6 +521,61 @@ To instantiate imported strings with module |module| and |importedStr 1. If |importedStringModule| is not null, 1. Let |exportsObject| be the result of [=instantiate imported strings=] with |module| and |importedStringModule| 1. [=map/set|Set=] |builtinOrStringImports|[|importedStringModule|] to |exportsObject| + 1. Return |builtinOrStringImports|. +
+ +
+ To read an import value |v| of type |externtype| into imports list |imports|, perform the following steps: + 1. If |externtype| is of the form [=external-type/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 |externtype| is of the form [=external-type/global=] mut |valtype|, + 1. If |v| [=implements=] {{Global}}, + 1. Let |globaladdr| be |v|.\[[Global]]. + 1. Otherwise, + 1. If |valtype| is [=i64=] and |v| [=is not a BigInt=], + 1. Throw a {{LinkError}} exception. + 1. If |valtype| is one of [=i32=], [=f32=] or [=f64=] and |v| [=is not a Number=], + 1. Throw a {{LinkError}} exception. + 1. If |valtype| is [=v128=], + 1. Throw a {{LinkError}} exception. + 1. Let |value| be [=ToWebAssemblyValue=](|v|, |valtype|). If this operation throws a {{TypeError}}, catch it, and throw a {{LinkError}} exception. + 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. + 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, [=const=] |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 |externtype| is of the form [=external-type/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 |externtype| is of the form [=external-type/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. If |externtype| is of the form [=external-type/tag=] |attribute| functype, + 1. Assert: |attribute| is [=tagtype/attribute/exception=]. + 1. If |v| does not [=implement=] {{Tag}}, throw a {{LinkError}} exception. + 1. Let |tagaddr| be |v|.\[[Address]]. + 1. Let |externtag| be the [=external value=] [=external value/tag=] |tagaddr|. + 1. [=list/Append=] |externtag| to |imports|. + +Note: This algorithm only verifies the right kind of JavaScript values are passed. +The verification of WebAssembly type requirements is deferred to the +"[=instantiate the core of a WebAssembly module=]" algorithm. +
+ +
+ To read the imports from a WebAssembly module |module| from imports object |importObject|, enabled builtins |builtinSetNames|, and |importedStringModule|, perform the following steps: + 1. If |module|.[=imports=] [=list/is empty|is not empty=], and |importObject| is undefined, throw a {{TypeError}} exception. + 1. Let |builtinOrStringImports| be the result of [=instantiate the builtin and string imports=] of |module| with |builtinSetNames| and |importedStringModule|. 1. Let |imports| be « ». 1. [=list/iterate|For each=] (|moduleName|, |componentName|, |externtype|) of [=module_imports=](|module|), 1. If |builtinOrStringImports| [=map/exist|contains=] |moduleName|, @@ -532,51 +586,8 @@ To instantiate imported strings with module |module| and |importedStr 1. Let |o| be [=?=] [$Get$](|importObject|, |moduleName|). 1. If |o| [=is not an Object=], throw a {{TypeError}} exception. 1. Let |v| be [=?=] [$Get$](|o|, |componentName|). - 1. If |externtype| is of the form [=external-type/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 |externtype| is of the form [=external-type/global=] mut |valtype|, - 1. If |v| [=implements=] {{Global}}, - 1. Let |globaladdr| be |v|.\[[Global]]. - 1. Otherwise, - 1. If |valtype| is [=i64=] and |v| [=is not a BigInt=], - 1. Throw a {{LinkError}} exception. - 1. If |valtype| is one of [=i32=], [=f32=] or [=f64=] and |v| [=is not a Number=], - 1. Throw a {{LinkError}} exception. - 1. If |valtype| is [=v128=], - 1. Throw a {{LinkError}} exception. - 1. Let |value| be [=ToWebAssemblyValue=](|v|, |valtype|). If this operation throws a {{TypeError}}, catch it, and throw a {{LinkError}} exception. - 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, [=const=] |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 |externtype| is of the form [=external-type/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 |externtype| is of the form [=external-type/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. If |externtype| is of the form [=external-type/tag=] |attribute| functype, - 1. Assert: |attribute| is [=tagtype/attribute/exception=]. - 1. If |v| does not [=implement=] {{Tag}}, throw a {{LinkError}} exception. - 1. Let |tagaddr| be |v|.\[[Address]]. - 1. Let |externtag| be the [=external value=] [=external value/tag=] |tagaddr|. - 1. [=list/Append=] |externtag| to |imports|. + 1. [=Read an import value=] |v| of type |externtype| into |imports|. 1. Return |imports|. - -Note: This algorithm only verifies the right kind of JavaScript values are passed. -The verification of WebAssembly type requirements is deferred to the -"[=instantiate the core of a WebAssembly module=]" algorithm.
@@ -2417,25 +2428,14 @@ WebAssembly Module Records have the following methods: 1. Let |module| be |moduleObject|.\[[Module]]. 1. Let |builtinSetNames| be |moduleObject|.\[[BuiltinSets]]. 1. Let |importedStringModule| be |moduleObject|.\[[ImportedStringModule]]. +1. Let |builtinOrStringImports| be the result of [=instantiate the builtin and string imports=] of |module| with |builtinSetNames| and |importedStringModule|. 1. Let |imports| be « ». 1. [=list/iterate|For each=] (|importedModuleName|, |name|, |importtype|) of [=module_imports=](|module|), - 1. If |importedStringModule| is not null and |importedModuleName| equals |importedStringModule|, - 1. Assert: |importtype| is of the form [=global=] [=const=] |valtype|. - 1. Let |store| be the [=surrounding agent=]'s [=associated store=]. - 1. Let |value| be [=?=] [=ToWebAssemblyValue=](|name|, |valtype|). - 1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, [=const=] |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. [=iteration/continue=]. - 1. If |builtinSetNames| is not null, - 1. Let |maybeBuiltin| be the result of [=find a builtin|finding a builtin=] for (|importedModuleName|, |name|, |importtype|) and |builtinSetNames|. - 1. If |maybeBuiltin| is not null, - 1. Let |funcType| be |maybeBuiltin|[1][1]. - 1. Let |steps| be |maybeBuiltin|[1][2]. - 1. Let |funcaddr| be the result of [=create a builtin function=] with |funcType| and |steps|. - 1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|. - 1. [=list/Append=] |externfunc| to |imports|. + 1. If |builtinOrStringImports| [=map/exist|contains=] |importedModuleName|, + 1. Let |o| be |builtinOrStringImports|[|importedModuleName|]. + 1. If [$HasProperty$](|o|, |name|) is true, + 1. Let |v| be [=!=] [$Get$](|o|, |name|). + 1. [=Read an import value=] |v| of type |importtype| into |imports|. 1. [=iteration/continue=]. 1. Let |importedModule| be [$GetImportedModule$](|record|, |importedModuleName|). 1. Let |resolution| be |importedModule|.ResolveExport(|name|). @@ -2457,38 +2457,7 @@ WebAssembly Module Records have the following methods: 1. Otherwise, 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=], - 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|, 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|, - 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 {{LinkError}} exception. - 1. Otherwise, - 1. If |valtype| is [=v128=], throw a {{LinkError}} exception. - 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|. - 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. [=Read an import value=] |v| of type |importtype| into |imports|. 1. [=Instantiate the core of a WebAssembly module=] |module| with |imports|, and let |coreInstance| be the result. 1. Let |instanceObject| be a [=/new=] {{Instance}}. 1. [=initialize an instance object|Initialize=] |instanceObject| from |module| and |coreInstance|. From 29c261c210409e10292cdd47b8bed4beba9afb97 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Thu, 24 Sep 2026 13:29:46 -0700 Subject: [PATCH 2/3] [js-api] Keep read the imports in place, extracted helpers follow --- document/js-api/index.bs | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index 8a5298be3..c659f8be2 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -509,6 +509,28 @@ To instantiate imported strings with module |module| and |importedStr
+
+ To read the imports from a WebAssembly module |module| from imports object |importObject|, enabled builtins |builtinSetNames|, and |importedStringModule|, perform the following steps: + 1. If |module|.[=imports=] [=list/is empty|is not empty=], and |importObject| is undefined, throw a {{TypeError}} exception. + 1. Let |builtinOrStringImports| be the result of [=instantiate the builtin and string imports=] of |module| with |builtinSetNames| and |importedStringModule|. + 1. Let |imports| be « ». + 1. [=list/iterate|For each=] (|moduleName|, |componentName|, |externtype|) of [=module_imports=](|module|), + 1. If |builtinOrStringImports| [=map/exist|contains=] |moduleName|, + 1. Let |o| be |builtinOrStringImports|[|moduleName|]. + 1. If |o| [=is not an Object=] or if [$HasProperty$](|o|, |componentName|) is false, + 1. Set |o| to [=?=] [$Get$](|importObject|, |moduleName|). + 1. Else, + 1. Let |o| be [=?=] [$Get$](|importObject|, |moduleName|). + 1. If |o| [=is not an Object=], throw a {{TypeError}} exception. + 1. Let |v| be [=?=] [$Get$](|o|, |componentName|). + 1. [=Read an import value=] |v| of type |externtype| into |imports|. + 1. Return |imports|. + +Note: This algorithm only verifies the right kind of JavaScript values are passed. +The verification of WebAssembly type requirements is deferred to the +"[=instantiate the core of a WebAssembly module=]" algorithm. +
+
To instantiate the builtin and string imports of a WebAssembly module |module| with enabled builtins |builtinSetNames| and |importedStringModule|, perform the following steps: 1. Let |builtinOrStringImports| be the ordered map « ». @@ -566,28 +588,6 @@ To instantiate imported strings with module |module| and |importedStr 1. Let |tagaddr| be |v|.\[[Address]]. 1. Let |externtag| be the [=external value=] [=external value/tag=] |tagaddr|. 1. [=list/Append=] |externtag| to |imports|. - -Note: This algorithm only verifies the right kind of JavaScript values are passed. -The verification of WebAssembly type requirements is deferred to the -"[=instantiate the core of a WebAssembly module=]" algorithm. -
- -
- To read the imports from a WebAssembly module |module| from imports object |importObject|, enabled builtins |builtinSetNames|, and |importedStringModule|, perform the following steps: - 1. If |module|.[=imports=] [=list/is empty|is not empty=], and |importObject| is undefined, throw a {{TypeError}} exception. - 1. Let |builtinOrStringImports| be the result of [=instantiate the builtin and string imports=] of |module| with |builtinSetNames| and |importedStringModule|. - 1. Let |imports| be « ». - 1. [=list/iterate|For each=] (|moduleName|, |componentName|, |externtype|) of [=module_imports=](|module|), - 1. If |builtinOrStringImports| [=map/exist|contains=] |moduleName|, - 1. Let |o| be |builtinOrStringImports|[|moduleName|]. - 1. If |o| [=is not an Object=] or if [$HasProperty$](|o|, |componentName|) is false, - 1. Set |o| to [=?=] [$Get$](|importObject|, |moduleName|). - 1. Else, - 1. Let |o| be [=?=] [$Get$](|importObject|, |moduleName|). - 1. If |o| [=is not an Object=], throw a {{TypeError}} exception. - 1. Let |v| be [=?=] [$Get$](|o|, |componentName|). - 1. [=Read an import value=] |v| of type |externtype| into |imports|. - 1. Return |imports|.
From 3207bbb481df9697e95dad7c33fa0f11bb51949a Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Fri, 25 Sep 2026 18:26:25 -0700 Subject: [PATCH 3/3] [js-api] Validate builtin and string constant imports when parsing parse a WebAssembly module constructed the Module object without running validate builtins and imported string, unlike compile, validate and the Module constructor, so a string constant import with a non extern type or a builtin import with the wrong signature was accepted at parse time and only failed during execution. --- document/js-api/index.bs | 1 + 1 file changed, 1 insertion(+) diff --git a/document/js-api/index.bs b/document/js-api/index.bs index c659f8be2..40d6bac08 100644 --- a/document/js-api/index.bs +++ b/document/js-api/index.bs @@ -2331,6 +2331,7 @@ To parse a WebAssembly module given a byte sequence |by 1. If |module| is [=error=], throw a {{CompileError}} exception. 1. Let |builtinSetNames| be « "js-string" ». 1. Let |importedStringModule| be "wasm:js/string-constants". +1. If [=validate builtins and imported string for a WebAssembly module|validating builtins and imported strings=] for |module| with |builtinSetNames| and |importedStringModule| is false, throw a {{CompileError}} exception. 1. [=Construct a WebAssembly module object=] from |module|, |bytes|, |builtinSetNames| and |importedStringModule|, and let |module| be the result. 1. Let |requestedModules| be a set. 1. For each (|moduleName|, |name|, type) of the [=external imports=] of |module|,