diff --git a/test/language/expressions/dynamic-import/import-errored-module.js b/test/language/expressions/dynamic-import/import-errored-module.js new file mode 100644 index 0000000000..2783bddc99 --- /dev/null +++ b/test/language/expressions/dynamic-import/import-errored-module.js @@ -0,0 +1,52 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-ContinueDynamicImport +description: > + Dynamic import of an already errored module should fail +info: | + ContinueDynamicImport ( _promiseCapability_, _moduleCompletion_ ) + 1. ... + 1. Let _module_ be _moduleCompletion_.[[Value]]. + 1. Let _loadPromise_ be _module_.LoadRequestedModules(). + 1. Let _rejectedClosure_ be a new Abstract Closure with parameters (_reason_) that captures _promiseCapability_ and performs the following steps when called: + 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _reason_ »). + 1. Return ~unused~. + 1. Let _onRejected_ be CreateBuiltinFunction(_rejectedClosure_, 1, *""*, « »). + 1. Let _linkAndEvaluateClosure_ be a new Abstract Closure with no parameters that captures _module_, _promiseCapability_, and _onRejected_ and performs the following steps when called: + 1. Let _link_ be Completion(_module_.Link()). + 1. ... + 1. Let _evaluatePromise_ be _module_.Evaluate(). + 1. Let _fulfilledClosure_ be a new Abstract Closure with no parameters that captures _module_ and _promiseCapability_ and performs the following steps when called: + 1. Let _namespace_ be GetModuleNamespace(_module_). + 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _namespace_ »). + 1. Return ~unused~. + 1. Let _onFulfilled_ be CreateBuiltinFunction(_fulfilledClosure_, 0, *""*, « »). + 1. Perform PerformPromiseThen(_evaluatePromise_, _onFulfilled_, _onRejected_). + 1. Return ~unused~. + 1. Let _linkAndEvaluate_ be CreateBuiltinFunction(_linkAndEvaluateClosure_, 0, *""*, « »). + 1. Perform PerformPromiseThen(_loadPromise_, _linkAndEvaluate_, _onRejected_). + 1. Return ~unused~. + + _module_ . Evaluate ( ) + 4. If _module_.[[TopLevelCapability]] is not ~empty~, then + a. Return _module_.[[TopLevelCapability]].[[Promise]]. + +flags: [async] +features: [dynamic-import] +includes: [asyncHelpers.js] +---*/ + +asyncTest(async function () { + await assert.throwsAsync( + Error, + () => import('./import-errored-module_FIXTURE.js'), + 'The import should reject (first import)' + ); + await assert.throwsAsync( + Error, + () => import('./import-errored-module_FIXTURE.js'), + 'The import should reject (second import)' + ); +}); diff --git a/test/language/expressions/dynamic-import/import-errored-module_FIXTURE.js b/test/language/expressions/dynamic-import/import-errored-module_FIXTURE.js new file mode 100644 index 0000000000..230029907a --- /dev/null +++ b/test/language/expressions/dynamic-import/import-errored-module_FIXTURE.js @@ -0,0 +1,4 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +throw new Error("boom"); diff --git a/test/language/module-code/top-level-await/dynamic-import-of-waiting-module-2_FIXTURE.js b/test/language/module-code/top-level-await/dynamic-import-of-waiting-module-2_FIXTURE.js new file mode 100644 index 0000000000..da2959ce53 --- /dev/null +++ b/test/language/module-code/top-level-await/dynamic-import-of-waiting-module-2_FIXTURE.js @@ -0,0 +1,3 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + diff --git a/test/language/module-code/top-level-await/dynamic-import-of-waiting-module.js b/test/language/module-code/top-level-await/dynamic-import-of-waiting-module.js new file mode 100644 index 0000000000..c4ad045c39 --- /dev/null +++ b/test/language/module-code/top-level-await/dynamic-import-of-waiting-module.js @@ -0,0 +1,69 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-ContinueDynamicImport +description: > + Dynamic import of an ~evaluating-async~ module waits for the module to finish its evaluation +info: | + ContinueDynamicImport ( _promiseCapability_, _moduleCompletion_ ) + 1. ... + 1. Let _module_ be _moduleCompletion_.[[Value]]. + 1. Let _loadPromise_ be _module_.LoadRequestedModules(). + 1. Let _rejectedClosure_ be a new Abstract Closure with parameters (_reason_) that captures _promiseCapability_ and performs the following steps when called: + 1. Perform ! Call(_promiseCapability_.[[Reject]], *undefined*, « _reason_ »). + 1. Return ~unused~. + 1. Let _onRejected_ be CreateBuiltinFunction(_rejectedClosure_, 1, *""*, « »). + 1. Let _linkAndEvaluateClosure_ be a new Abstract Closure with no parameters that captures _module_, _promiseCapability_, and _onRejected_ and performs the following steps when called: + 1. Let _link_ be Completion(_module_.Link()). + 1. ... + 1. Let _evaluatePromise_ be _module_.Evaluate(). + 1. Let _fulfilledClosure_ be a new Abstract Closure with no parameters that captures _module_ and _promiseCapability_ and performs the following steps when called: + 1. Let _namespace_ be GetModuleNamespace(_module_). + 1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _namespace_ »). + 1. Return ~unused~. + 1. Let _onFulfilled_ be CreateBuiltinFunction(_fulfilledClosure_, 0, *""*, « »). + 1. Perform PerformPromiseThen(_evaluatePromise_, _onFulfilled_, _onRejected_). + 1. Return ~unused~. + 1. Let _linkAndEvaluate_ be CreateBuiltinFunction(_linkAndEvaluateClosure_, 0, *""*, « »). + 1. Perform PerformPromiseThen(_loadPromise_, _linkAndEvaluate_, _onRejected_). + 1. Return ~unused~. + + _module_ . Evaluate ( ) + 4. If _module_.[[TopLevelCapability]] is not ~empty~, then + a. Return _module_.[[TopLevelCapability]].[[Promise]]. + +flags: [async] +features: [dynamic-import] +includes: [asyncHelpers.js] +---*/ + +let continueExecution; +globalThis.promise = new Promise((resolve) => continueExecution = resolve); + +const executionStartPromise = new Promise((resolve) => globalThis.executionStarted = resolve); + +asyncTest(async function () { + const promiseForNamespace = import("./dynamic-import-of-waiting-module_FIXTURE.js"); + + await executionStartPromise; + + const promiseForNamespace2 = import("./dynamic-import-of-waiting-module_FIXTURE.js"); + + // We only continue execution of the first fixture file after importing a second, + // empty, fixture file. This is so that if the implementation uses a separate + // queue to resolve dynamic import promises, if dynamic-import-of-waiting-module_FIXTURE + // wasn't waiting on top-level await its top-level promise would already be resolved. + await import("./dynamic-import-of-waiting-module-2_FIXTURE.js"); + continueExecution(); + + let secondPromiseResolved = false; + await Promise.all([ + promiseForNamespace.then(() => { + assert(!secondPromiseResolved, "The second import should not resolve before the first one"); + }), + promiseForNamespace2.then(() => { + secondPromiseResolved = true; + }) + ]); +}); diff --git a/test/language/module-code/top-level-await/dynamic-import-of-waiting-module_FIXTURE.js b/test/language/module-code/top-level-await/dynamic-import-of-waiting-module_FIXTURE.js new file mode 100644 index 0000000000..d4a0e7c63b --- /dev/null +++ b/test/language/module-code/top-level-await/dynamic-import-of-waiting-module_FIXTURE.js @@ -0,0 +1,8 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +globalThis.executionStarted(); + +export let x = 1; + +await globalThis.promise;