commit e123932d1854c445432f5b46532b369bab3e0264
parent bc8068f5592a3af1219015633aae1dfed07ba0cc
Author: Nicolò Ribaudo <nribaudo@igalia.com>
Date: Fri, 17 Oct 2025 09:03:34 +0000
Bug 1992208 - Part 2: Only reset moduleAsyncEvaluatingPostOrder when there are no pending modules r=jonco
This patch fixes the bug by aligning our implementation with the spec
note: we cannot simply reset `moduleAsyncEvaluatingPostOrder` when the
last discovered module settles. Instead, we can only reset it when
*all* previously pending modules have settled.
test262 tests: https://github.com/tc39/test262/pull/4600
Differential Revision: https://phabricator.services.mozilla.com/D268740
Diffstat:
3 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
@@ -691,25 +691,26 @@ void ModuleNamespaceObject::ProxyHandler::finalize(JS::GCContext* gcx,
}
}
+// https://tc39.es/ecma262/#sec-IncrementModuleAsyncEvaluationCount
+// 9.6.3 IncrementModuleAsyncEvaluationCount()
static uint32_t IncrementModuleAsyncEvaluationCount(JSRuntime* rt) {
+ if (rt->pendingAsyncModuleEvaluations == 0) {
+ // From the spec NOTE:
+ // An implementation may unobservably reset [[ModuleAsyncEvaluationCount]]
+ // to 0 whenever there are no pending modules.
+ rt->moduleAsyncEvaluatingPostOrder = 0;
+ }
+
uint32_t ordinal = rt->moduleAsyncEvaluatingPostOrder;
MOZ_ASSERT(ordinal != ASYNC_EVALUATING_POST_ORDER_DONE);
MOZ_ASSERT(ordinal != ASYNC_EVALUATING_POST_ORDER_UNSET);
MOZ_ASSERT(ordinal < ASYNC_EVALUATING_POST_ORDER_MAX_VALUE);
rt->moduleAsyncEvaluatingPostOrder++;
- return ordinal;
-}
-// Reset the runtime's moduleAsyncEvaluatingPostOrder counter when the last
-// module that was async evaluating is finished.
-//
-// The graph is not re-entrant and any future modules will be independent from
-// this one.
-static void MaybeResetPostOrderCounter(JSRuntime* rt,
- uint32_t finishedPostOrder) {
- if (rt->moduleAsyncEvaluatingPostOrder == finishedPostOrder + 1) {
- rt->moduleAsyncEvaluatingPostOrder = 0;
- }
+ MOZ_ASSERT(rt->pendingAsyncModuleEvaluations < MAX_UINT32);
+ rt->pendingAsyncModuleEvaluations++;
+
+ return ordinal;
}
bool AsyncEvaluationOrder::isUnset() const {
@@ -736,7 +737,8 @@ void AsyncEvaluationOrder::set(JSRuntime* rt) {
void AsyncEvaluationOrder::setDone(JSRuntime* rt) {
MOZ_ASSERT(isInteger());
- MaybeResetPostOrderCounter(rt, value);
+ MOZ_ASSERT(rt->pendingAsyncModuleEvaluations > 0);
+ rt->pendingAsyncModuleEvaluations--;
value = ASYNC_EVALUATING_POST_ORDER_DONE;
}
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
@@ -150,7 +150,8 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
stackFormat_(parentRuntime ? js::StackFormat::Default
: js::StackFormat::SpiderMonkey),
wasmInstances(mutexid::WasmRuntimeInstances),
- moduleAsyncEvaluatingPostOrder(0) {
+ moduleAsyncEvaluatingPostOrder(0),
+ pendingAsyncModuleEvaluations(0) {
JS_COUNT_CTOR(JSRuntime);
liveRuntimesCount++;
diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h
@@ -1074,15 +1074,18 @@ struct JSRuntime {
// threads for purposes of wasm::InterruptRunningCode().
js::ExclusiveData<js::wasm::InstanceVector> wasmInstances;
- // A counter used when recording the order in which modules had their
- // AsyncEvaluation field set to true. This is used to order queued
- // evaluations. This is reset when the last module that was async evaluating
- // is finished.
+ // The [[ModuleAsyncEvaluationCount]] field of agent records
//
- // See https://tc39.es/ecma262/#sec-async-module-execution-fulfilled step 10
- // for use.
+ // See https://tc39.es/ecma262/#sec-agents.
js::MainThreadData<uint32_t> moduleAsyncEvaluatingPostOrder;
+ // A counter used to detect when there are no pending async modules, so
+ // that we can reset [[ModuleAsyncEvaluationCount]] to 0.
+ //
+ // See the note in
+ // https://tc39.es/ecma262/#sec-IncrementModuleAsyncEvaluationCount for use.
+ js::MainThreadData<uint32_t> pendingAsyncModuleEvaluations;
+
// The implementation-defined abstract operation HostLoadImportedModule.
js::MainThreadData<JS::ModuleLoadHook> moduleLoadHook;