commit e02a390fa4b4abd0040e608ed06214c7b04d7eff
parent 577c80a38fcf58c95ef4fc652fa56a4cdcf82ea4
Author: Nicolò Ribaudo <nribaudo@igalia.com>
Date: Fri, 3 Oct 2025 14:44:09 +0000
Bug 1992103 - Align async modules rejection order with fulfillment order r=jonco
This patch inverts the order in which we reject promises in
AsyncModuleExecutionRejected to match AsyncModuleExecutionFulfilled:
first the promise corresponding to the leaf module (the one that throws)
is rejected, and then its ancestors.
This change was discussed at the July 2025 TC39 meeting.
Spec PR: https://github.com/tc39/ecma262/pull/3695
test262: https://github.com/tc39/test262/pull/4591
Differential Revision: https://phabricator.services.mozilla.com/D267210
Diffstat:
1 file changed, 18 insertions(+), 17 deletions(-)
diff --git a/js/src/vm/Modules.cpp b/js/src/vm/Modules.cpp
@@ -2507,10 +2507,10 @@ void js::AsyncModuleExecutionRejected(JSContext* cx,
// Step 2. Assert: module.[[Status]] is evaluating-async.
MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync);
- // Step 3. Assert: module.[[AsyncEvaluation]] is true.
+ // Step 3. Assert: module.[[AsyncEvaluationOrder]] is an integer.
MOZ_ASSERT(module->isAsyncEvaluating());
- // Step 4. 4. Assert: module.[[EvaluationError]] is empty.
+ // Step 4. Assert: module.[[EvaluationError]] is empty.
MOZ_ASSERT(!module->hadEvaluationError());
ModuleObject::onTopLevelEvaluationFinished(module);
@@ -2521,25 +2521,15 @@ void js::AsyncModuleExecutionRejected(JSContext* cx,
// Step 6. Set module.[[Status]] to evaluated.
MOZ_ASSERT(module->status() == ModuleStatus::Evaluated);
+ // Step 7. Set module.[[AsyncEvaluationOrder]] to done.
module->clearAsyncEvaluatingPostOrder();
- // Step 7. For each Cyclic Module Record m of module.[[AsyncParentModules]],
- // do:
- Rooted<ListObject*> parents(cx, module->asyncParentModules());
- Rooted<ModuleObject*> parent(cx);
- for (uint32_t i = 0; i < parents->length(); i++) {
- parent = &parents->get(i).toObject().as<ModuleObject>();
-
- // Step 7.a. Perform AsyncModuleExecutionRejected(m, error).
- AsyncModuleExecutionRejected(cx, parent, error);
- }
-
- // Step 8. If module.[[TopLevelCapability]] is not empty, then:
+ // Step 9. If module.[[TopLevelCapability]] is not empty, then:
if (module->hasTopLevelCapability()) {
- // Step 8.a. Assert: module.[[CycleRoot]] is module.
+ // Step 9.a. Assert: module.[[CycleRoot]] is module.
MOZ_ASSERT(module->getCycleRoot() == module);
- // Step 8.b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]],
+ // Step 9.b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]],
// undefined, error).
if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) {
// If Reject fails, there's nothing more we can do here.
@@ -2547,7 +2537,18 @@ void js::AsyncModuleExecutionRejected(JSContext* cx,
}
}
- // Step 9. Return unused.
+ // Step 10. For each Cyclic Module Record m of module.[[AsyncParentModules]],
+ // do:
+ Rooted<ListObject*> parents(cx, module->asyncParentModules());
+ Rooted<ModuleObject*> parent(cx);
+ for (uint32_t i = 0; i < parents->length(); i++) {
+ parent = &parents->get(i).toObject().as<ModuleObject>();
+
+ // Step 10.a. Perform AsyncModuleExecutionRejected(m, error).
+ AsyncModuleExecutionRejected(cx, parent, error);
+ }
+
+ // Step 11. Return unused.
}
// https://tc39.es/proposal-import-attributes/#sec-evaluate-import-call