Skip to content

Commit d2940bd

Browse files
nicolo-ribaudoMs2ger
authored andcommitted
Test that resetting [[ModuleAsyncEvaluationCount]] is unobserbable
1 parent 687cf88 commit d2940bd

7 files changed

+117
-0
lines changed
Lines changed: 6 additions & 0 deletions
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 { logs } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js";
5+
import "./unobservable-global-async-evaluation-count-reset-b_FIXTURE.js";
6+
logs.push("A");
Lines changed: 6 additions & 0 deletions
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 { pB, pB_start } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js";
5+
pB_start.resolve();
6+
await pB.promise;
Lines changed: 4 additions & 0 deletions
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+
await 1;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
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 { logs } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js";
5+
import "./unobservable-global-async-evaluation-count-reset-e_FIXTURE.js";
6+
import "./unobservable-global-async-evaluation-count-reset-b_FIXTURE.js";
7+
logs.push("D");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
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 { pE_start } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js";
5+
pE_start.resolve();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
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+
export const logs = [];
5+
export const pB = Promise.withResolvers();
6+
export const pB_start = Promise.withResolvers();
7+
export const pE_start = Promise.withResolvers();
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
description: >
6+
An implementation may unobservably reset [[ModuleAsyncEvaluationCount]] to 0
7+
whenever there are no pending modules.
8+
info: |
9+
IncrementModuleAsyncEvaluationCount ( )
10+
1. Let AR be the Agent Record of the surrounding agent.
11+
2. Let count be AR.[[ModuleAsyncEvaluationCount]].
12+
3. Set AR.[[ModuleAsyncEvaluationCount]] to count + 1.
13+
4. Return count.
14+
15+
NOTE: This value is only used to keep track of the relative evaluation order
16+
between pending modules. An implementation may unobservably reset
17+
[[ModuleAsyncEvaluationCount]] to 0 whenever there are no pending modules.
18+
19+
InnerModuleEvaluation ( module, stack, index )
20+
...
21+
12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is true, then
22+
a. Assert: module.[[AsyncEvaluationOrder]] is unset.
23+
b. Set module.[[AsyncEvaluationOrder]] to IncrementModuleAsyncEvaluationCount().
24+
...
25+
26+
AsyncModuleExecutionFulfilled ( module )
27+
...
28+
9. Perform GatherAvailableAncestors(module, execList).
29+
10. ...
30+
11. Let sortedExecList be a List whose elements are the elements of execList, sorted by their [[AsyncEvaluationOrder]] field in ascending order.
31+
12. For each Cyclic Module Record m of sortedExecList, do
32+
a. If m.[[Status]] is evaluated, then
33+
i. Assert: m.[[EvaluationError]] is not empty.
34+
b. Else if m.[[HasTLA]] is true, then
35+
i. Perform ExecuteAsyncModule(m).
36+
c. Else,
37+
i. Let result be m.ExecuteModule().
38+
...
39+
40+
Module graph (the order of dependencies in each module is important, and it's left-to-right):
41+
┌─────┐ ┌─────┐ ┌─────┐
42+
│ A │ │ C │ │ D │
43+
└─────┘ └─────┘ └─────┘
44+
│ │ │
45+
│ ▼ │
46+
│ ┌─────┐ │
47+
│ │ E │ │
48+
│ └─────┘ │
49+
│ ┌──────────────────┘
50+
▼ ▼
51+
┌───────┐
52+
│ B │
53+
└───────┘
54+
55+
Where B and C have top-level await. The test orchestrates the evaluation order such that:
56+
- Import A first
57+
- Once B starts evaluating, import C and immediately resolve its top-level await
58+
- Once C finishes evaluating, import D
59+
- Once E is evaluated, resolve B's await
60+
61+
esid: sec-IncrementModuleAsyncEvaluationCount
62+
flags: [module, async]
63+
features: [top-level-await, dynamic-import, promise-with-resolvers]
64+
includes: [compareArray.js]
65+
---*/
66+
67+
import { logs, pB, pB_start, pE_start } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js";
68+
69+
const pA = import("./unobservable-global-async-evaluation-count-reset-a_FIXTURE.js");
70+
let pD;
71+
72+
pB_start.promise.then(() => {
73+
return import("./unobservable-global-async-evaluation-count-reset-c_FIXTURE.js");
74+
}).then(() => {
75+
pD = import("./unobservable-global-async-evaluation-count-reset-d_FIXTURE.js");
76+
return pE_start.promise;
77+
}).then(() => {
78+
pB.resolve();
79+
return Promise.all([pA, pD]);
80+
}).then(() => {
81+
assert.compareArray(logs, ["A", "D"], "A should evaluate before D");
82+
}).then($DONE, $DONE);

0 commit comments

Comments
 (0)