diff --git a/document/js-api/index.bs b/document/js-api/index.bs
index 64dfd22f77..6ee5103b46 100644
--- a/document/js-api/index.bs
+++ b/document/js-api/index.bs
@@ -486,7 +486,14 @@ 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=](|module|) [=list/is empty|is not empty=], and |importObject| is missing, throw a {{TypeError}} exception.
+ 1. If |importObject| is missing,
+ 1. [=list/iterate|For each=] |import| of [=module_imports=](|module|),
+ 1. Let |moduleName| be |import|[0].
+ 1. If |importedStringModule| is not null and |moduleName| equals |importedStringModule|,
+ 1. [=iteration/continue=].
+ 1. If [=find a builtin|finding a builtin=] for |import| and |builtinSetNames| is not null,
+ 1. [=iteration/continue=].
+ 1. Throw a {{TypeError}} exception.
1. Let |builtinOrStringImports| be the ordered map «[ ]».
1. [=list/iterate|For each=] |builtinSetName| of |builtinSetNames|,
1. Let |builtinSetQualifiedName| be |builtinSetName| prefixed with "wasm:"
@@ -502,8 +509,10 @@ To instantiate imported strings with module |module| and |importedStr
1. If |builtinOrStringImports|[|moduleName|] [=map/exists=],
1. Let |o| be |builtinOrStringImports|[|moduleName|].
1. If |o| [=is not an Object=] or if [=?=] [$HasProperty$](|o|, |componentName|) is false,
+ 1. Assert: |importObject| is not missing.
1. Set |o| to [=?=] [$Get$](|importObject|, |moduleName|).
1. Else,
+ 1. Assert: |importObject| is not missing.
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|).
@@ -551,6 +560,13 @@ To instantiate imported strings with module |module| and |importedStr
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.
+
+Note: |importObject| is required only for an import that is not supplied by an
+enabled builtin or by the imported string constants module. If it is missing,
+the opening check throws unless every import is supplied that way. Each such
+import is then an own property of the object in |builtinOrStringImports| for
+its module name, so the [$Get$](|importObject|, |moduleName|) steps are not
+reached.
diff --git a/test/js-api/js-string/imports.any.js b/test/js-api/js-string/imports.any.js
index c357760bef..267945bd99 100644
--- a/test/js-api/js-string/imports.any.js
+++ b/test/js-api/js-string/imports.any.js
@@ -24,3 +24,56 @@ test(() => {
// reflection.
assert_equals(imports.length, 0);
});
+
+function moduleWithBuiltin() {
+ const builder = new WasmModuleBuilder();
+ builder.addImport(
+ "wasm:js-string",
+ "test",
+ {params: [kWasmExternRef], results: [kWasmI32]});
+ return new WebAssembly.Module(builder.toBuffer(), {builtins: ["js-string"]});
+}
+
+test(() => {
+ const instance = new WebAssembly.Instance(moduleWithBuiltin());
+ assert_equals(typeof instance.exports, "object");
+}, "A builtin-only module can be instantiated without an importObject");
+
+test(() => {
+ const instance = new WebAssembly.Instance(moduleWithBuiltin(), undefined);
+ assert_equals(typeof instance.exports, "object");
+}, "A builtin-only module can be instantiated with undefined importObject");
+
+promise_test(async () => {
+ const instance = await WebAssembly.instantiate(moduleWithBuiltin());
+ assert_equals(typeof instance.exports, "object");
+}, "WebAssembly.instantiate accepts a builtin-only module without an importObject");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+ builder.addImportedGlobal("strings", "hi", kWasmExternRef, false);
+ builder.addExportOfKind("hi", kExternalGlobal, 0);
+ const module = new WebAssembly.Module(builder.toBuffer(), {
+ importedStringConstants: "strings",
+ });
+ const instance = new WebAssembly.Instance(module);
+ assert_equals(instance.exports.hi.value, "hi");
+}, "Imported string constants do not require an importObject");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+ builder.addImport(
+ "wasm:js-string",
+ "test",
+ {params: [kWasmExternRef], results: [kWasmI32]});
+ builder.addImport("env", "f", {params: [], results: []});
+ const module = new WebAssembly.Module(builder.toBuffer(), {builtins: ["js-string"]});
+ assert_throws_js(TypeError, () => new WebAssembly.Instance(module));
+}, "A non-builtin import still requires an importObject");
+
+test(() => {
+ const builder = new WasmModuleBuilder();
+ builder.addImport("wasm:js-string", "not-a-builtin", {params: [], results: []});
+ const module = new WebAssembly.Module(builder.toBuffer(), {builtins: ["js-string"]});
+ assert_throws_js(TypeError, () => new WebAssembly.Instance(module));
+}, "An unknown import from a builtin module requires an importObject");