Skip to content

Commit 2c1404e

Browse files
Add evaluation tests for import.defer with sync/async modules
1 parent e7a84b0 commit 2c1404e

File tree

16 files changed

+337
-0
lines changed

16 files changed

+337
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-ContinueDynamicImport
6+
description: >
7+
`import.defer` causes eager evaluation if the module has top-level await
8+
info: |
9+
13.3.10.1.1 ContinueDynamicImport (
10+
_payload_: a DynamicImportState Record,
11+
_moduleCompletion_: either a normal completion containing a Module Record or a throw completion,
12+
): ~UNUSED~
13+
1. Let _promiseCapability_ be _payload_.[[PromiseCapability]].
14+
1. Let _phase_ be _payload_.[[Phase]].
15+
1. ...
16+
1. Let _module_ be _moduleCompletion_.[[Value]].
17+
1. Let _loadPromise_ be _module_.LoadRequestedModules().
18+
1. ...
19+
1. Let _linkAndEvaluateClosure_ be a new Abstract Closure with no parameters that captures _module_, _promiseCapability_, _phase_ and _onRejected_ and performs the following steps when called:
20+
1. ...
21+
1. Let _fulfilledClosure_ be a new Abstract Closure with no parameters that captures _module_, _phase_, and _promiseCapability_ and performs the following steps when called:
22+
1. Let _namespace_ be GetModuleNamespace(_module_, _phase_).
23+
1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _namespace_ »).
24+
1. Return ~UNUSED~.
25+
1. If _phase_ is ~defer~, then
26+
1. Let _evaluationList_ be GatherAsynchronousTransitiveDependencies(_module_).
27+
1. If _evaluationList_ is empty, then
28+
1. Perform _fulfilledClosure_().
29+
1. Return ~UNUSED~.
30+
1. Let _asyncDepsEvaluationPromises_ be a new empty List.
31+
1. For each Module Record _dep_ of _evaluationList_, append _dep_.Evaluate() to _asyncDepsEvaluationPromises_.
32+
1. Let _iterator_ be CreateListIteratorRecord(_asyncDepsEvaluationPromises_).
33+
1. Let _pc_ be ! NewPromiseCapability(%Promise%).
34+
1. Let _evaluatePromise_ be ! PerformPromiseAll(_iterator_, %Promise%, _pc_, %Promise.resolve%).
35+
1. Else,
36+
1. ...
37+
1. Let _onFulfilled_ be CreateBuiltinFunction(_fulfilledClosure_, *""*, 0, « »).
38+
1. Perform PerformPromiseThen(_evaluatePromise_, _onFulfilled_, _onRejected_).
39+
1. Return ~UNUSED~.
40+
1. Let _linkAndEvaluate_ be CreateBuiltinFunction(_linkAndEvaluateClosure_, *""*, 0, « »).
41+
1. Perform PerformPromiseThen(_loadPromise_, _linkAndEvaluate_, _onRejected_).
42+
1. Return ~UNUSED~.
43+
44+
GatherAsynchronousTransitiveDependencies ( _module_, [ _seen_ ] )
45+
1. If _seen_ is not specified, let _seen_ be a new empty List.
46+
1. Let _result_ be a new empty List.
47+
1. If _seen_ contains _module_, return _result_.
48+
1. Append _module_ to _seen_.
49+
1. If _module_ is not a Cyclic Module Record, return _result_.
50+
1. If _module_.[[Status]] is either ~evaluating~ or ~evaluated~, return _result_.
51+
1. If _module_.[[HasTLA]] is *true*, then
52+
1. Append _module_ to _result_.
53+
1. Return _result_.
54+
1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do
55+
1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifier]]).
56+
1. Let _additionalModules_ be GatherAsynchronousTransitiveDependencies(_requiredModule_, _seen_).
57+
1. For each Module Record _m_ of _additionalModules_, do
58+
1. If _result_ does not contain _m_, append _m_ to _result_.
59+
1. Return _result_.
60+
61+
flags: [module, async]
62+
features: [import-defer, top-level-await]
63+
includes: [compareArray.js]
64+
---*/
65+
66+
import "./setup_FIXTURE.js";
67+
68+
import.defer("./tla_FIXTURE.js").then(ns => {
69+
assert.compareArray(globalThis.evaluations, ["tla start", "tla end"]);
70+
ns.x;
71+
assert.compareArray(globalThis.evaluations, ["tla start", "tla end"]);
72+
}).then($DONE, $DONE);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
globalThis.evaluations = [];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
globalThis.evaluations.push("tla start");
5+
6+
await Promise.resolve(0);
7+
8+
globalThis.evaluations.push("tla end");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
import "./tla_FIXTURE.js";
5+
6+
globalThis.evaluations.push("imports-tla");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-ContinueDynamicImport
6+
description: >
7+
`import.defer` causes eager evaluation of transitive dependencies with top-level await
8+
info: |
9+
13.3.10.1.1 ContinueDynamicImport (
10+
_payload_: a DynamicImportState Record,
11+
_moduleCompletion_: either a normal completion containing a Module Record or a throw completion,
12+
): ~UNUSED~
13+
1. Let _promiseCapability_ be _payload_.[[PromiseCapability]].
14+
1. Let _phase_ be _payload_.[[Phase]].
15+
1. ...
16+
1. Let _module_ be _moduleCompletion_.[[Value]].
17+
1. Let _loadPromise_ be _module_.LoadRequestedModules().
18+
1. ...
19+
1. Let _linkAndEvaluateClosure_ be a new Abstract Closure with no parameters that captures _module_, _promiseCapability_, _phase_ and _onRejected_ and performs the following steps when called:
20+
1. ...
21+
1. Let _fulfilledClosure_ be a new Abstract Closure with no parameters that captures _module_, _phase_, and _promiseCapability_ and performs the following steps when called:
22+
1. Let _namespace_ be GetModuleNamespace(_module_, _phase_).
23+
1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _namespace_ »).
24+
1. Return ~UNUSED~.
25+
1. If _phase_ is ~defer~, then
26+
1. Let _evaluationList_ be GatherAsynchronousTransitiveDependencies(_module_).
27+
1. If _evaluationList_ is empty, then
28+
1. Perform _fulfilledClosure_().
29+
1. Return ~UNUSED~.
30+
1. Let _asyncDepsEvaluationPromises_ be a new empty List.
31+
1. For each Module Record _dep_ of _evaluationList_, append _dep_.Evaluate() to _asyncDepsEvaluationPromises_.
32+
1. Let _iterator_ be CreateListIteratorRecord(_asyncDepsEvaluationPromises_).
33+
1. Let _pc_ be ! NewPromiseCapability(%Promise%).
34+
1. Let _evaluatePromise_ be ! PerformPromiseAll(_iterator_, %Promise%, _pc_, %Promise.resolve%).
35+
1. Else,
36+
1. ...
37+
1. Let _onFulfilled_ be CreateBuiltinFunction(_fulfilledClosure_, *""*, 0, « »).
38+
1. Perform PerformPromiseThen(_evaluatePromise_, _onFulfilled_, _onRejected_).
39+
1. Return ~UNUSED~.
40+
1. Let _linkAndEvaluate_ be CreateBuiltinFunction(_linkAndEvaluateClosure_, *""*, 0, « »).
41+
1. Perform PerformPromiseThen(_loadPromise_, _linkAndEvaluate_, _onRejected_).
42+
1. Return ~UNUSED~.
43+
44+
GatherAsynchronousTransitiveDependencies ( _module_, [ _seen_ ] )
45+
1. If _seen_ is not specified, let _seen_ be a new empty List.
46+
1. Let _result_ be a new empty List.
47+
1. If _seen_ contains _module_, return _result_.
48+
1. Append _module_ to _seen_.
49+
1. If _module_ is not a Cyclic Module Record, return _result_.
50+
1. If _module_.[[Status]] is either ~evaluating~ or ~evaluated~, return _result_.
51+
1. If _module_.[[HasTLA]] is *true*, then
52+
1. Append _module_ to _result_.
53+
1. Return _result_.
54+
1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do
55+
1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifier]]).
56+
1. Let _additionalModules_ be GatherAsynchronousTransitiveDependencies(_requiredModule_, _seen_).
57+
1. For each Module Record _m_ of _additionalModules_, do
58+
1. If _result_ does not contain _m_, append _m_ to _result_.
59+
1. Return _result_.
60+
61+
flags: [module, async]
62+
features: [import-defer, top-level-await]
63+
includes: [compareArray.js]
64+
---*/
65+
66+
import "./setup_FIXTURE.js";
67+
68+
import.defer("./imports-tla_FIXTURE.js").then(ns => {
69+
assert.compareArray(globalThis.evaluations, ["tla start", "tla end"]);
70+
ns.x;
71+
assert.compareArray(globalThis.evaluations, ["tla start", "tla end", "imports-tla"]);
72+
}).then($DONE, $DONE);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
globalThis.evaluations = [];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
globalThis.evaluations.push("tla start");
5+
6+
await Promise.resolve(0);
7+
8+
globalThis.evaluations.push("tla end");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
globalThis.evaluations.push("dep");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
import "./tla-with-dep_FIXTURE.js";
5+
6+
globalThis.evaluations.push("imports-tla-with-dep");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-ContinueDynamicImport
6+
description: >
7+
`import.defer` causes eager evaluation of synchronous dependencies of async dependencies
8+
info: |
9+
13.3.10.1.1 ContinueDynamicImport (
10+
_payload_: a DynamicImportState Record,
11+
_moduleCompletion_: either a normal completion containing a Module Record or a throw completion,
12+
): ~UNUSED~
13+
1. Let _promiseCapability_ be _payload_.[[PromiseCapability]].
14+
1. Let _phase_ be _payload_.[[Phase]].
15+
1. ...
16+
1. Let _module_ be _moduleCompletion_.[[Value]].
17+
1. Let _loadPromise_ be _module_.LoadRequestedModules().
18+
1. ...
19+
1. Let _linkAndEvaluateClosure_ be a new Abstract Closure with no parameters that captures _module_, _promiseCapability_, _phase_ and _onRejected_ and performs the following steps when called:
20+
1. ...
21+
1. Let _fulfilledClosure_ be a new Abstract Closure with no parameters that captures _module_, _phase_, and _promiseCapability_ and performs the following steps when called:
22+
1. Let _namespace_ be GetModuleNamespace(_module_, _phase_).
23+
1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _namespace_ »).
24+
1. Return ~UNUSED~.
25+
1. If _phase_ is ~defer~, then
26+
1. Let _evaluationList_ be GatherAsynchronousTransitiveDependencies(_module_).
27+
1. If _evaluationList_ is empty, then
28+
1. Perform _fulfilledClosure_().
29+
1. Return ~UNUSED~.
30+
1. Let _asyncDepsEvaluationPromises_ be a new empty List.
31+
1. For each Module Record _dep_ of _evaluationList_, append _dep_.Evaluate() to _asyncDepsEvaluationPromises_.
32+
1. Let _iterator_ be CreateListIteratorRecord(_asyncDepsEvaluationPromises_).
33+
1. Let _pc_ be ! NewPromiseCapability(%Promise%).
34+
1. Let _evaluatePromise_ be ! PerformPromiseAll(_iterator_, %Promise%, _pc_, %Promise.resolve%).
35+
1. Else,
36+
1. ...
37+
1. Let _onFulfilled_ be CreateBuiltinFunction(_fulfilledClosure_, *""*, 0, « »).
38+
1. Perform PerformPromiseThen(_evaluatePromise_, _onFulfilled_, _onRejected_).
39+
1. Return ~UNUSED~.
40+
1. Let _linkAndEvaluate_ be CreateBuiltinFunction(_linkAndEvaluateClosure_, *""*, 0, « »).
41+
1. Perform PerformPromiseThen(_loadPromise_, _linkAndEvaluate_, _onRejected_).
42+
1. Return ~UNUSED~.
43+
44+
GatherAsynchronousTransitiveDependencies ( _module_, [ _seen_ ] )
45+
1. If _seen_ is not specified, let _seen_ be a new empty List.
46+
1. Let _result_ be a new empty List.
47+
1. If _seen_ contains _module_, return _result_.
48+
1. Append _module_ to _seen_.
49+
1. If _module_ is not a Cyclic Module Record, return _result_.
50+
1. If _module_.[[Status]] is either ~evaluating~ or ~evaluated~, return _result_.
51+
1. If _module_.[[HasTLA]] is *true*, then
52+
1. Append _module_ to _result_.
53+
1. Return _result_.
54+
1. For each ModuleRequest Record _required_ of _module_.[[RequestedModules]], do
55+
1. Let _requiredModule_ be GetImportedModule(_module_, _required_.[[Specifier]]).
56+
1. Let _additionalModules_ be GatherAsynchronousTransitiveDependencies(_requiredModule_, _seen_).
57+
1. For each Module Record _m_ of _additionalModules_, do
58+
1. If _result_ does not contain _m_, append _m_ to _result_.
59+
1. Return _result_.
60+
61+
flags: [module, async]
62+
features: [import-defer, top-level-await]
63+
includes: [compareArray.js]
64+
---*/
65+
66+
import "./setup_FIXTURE.js";
67+
import defer * as ns from "./imports-tla-with-dep_FIXTURE.js";
68+
69+
import.defer("./imports-tla-with-dep_FIXTURE.js").then(ns => {
70+
assert.compareArray(
71+
globalThis.evaluations,
72+
["dep", "tla-with-dep start", "tla-with-dep end"]
73+
);
74+
ns.x;
75+
assert.compareArray(
76+
globalThis.evaluations,
77+
["dep", "tla-with-dep start", "tla-with-dep end", "imports-tla-with-dep"]
78+
);
79+
}).then($DONE, $DONE);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
globalThis.evaluations = [];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
import "./dep_FIXTURE.js";
5+
6+
globalThis.evaluations.push("tla-with-dep start");
7+
8+
await Promise.resolve(0);
9+
10+
globalThis.evaluations.push("tla-with-dep end");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
globalThis.evaluations.push("dep");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
/*---
5+
esid: sec-ContinueDynamicImport
6+
description: >
7+
`import.defer` of a sync module graph does not cause evaluation
8+
info: |
9+
13.3.10.1.1 ContinueDynamicImport (
10+
_payload_: a DynamicImportState Record,
11+
_moduleCompletion_: either a normal completion containing a Module Record or a throw completion,
12+
): ~UNUSED~
13+
1. Let _promiseCapability_ be _payload_.[[PromiseCapability]].
14+
1. Let _phase_ be _payload_.[[Phase]].
15+
1. ...
16+
1. Let _module_ be _moduleCompletion_.[[Value]].
17+
1. Let _loadPromise_ be _module_.LoadRequestedModules().
18+
1. ...
19+
1. Let _linkAndEvaluateClosure_ be a new Abstract Closure with no parameters that captures _module_, _promiseCapability_, _phase_ and _onRejected_ and performs the following steps when called:
20+
1. ...
21+
1. Let _fulfilledClosure_ be a new Abstract Closure with no parameters that captures _module_, _phase_, and _promiseCapability_ and performs the following steps when called:
22+
1. Let _namespace_ be GetModuleNamespace(_module_, _phase_).
23+
1. Perform ! Call(_promiseCapability_.[[Resolve]], *undefined*, « _namespace_ »).
24+
1. Return ~UNUSED~.
25+
1. If _phase_ is ~defer~, then
26+
1. Let _evaluationList_ be GatherAsynchronousTransitiveDependencies(_module_).
27+
1. If _evaluationList_ is empty, then
28+
1. Perform _fulfilledClosure_().
29+
1. Return ~UNUSED~.
30+
1. ...
31+
1. Let _linkAndEvaluate_ be CreateBuiltinFunction(_linkAndEvaluateClosure_, *""*, 0, « »).
32+
1. Perform PerformPromiseThen(_loadPromise_, _linkAndEvaluate_, _onRejected_).
33+
1. Return ~UNUSED~.
34+
35+
flags: [module, async]
36+
features: [import-defer]
37+
includes: [compareArray.js]
38+
---*/
39+
40+
import "./setup_FIXTURE.js";
41+
42+
import.defer("./sync_FIXTURE.js").then(ns => {
43+
assert.compareArray(globalThis.evaluations, []);
44+
ns.x;
45+
assert.compareArray(globalThis.evaluations, ["dep", "sync"]);
46+
}).then($DONE, $DONE);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
globalThis.evaluations = [];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (C) 2025 Igalia, S.L. All rights reserved.
2+
// This code is governed by the BSD license found in the LICENSE file.
3+
4+
import "./dep_FIXTURE.js";
5+
6+
globalThis.evaluations.push("sync");

0 commit comments

Comments
 (0)