From d2940bdbb0e28fd002ec31b89f8182bbf63da092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Wed, 15 Oct 2025 18:27:53 +0200 Subject: [PATCH] Test that resetting [[ModuleAsyncEvaluationCount]] is unobserbable --- ...-async-evaluation-count-reset-a_FIXTURE.js | 6 ++ ...-async-evaluation-count-reset-b_FIXTURE.js | 6 ++ ...-async-evaluation-count-reset-c_FIXTURE.js | 4 + ...-async-evaluation-count-reset-d_FIXTURE.js | 7 ++ ...-async-evaluation-count-reset-e_FIXTURE.js | 5 ++ ...nc-evaluation-count-reset-setup_FIXTURE.js | 7 ++ ...ble-global-async-evaluation-count-reset.js | 82 +++++++++++++++++++ 7 files changed, 117 insertions(+) create mode 100644 test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-a_FIXTURE.js create mode 100644 test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-b_FIXTURE.js create mode 100644 test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-c_FIXTURE.js create mode 100644 test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-d_FIXTURE.js create mode 100644 test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-e_FIXTURE.js create mode 100644 test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js create mode 100644 test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset.js diff --git a/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-a_FIXTURE.js b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-a_FIXTURE.js new file mode 100644 index 0000000000..cfd99c55ac --- /dev/null +++ b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-a_FIXTURE.js @@ -0,0 +1,6 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +import { logs } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js"; +import "./unobservable-global-async-evaluation-count-reset-b_FIXTURE.js"; +logs.push("A"); diff --git a/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-b_FIXTURE.js b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-b_FIXTURE.js new file mode 100644 index 0000000000..0d02d28631 --- /dev/null +++ b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-b_FIXTURE.js @@ -0,0 +1,6 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +import { pB, pB_start } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js"; +pB_start.resolve(); +await pB.promise; diff --git a/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-c_FIXTURE.js b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-c_FIXTURE.js new file mode 100644 index 0000000000..e15f94d8ad --- /dev/null +++ b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-c_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. + +await 1; diff --git a/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-d_FIXTURE.js b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-d_FIXTURE.js new file mode 100644 index 0000000000..cffc326f0a --- /dev/null +++ b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-d_FIXTURE.js @@ -0,0 +1,7 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +import { logs } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js"; +import "./unobservable-global-async-evaluation-count-reset-e_FIXTURE.js"; +import "./unobservable-global-async-evaluation-count-reset-b_FIXTURE.js"; +logs.push("D"); diff --git a/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-e_FIXTURE.js b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-e_FIXTURE.js new file mode 100644 index 0000000000..62dc105b29 --- /dev/null +++ b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-e_FIXTURE.js @@ -0,0 +1,5 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +import { pE_start } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js"; +pE_start.resolve(); diff --git a/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js new file mode 100644 index 0000000000..2d4606e82d --- /dev/null +++ b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js @@ -0,0 +1,7 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +export const logs = []; +export const pB = Promise.withResolvers(); +export const pB_start = Promise.withResolvers(); +export const pE_start = Promise.withResolvers(); diff --git a/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset.js b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset.js new file mode 100644 index 0000000000..8dadfedf2b --- /dev/null +++ b/test/language/module-code/top-level-await/unobservable-global-async-evaluation-count-reset.js @@ -0,0 +1,82 @@ +// Copyright (C) 2025 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: > + An implementation may unobservably reset [[ModuleAsyncEvaluationCount]] to 0 + whenever there are no pending modules. +info: | + IncrementModuleAsyncEvaluationCount ( ) + 1. Let AR be the Agent Record of the surrounding agent. + 2. Let count be AR.[[ModuleAsyncEvaluationCount]]. + 3. Set AR.[[ModuleAsyncEvaluationCount]] to count + 1. + 4. Return count. + + NOTE: This value is only used to keep track of the relative evaluation order + between pending modules. An implementation may unobservably reset + [[ModuleAsyncEvaluationCount]] to 0 whenever there are no pending modules. + + InnerModuleEvaluation ( module, stack, index ) + ... + 12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is true, then + a. Assert: module.[[AsyncEvaluationOrder]] is unset. + b. Set module.[[AsyncEvaluationOrder]] to IncrementModuleAsyncEvaluationCount(). + ... + + AsyncModuleExecutionFulfilled ( module ) + ... + 9. Perform GatherAvailableAncestors(module, execList). + 10. ... + 11. Let sortedExecList be a List whose elements are the elements of execList, sorted by their [[AsyncEvaluationOrder]] field in ascending order. + 12. For each Cyclic Module Record m of sortedExecList, do + a. If m.[[Status]] is evaluated, then + i. Assert: m.[[EvaluationError]] is not empty. + b. Else if m.[[HasTLA]] is true, then + i. Perform ExecuteAsyncModule(m). + c. Else, + i. Let result be m.ExecuteModule(). + ... + + Module graph (the order of dependencies in each module is important, and it's left-to-right): + ┌─────┐ ┌─────┐ ┌─────┐ + │ A │ │ C │ │ D │ + └─────┘ └─────┘ └─────┘ + │ │ │ + │ ▼ │ + │ ┌─────┐ │ + │ │ E │ │ + │ └─────┘ │ + │ ┌──────────────────┘ + ▼ ▼ + ┌───────┐ + │ B │ + └───────┘ + + Where B and C have top-level await. The test orchestrates the evaluation order such that: + - Import A first + - Once B starts evaluating, import C and immediately resolve its top-level await + - Once C finishes evaluating, import D + - Once E is evaluated, resolve B's await + +esid: sec-IncrementModuleAsyncEvaluationCount +flags: [module, async] +features: [top-level-await, dynamic-import, promise-with-resolvers] +includes: [compareArray.js] +---*/ + +import { logs, pB, pB_start, pE_start } from "./unobservable-global-async-evaluation-count-reset-setup_FIXTURE.js"; + +const pA = import("./unobservable-global-async-evaluation-count-reset-a_FIXTURE.js"); +let pD; + +pB_start.promise.then(() => { + return import("./unobservable-global-async-evaluation-count-reset-c_FIXTURE.js"); +}).then(() => { + pD = import("./unobservable-global-async-evaluation-count-reset-d_FIXTURE.js"); + return pE_start.promise; +}).then(() => { + pB.resolve(); + return Promise.all([pA, pD]); +}).then(() => { + assert.compareArray(logs, ["A", "D"], "A should evaluate before D"); +}).then($DONE, $DONE);