commit bc8068f5592a3af1219015633aae1dfed07ba0cc
parent afe117bf0e8143fccb96a0165e1a9f2ca72b6a49
Author: Nicolò Ribaudo <nribaudo@igalia.com>
Date: Fri, 17 Oct 2025 09:03:34 +0000
Bug 1992208 - Part 1: Align [[AsyncEvaluationOrder]] with the spec r=jonco
This patch does not change any behavior. It aligns our
asyncEvaluationOrder tracking (used for execution of modules that were
waiting for a dependency to finish evaluating asynchronously) with the
spec definition of [[AsyncEvaluationOrder]].
Differential Revision: https://phabricator.services.mozilla.com/D268584
Diffstat:
7 files changed, 192 insertions(+), 208 deletions(-)
diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
@@ -691,6 +691,55 @@ void ModuleNamespaceObject::ProxyHandler::finalize(JS::GCContext* gcx,
}
}
+static uint32_t IncrementModuleAsyncEvaluationCount(JSRuntime* rt) {
+ 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;
+ }
+}
+
+bool AsyncEvaluationOrder::isUnset() const {
+ return value == ASYNC_EVALUATING_POST_ORDER_UNSET;
+}
+
+bool AsyncEvaluationOrder::isDone() const {
+ return value == ASYNC_EVALUATING_POST_ORDER_DONE;
+}
+
+bool AsyncEvaluationOrder::isInteger() const {
+ return value <= ASYNC_EVALUATING_POST_ORDER_MAX_VALUE;
+}
+
+uint32_t AsyncEvaluationOrder::get() const {
+ MOZ_ASSERT(isInteger());
+ return value;
+}
+
+void AsyncEvaluationOrder::set(JSRuntime* rt) {
+ MOZ_ASSERT(isUnset());
+ value = IncrementModuleAsyncEvaluationCount(rt);
+}
+
+void AsyncEvaluationOrder::setDone(JSRuntime* rt) {
+ MOZ_ASSERT(isInteger());
+ MaybeResetPostOrderCounter(rt, value);
+ value = ASYNC_EVALUATING_POST_ORDER_DONE;
+}
+
///////////////////////////////////////////////////////////////////////////
// SyntheticModuleFields
@@ -721,13 +770,11 @@ class js::CyclicModuleFields {
// Flag bits that determine whether other fields are present.
bool hasDfsIndex : 1;
bool hasDfsAncestorIndex : 1;
- bool isAsyncEvaluating : 1;
bool hasPendingAsyncDependencies : 1;
// Fields whose presence is conditional on the flag bits above.
uint32_t dfsIndex = 0;
uint32_t dfsAncestorIndex = 0;
- uint32_t asyncEvaluatingPostOrder = 0;
uint32_t pendingAsyncDependencies = 0;
// Fields describing the layout of exportEntries.
@@ -744,6 +791,7 @@ class js::CyclicModuleFields {
ExportEntryVector exportEntries;
IndirectBindingMap importBindings;
UniquePtr<FunctionDeclarationVector> functionDeclarations;
+ AsyncEvaluationOrder asyncEvaluationOrder;
HeapPtr<PromiseObject*> topLevelCapability;
HeapPtr<ListObject*> asyncParentModules;
HeapPtr<ModuleObject*> cycleRoot;
@@ -767,11 +815,6 @@ class js::CyclicModuleFields {
Maybe<uint32_t> maybeDfsAncestorIndex() const;
void clearDfsIndexes();
- void setAsyncEvaluating(uint32_t postOrder);
- bool getIsAsyncEvaluating() const;
- Maybe<uint32_t> maybeAsyncEvaluatingPostOrder() const;
- void clearAsyncEvaluatingPostOrder();
-
void setPendingAsyncDependencies(uint32_t newValue);
Maybe<uint32_t> maybePendingAsyncDependencies() const;
};
@@ -780,7 +823,6 @@ CyclicModuleFields::CyclicModuleFields()
: hasTopLevelAwait(false),
hasDfsIndex(false),
hasDfsAncestorIndex(false),
- isAsyncEvaluating(false),
hasPendingAsyncDependencies(false) {}
void CyclicModuleFields::trace(JSTracer* trc) {
@@ -855,28 +897,6 @@ void CyclicModuleFields::clearDfsIndexes() {
hasDfsAncestorIndex = false;
}
-void CyclicModuleFields::setAsyncEvaluating(uint32_t postOrder) {
- isAsyncEvaluating = true;
- asyncEvaluatingPostOrder = postOrder;
-}
-
-bool CyclicModuleFields::getIsAsyncEvaluating() const {
- return isAsyncEvaluating;
-}
-
-Maybe<uint32_t> CyclicModuleFields::maybeAsyncEvaluatingPostOrder() const {
- if (!isAsyncEvaluating ||
- asyncEvaluatingPostOrder == ASYNC_EVALUATING_POST_ORDER_CLEARED) {
- return Nothing();
- }
-
- return Some(asyncEvaluatingPostOrder);
-}
-
-void CyclicModuleFields::clearAsyncEvaluatingPostOrder() {
- asyncEvaluatingPostOrder = ASYNC_EVALUATING_POST_ORDER_CLEARED;
-}
-
void CyclicModuleFields::setPendingAsyncDependencies(uint32_t newValue) {
pendingAsyncDependencies = newValue;
hasPendingAsyncDependencies = true;
@@ -1053,32 +1073,6 @@ void ModuleObject::initAsyncSlots(JSContext* cx, bool hasTopLevelAwait,
cyclicModuleFields()->asyncParentModules = asyncParentModules;
}
-static uint32_t NextPostOrder(JSRuntime* rt) {
- uint32_t ordinal = rt->moduleAsyncEvaluatingPostOrder;
- MOZ_ASSERT(ordinal != ASYNC_EVALUATING_POST_ORDER_CLEARED);
- MOZ_ASSERT(ordinal < MAX_UINT32);
- 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 = ASYNC_EVALUATING_POST_ORDER_INIT;
- }
-}
-
-void ModuleObject::setAsyncEvaluating() {
- MOZ_ASSERT(!isAsyncEvaluating());
- uint32_t postOrder = NextPostOrder(runtimeFromMainThread());
- cyclicModuleFields()->setAsyncEvaluating(postOrder);
-}
-
void ModuleObject::initScriptSlots(HandleScript script) {
MOZ_ASSERT(script);
MOZ_ASSERT(script->sourceObject());
@@ -1189,8 +1183,12 @@ bool ModuleObject::hasTopLevelAwait() const {
return cyclicModuleFields()->hasTopLevelAwait;
}
-bool ModuleObject::isAsyncEvaluating() const {
- return cyclicModuleFields()->getIsAsyncEvaluating();
+AsyncEvaluationOrder& ModuleObject::asyncEvaluationOrder() {
+ return cyclicModuleFields()->asyncEvaluationOrder;
+}
+
+AsyncEvaluationOrder const& ModuleObject::asyncEvaluationOrder() const {
+ return cyclicModuleFields()->asyncEvaluationOrder;
}
Maybe<uint32_t> ModuleObject::maybeDfsIndex() const {
@@ -1267,23 +1265,6 @@ uint32_t ModuleObject::pendingAsyncDependencies() const {
return maybePendingAsyncDependencies().value();
}
-Maybe<uint32_t> ModuleObject::maybeAsyncEvaluatingPostOrder() const {
- return cyclicModuleFields()->maybeAsyncEvaluatingPostOrder();
-}
-
-uint32_t ModuleObject::getAsyncEvaluatingPostOrder() const {
- return cyclicModuleFields()->maybeAsyncEvaluatingPostOrder().value();
-}
-
-void ModuleObject::clearAsyncEvaluatingPostOrder() {
- MOZ_ASSERT(status() == ModuleStatus::Evaluated);
-
- JSRuntime* rt = runtimeFromMainThread();
- MaybeResetPostOrderCounter(rt, getAsyncEvaluatingPostOrder());
-
- cyclicModuleFields()->clearAsyncEvaluatingPostOrder();
-}
-
void ModuleObject::setPendingAsyncDependencies(uint32_t newValue) {
cyclicModuleFields()->setPendingAsyncDependencies(newValue);
}
diff --git a/js/src/builtin/ModuleObject.h b/js/src/builtin/ModuleObject.h
@@ -11,13 +11,14 @@
#include "mozilla/Maybe.h" // mozilla::Maybe
#include "mozilla/Span.h"
+#include <cstdint> // UINT32_MAX
#include <stddef.h> // size_t
#include <stdint.h> // int32_t, uint32_t
-#include "gc/Barrier.h" // HeapPtr
-#include "gc/ZoneAllocator.h" // CellAllocPolicy
-#include "js/Class.h" // JSClass, ObjectOpResult
-#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
+#include "gc/Barrier.h" // HeapPtr
+#include "gc/ZoneAllocator.h" // CellAllocPolicy
+#include "js/Class.h" // JSClass, ObjectOpResult
+#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
#include "js/GCVector.h"
#include "js/Id.h" // jsid
#include "js/Modules.h"
@@ -322,25 +323,41 @@ enum class ModuleStatus : int8_t {
Evaluated_Error
};
-// Special values for CyclicModuleFields' asyncEvaluatingPostOrderSlot field,
-// which is used as part of the implementation of the AsyncEvaluation field of
-// cyclic module records.
+// Special values for CyclicModuleFields' asyncEvaluationOrderSlot field,
+// which represents the AsyncEvaluationOrder field of cyclic module records.
//
-// The spec requires us to be able to tell the order in which the field was set
-// to true for async evaluating modules.
-//
-// This is arranged by using an integer to record the order. After evaluation is
-// complete the value is set to ASYNC_EVALUATING_POST_ORDER_CLEARED.
+// AsyncEvaluationOrder can have three states:
+// - a positive integer, represented by values <=
+// ASYNC_EVALUATING_POST_ORDER_MAX_VALUE
+// - ~unset~, represented by ASYNC_EVALUATING_POST_ORDER_UNSET
+// - ~done~, represented by ASYNC_EVALUATING_POST_ORDER_DONE
//
// See https://tc39.es/ecma262/#sec-cyclic-module-records for field defintion.
// See https://tc39.es/ecma262/#sec-async-module-execution-fulfilled for sort
// requirement.
-// Initial value for the runtime's counter used to generate these values.
-constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_INIT = 1;
+// Value that the field is initially set to.
+constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_UNSET = UINT32_MAX;
// Value that the field is set to after being cleared.
-constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_CLEARED = 0;
+constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_DONE = UINT32_MAX - 1;
+
+constexpr uint32_t ASYNC_EVALUATING_POST_ORDER_MAX_VALUE = UINT32_MAX - 2;
+
+class AsyncEvaluationOrder {
+ private:
+ uint32_t value = ASYNC_EVALUATING_POST_ORDER_UNSET;
+
+ public:
+ bool isUnset() const;
+ bool isInteger() const;
+ bool isDone() const;
+
+ uint32_t get() const;
+
+ void set(JSRuntime* rt);
+ void setDone(JSRuntime* rt);
+};
// The map used by [[LoadedModules]] in Realm Record Fields, Script Record
// Fields, and additional fields of Cyclic Module Records.
@@ -435,8 +452,6 @@ class ModuleObject : public NativeObject {
static PromiseObject* createTopLevelCapability(JSContext* cx,
Handle<ModuleObject*> module);
bool hasTopLevelAwait() const;
- bool isAsyncEvaluating() const;
- void setAsyncEvaluating();
void setEvaluationError(HandleValue newValue);
void setPendingAsyncDependencies(uint32_t newValue);
void setInitialTopLevelCapability(Handle<PromiseObject*> capability);
@@ -446,9 +461,8 @@ class ModuleObject : public NativeObject {
ListObject* asyncParentModules() const;
mozilla::Maybe<uint32_t> maybePendingAsyncDependencies() const;
uint32_t pendingAsyncDependencies() const;
- mozilla::Maybe<uint32_t> maybeAsyncEvaluatingPostOrder() const;
- uint32_t getAsyncEvaluatingPostOrder() const;
- void clearAsyncEvaluatingPostOrder();
+ AsyncEvaluationOrder& asyncEvaluationOrder();
+ AsyncEvaluationOrder const& asyncEvaluationOrder() const;
void setCycleRoot(ModuleObject* cycleRoot);
ModuleObject* getCycleRoot() const;
bool hasCyclicModuleFields() const;
diff --git a/js/src/jit-test/tests/modules/async-eval-state.js b/js/src/jit-test/tests/modules/async-eval-state.js
@@ -1,15 +1,18 @@
// Test module fields related to asynchronous evaluation.
+const UNSET = -1;
+const DONE = -2;
+
{
let m = parseModule('');
assertEq(m.status, "New");
moduleLink(m);
- assertEq(m.isAsyncEvaluating, false);
+ assertEq(m.asyncEvaluationOrder, UNSET);
assertEq(m.status, "Linked");
moduleEvaluate(m);
- assertEq(m.isAsyncEvaluating, false);
+ assertEq(m.asyncEvaluationOrder, UNSET);
assertEq(m.status, "Evaluated");
}
@@ -17,17 +20,15 @@
let m = parseModule('await 1;');
moduleLink(m);
- assertEq(m.isAsyncEvaluating, false);
+ assertEq(m.asyncEvaluationOrder, UNSET);
moduleEvaluate(m);
- assertEq(m.isAsyncEvaluating, true);
assertEq(m.status, "EvaluatingAsync");
- assertEq(m.asyncEvaluatingPostOrder, 1);
+ assertEq(m.asyncEvaluationOrder, 0);
drainJobQueue();
- assertEq(m.isAsyncEvaluating, true);
assertEq(m.status, "Evaluated");
- assertEq(m.asyncEvaluatingPostOrder, undefined);
+ assertEq(m.asyncEvaluationOrder, DONE);
}
{
@@ -35,30 +36,26 @@
moduleLink(m);
moduleEvaluate(m).catch(() => 0);
- assertEq(m.isAsyncEvaluating, true);
assertEq(m.status, "EvaluatingAsync");
- assertEq(m.asyncEvaluatingPostOrder, 1);
+ assertEq(m.asyncEvaluationOrder, 0);
drainJobQueue();
- assertEq(m.isAsyncEvaluating, true);
assertEq(m.status, "Evaluated");
assertEq(m.evaluationError, 2);
- assertEq(m.asyncEvaluatingPostOrder, undefined);
+ assertEq(m.asyncEvaluationOrder, DONE);
}
{
let m = parseModule('throw 1; await 2;');
moduleLink(m);
moduleEvaluate(m).catch(() => 0);
- assertEq(m.isAsyncEvaluating, true);
assertEq(m.status, "EvaluatingAsync");
- assertEq(m.asyncEvaluatingPostOrder, 1);
+ assertEq(m.asyncEvaluationOrder, 0);
drainJobQueue();
- assertEq(m.isAsyncEvaluating, true);
assertEq(m.status, "Evaluated");
assertEq(m.evaluationError, 1);
- assertEq(m.asyncEvaluatingPostOrder, undefined);
+ assertEq(m.asyncEvaluationOrder, DONE);
}
{
@@ -68,18 +65,16 @@
moduleLink(b);
moduleEvaluate(b);
- assertEq(a.isAsyncEvaluating, false);
assertEq(a.status, "Evaluated");
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, UNSET);
assertEq(b.status, "EvaluatingAsync");
- assertEq(b.asyncEvaluatingPostOrder, 1);
+ assertEq(b.asyncEvaluationOrder, 0);
drainJobQueue();
- assertEq(a.isAsyncEvaluating, false);
assertEq(a.status, "Evaluated");
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, UNSET);
assertEq(b.status, "Evaluated");
- assertEq(b.asyncEvaluatingPostOrder, undefined);
+ assertEq(b.asyncEvaluationOrder, DONE);
}
{
@@ -89,20 +84,16 @@
moduleLink(b);
moduleEvaluate(b);
- assertEq(a.isAsyncEvaluating, true);
assertEq(a.status, "EvaluatingAsync");
- assertEq(a.asyncEvaluatingPostOrder, 1);
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, 0);
assertEq(b.status, "EvaluatingAsync");
- assertEq(b.asyncEvaluatingPostOrder, 2);
+ assertEq(b.asyncEvaluationOrder, 1);
drainJobQueue();
- assertEq(a.isAsyncEvaluating, true);
assertEq(a.status, "Evaluated");
- assertEq(a.asyncEvaluatingPostOrder, undefined);
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, DONE);
assertEq(b.status, "Evaluated");
- assertEq(b.asyncEvaluatingPostOrder, undefined);
+ assertEq(b.asyncEvaluationOrder, DONE);
}
{
@@ -115,27 +106,21 @@
moduleLink(c);
moduleEvaluate(c);
- assertEq(a.isAsyncEvaluating, true);
assertEq(a.status, "EvaluatingAsync");
- assertEq(a.asyncEvaluatingPostOrder, 1);
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, 0);
assertEq(b.status, "EvaluatingAsync");
- assertEq(b.asyncEvaluatingPostOrder, 2);
- assertEq(c.isAsyncEvaluating, true);
+ assertEq(b.asyncEvaluationOrder, 1);
assertEq(c.status, "EvaluatingAsync");
- assertEq(c.asyncEvaluatingPostOrder, 3);
+ assertEq(c.asyncEvaluationOrder, 2);
resolve(1);
drainJobQueue();
- assertEq(a.isAsyncEvaluating, true);
assertEq(a.status, "Evaluated");
- assertEq(a.asyncEvaluatingPostOrder, undefined);
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, DONE);
assertEq(b.status, "Evaluated");
- assertEq(b.asyncEvaluatingPostOrder, undefined);
- assertEq(c.isAsyncEvaluating, true);
+ assertEq(b.asyncEvaluationOrder, DONE);
assertEq(c.status, "Evaluated");
- assertEq(c.asyncEvaluatingPostOrder, undefined);
+ assertEq(c.asyncEvaluationOrder, DONE);
}
{
@@ -146,10 +131,10 @@
moduleLink(b);
moduleEvaluate(b).catch(() => 0);
assertEq(a.status, "Evaluated");
- assertEq(a.isAsyncEvaluating, false);
+ assertEq(a.asyncEvaluationOrder, UNSET);
assertEq(a.evaluationError, 1);
assertEq(b.status, "Evaluated");
- assertEq(b.isAsyncEvaluating, false);
+ assertEq(b.asyncEvaluationOrder, UNSET);
assertEq(b.evaluationError, 1);
}
@@ -160,20 +145,18 @@
moduleLink(b);
moduleEvaluate(b).catch(() => 0);
- assertEq(a.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, 0);
assertEq(a.status, "EvaluatingAsync");
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(b.asyncEvaluationOrder, 1);
assertEq(b.status, "EvaluatingAsync");
drainJobQueue();
- assertEq(a.isAsyncEvaluating, true);
assertEq(a.status, "Evaluated");
assertEq(a.evaluationError, 1);
- assertEq(a.asyncEvaluatingPostOrder, undefined);
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, DONE);
assertEq(b.status, "Evaluated");
assertEq(b.evaluationError, 1);
- assertEq(b.asyncEvaluatingPostOrder, undefined);
+ assertEq(b.asyncEvaluationOrder, DONE);
}
{
@@ -183,18 +166,16 @@
moduleLink(b);
moduleEvaluate(b).catch(() => 0);
- assertEq(a.isAsyncEvaluating, true);
assertEq(a.status, "EvaluatingAsync");
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, 0);
assertEq(b.status, "EvaluatingAsync");
+ assertEq(b.asyncEvaluationOrder, 1);
drainJobQueue();
- assertEq(a.isAsyncEvaluating, true);
assertEq(a.status, "Evaluated");
assertEq(a.evaluationError, 2);
- assertEq(a.asyncEvaluatingPostOrder, undefined);
- assertEq(b.isAsyncEvaluating, true);
+ assertEq(a.asyncEvaluationOrder, DONE);
assertEq(b.status, "Evaluated");
assertEq(b.evaluationError, 2);
- assertEq(b.asyncEvaluatingPostOrder, undefined);
+ assertEq(b.asyncEvaluationOrder, DONE);
}
diff --git a/js/src/jit-test/tests/modules/shell-wrapper.js b/js/src/jit-test/tests/modules/shell-wrapper.js
@@ -189,11 +189,11 @@ const m = parseModule(`
`);
assertEq(m.hasTopLevelAwait, false);
assertEq(m.topLevelCapability, undefined);
-assertEq(m.asyncEvaluatingPostOrder, undefined);
+assertEq(m.asyncEvaluationOrder, -1);
assertEq(m.asyncParentModules[0], undefined);
assertEq(m.pendingAsyncDependencies, undefined);
testGetter(m, "hasTopLevelAwait");
testGetter(m, "topLevelCapability");
-testGetter(m, "asyncEvaluatingPostOrder");
+testGetter(m, "asyncEvaluationOrder");
testGetter(m, "asyncParentModules");
testGetter(m, "pendingAsyncDependencies");
diff --git a/js/src/shell/ShellModuleObjectWrapper.cpp b/js/src/shell/ShellModuleObjectWrapper.cpp
@@ -248,6 +248,16 @@ static Value Uint32OrUndefinedValue(mozilla::Maybe<uint32_t> x) {
return Uint32Value(x.value());
}
+static Value AsyncEvaluationOrderInt32Value(AsyncEvaluationOrder x) {
+ if (x.isUnset()) {
+ return Int32Value(-1);
+ }
+ if (x.isDone()) {
+ return Int32Value(-2);
+ }
+ return Uint32Value(x.get());
+}
+
static Value ColumnNumberOneOriginValue(JS::ColumnNumberOneOrigin x) {
uint32_t column = x.oneOriginValue();
MOZ_ASSERT(column <= INT32_MAX);
@@ -479,10 +489,8 @@ DEFINE_GETTER_FUNCTIONS(ModuleObject, hasTopLevelAwait, BooleanValue,
IdentFilter)
DEFINE_GETTER_FUNCTIONS(ModuleObject, maybeTopLevelCapability,
ObjectOrUndefinedValue, IdentFilter)
-DEFINE_GETTER_FUNCTIONS(ModuleObject, isAsyncEvaluating, BooleanValue,
- IdentFilter)
-DEFINE_GETTER_FUNCTIONS(ModuleObject, maybeAsyncEvaluatingPostOrder,
- Uint32OrUndefinedValue, IdentFilter)
+DEFINE_GETTER_FUNCTIONS(ModuleObject, asyncEvaluationOrder,
+ AsyncEvaluationOrderInt32Value, IdentFilter)
DEFINE_GETTER_FUNCTIONS(ModuleObject, asyncParentModules, ObjectOrNullValue,
ListToArrayFilter<ShellModuleObjectWrapper>)
DEFINE_GETTER_FUNCTIONS(ModuleObject, maybePendingAsyncDependencies,
@@ -509,10 +517,8 @@ static const JSPropertySpec ShellModuleObjectWrapper_accessors[] = {
0),
JS_PSG("topLevelCapability",
ShellModuleObjectWrapper_maybeTopLevelCapabilityGetter, 0),
- JS_PSG("isAsyncEvaluating",
- ShellModuleObjectWrapper_isAsyncEvaluatingGetter, 0),
- JS_PSG("asyncEvaluatingPostOrder",
- ShellModuleObjectWrapper_maybeAsyncEvaluatingPostOrderGetter, 0),
+ JS_PSG("asyncEvaluationOrder",
+ ShellModuleObjectWrapper_asyncEvaluationOrderGetter, 0),
JS_PSG("asyncParentModules",
ShellModuleObjectWrapper_asyncParentModulesGetter, 0),
JS_PSG("pendingAsyncDependencies",
diff --git a/js/src/vm/Modules.cpp b/js/src/vm/Modules.cpp
@@ -2032,7 +2032,7 @@ static bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
// Step 10.b. Assert: module.[[EvaluationError]] is empty.
MOZ_ASSERT(!module->hadEvaluationError());
- // Step 10.c. If module.[[AsyncEvaluation]] is false, then:
+ // Step 10.c. If module.[[Status]] is evalated, then:
if (module->status() == ModuleStatus::Evaluated) {
// Step 10.c.ii. Perform ! Call(capability.[[Resolve]], undefined,
// undefined).
@@ -2116,11 +2116,8 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
Rooted<ModuleRequestObject*> required(cx);
Rooted<ModuleObject*> requiredModule(cx);
for (const RequestedModule& request : module->requestedModules()) {
- // Step 11.a. Let requiredModule be ! GetImportedModule(module,
+ // Step 11.a. Let requiredModule be GetImportedModule(module,
// required).
- // Step 11.b. NOTE: Link must be completed successfully prior to invoking
- // this method, so every requested module is guaranteed to
- // resolve successfully.
required = request.moduleRequest();
requiredModule = GetImportedModule(cx, module, required);
if (!requiredModule) {
@@ -2128,43 +2125,43 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
}
MOZ_ASSERT(requiredModule->status() >= ModuleStatus::Linked);
- // Step 11.c. Set index to ? InnerModuleEvaluation(requiredModule, stack,
+ // Step 11.b. Set index to ? InnerModuleEvaluation(requiredModule, stack,
// index).
if (!InnerModuleEvaluation(cx, requiredModule, stack, index, &index)) {
return false;
}
- // Step 11.d. If requiredModule is a Cyclic Module Record, then:
+ // Step 11.c. If requiredModule is a Cyclic Module Record, then:
if (requiredModule->hasCyclicModuleFields()) {
- // Step 11.d.i. Assert: requiredModule.[[Status]] is either evaluating,
+ // Step 11.c.i. Assert: requiredModule.[[Status]] is either evaluating,
// evaluating-async, or evaluated.
MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating ||
requiredModule->status() == ModuleStatus::EvaluatingAsync ||
requiredModule->status() == ModuleStatus::Evaluated);
- // Step 11.d.ii. Assert: requiredModule.[[Status]] is evaluating if and
+ // Step 11.c.ii. Assert: requiredModule.[[Status]] is evaluating if and
// only if requiredModule is in stack.
MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) ==
ContainsElement(stack, requiredModule));
- // Step 11.d.iii. If requiredModule.[[Status]] is evaluating, then:
+ // Step 11.c.iii. If requiredModule.[[Status]] is evaluating, then:
if (requiredModule->status() == ModuleStatus::Evaluating) {
- // Step 11.d.iii.1. Set module.[[DFSAncestorIndex]] to
+ // Step 11.c.iii.1. Set module.[[DFSAncestorIndex]] to
// min(module.[[DFSAncestorIndex]],
// requiredModule.[[DFSAncestorIndex]]).
module->setDfsAncestorIndex(std::min(
module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex()));
} else {
- // Step 11.d.iv. Else:
- // Step 11.d.iv.1. Set requiredModule to requiredModule.[[CycleRoot]].
+ // Step 11.c.iv. Else:
+ // Step 11.c.iv.1. Set requiredModule to requiredModule.[[CycleRoot]].
requiredModule = requiredModule->getCycleRoot();
- // Step 11.d.iv.2. Assert: requiredModule.[[Status]] is evaluating-async
+ // Step 11.c.iv.2. Assert: requiredModule.[[Status]] is evaluating-async
// or evaluated.
MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync ||
requiredModule->status() == ModuleStatus::Evaluated);
- // Step 11.d.iv.3. If requiredModule.[[EvaluationError]] is not empty,
+ // Step 11.c.iv.3. If requiredModule.[[EvaluationError]] is not empty,
// return ? requiredModule.[[EvaluationError]].
if (requiredModule->hadEvaluationError()) {
Rooted<Value> error(cx, requiredModule->evaluationError());
@@ -2173,10 +2170,10 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
}
}
- // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then:
- if (requiredModule->isAsyncEvaluating() &&
- requiredModule->status() != ModuleStatus::Evaluated) {
- // Step 11.d.v.2. Append module to
+ // Step 11.c.v. If requiredModule.[[AsyncEvaluationOrder]] is an integer,
+ // then:
+ if (requiredModule->asyncEvaluationOrder().isInteger()) {
+ // Step 11.c.v.2. Append module to
// requiredModule.[[AsyncParentModules]].
if (!ModuleObject::appendAsyncParentModule(cx, requiredModule,
module)) {
@@ -2194,15 +2191,12 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
// Step 12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is
// true, then:
if (module->pendingAsyncDependencies() > 0 || module->hasTopLevelAwait()) {
- // Step 12.a. Assert: module.[[AsyncEvaluation]] is false and was never
- // previously set to true.
- MOZ_ASSERT(!module->isAsyncEvaluating());
+ // Step 12.a. Assert: module.[[AsyncEvaluationOrder]] is unset.
+ MOZ_ASSERT(module->asyncEvaluationOrder().isUnset());
- // Step 12.b. Set module.[[AsyncEvaluation]] to true.
- // Step 12.c. NOTE: The order in which module records have their
- // [[AsyncEvaluation]] fields transition to true is
- // significant. (See 16.2.1.5.2.4.)
- module->setAsyncEvaluating();
+ // Step 12.b. Set module.[[AsyncEvaluationOrder]] to
+ // IncrementModuleAsyncEvaluationCount().
+ module->asyncEvaluationOrder().set(cx->runtime());
// Step 12.d. If module.[[PendingAsyncDependencies]] is 0, perform
// ExecuteAsyncModule(module).
@@ -2235,21 +2229,26 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
// Step 16.b.ii. Remove the last element of stack.
requiredModule = stack.popCopy();
- // Step 16.b.iv. If requiredModule.[[AsyncEvaluation]] is false, set
+ // Step 16.b.iii. Assert: requiredModule.[[AsyncEvaluationOrder]] is
+ // either an integer or unset
+ MOZ_ASSERT(requiredModule->asyncEvaluationOrder().isInteger() ||
+ requiredModule->asyncEvaluationOrder().isUnset());
+
+ // Step 16.b.v. If requiredModule.[[AsyncEvaluationOrder]] is unset, set
// requiredModule.[[Status]] to evaluated.
- if (!requiredModule->isAsyncEvaluating()) {
+ if (requiredModule->asyncEvaluationOrder().isUnset()) {
requiredModule->setStatus(ModuleStatus::Evaluated);
} else {
- // Step 16.b.v. Otherwise, set requiredModule.[[Status]] to
- // evaluating-async.
+ // Step 16.b.vi. Otherwise, set requiredModule.[[Status]] to
+ // evaluating-async.
requiredModule->setStatus(ModuleStatus::EvaluatingAsync);
}
- // Step 16.b.vi. If requiredModule and module are the same Module Record,
- // set done to true.
+ // Step 16.b.vii. If requiredModule and module are the same Module Record,
+ // set done to true.
done = requiredModule == module;
- // Step 16.b.vii. Set requiredModule.[[CycleRoot]] to module.
+ // Step 16.b.viii. Set requiredModule.[[CycleRoot]] to module.
requiredModule->setCycleRoot(module);
}
}
@@ -2303,8 +2302,8 @@ static bool GatherAvailableModuleAncestors(
// Step 1.a.ii. Assert: m.[[EvaluationError]] is empty.
MOZ_ASSERT(!m->hadEvaluationError());
- // Step 1.a.iii. Assert: m.[[AsyncEvaluation]] is true.
- MOZ_ASSERT(m->isAsyncEvaluating());
+ // Step 1.a.iii. Assert: m.[[AsyncEvaluationOrder]] is an integer.
+ MOZ_ASSERT(m->asyncEvaluationOrder().isInteger());
// Step 1.a.iv. Assert: m.[[PendingAsyncDependencies]] > 0.
MOZ_ASSERT(m->pendingAsyncDependencies() > 0);
@@ -2336,8 +2335,8 @@ static bool GatherAvailableModuleAncestors(
struct EvalOrderComparator {
bool operator()(ModuleObject* a, ModuleObject* b, bool* lessOrEqualp) {
- int32_t result = int32_t(a->getAsyncEvaluatingPostOrder()) -
- int32_t(b->getAsyncEvaluatingPostOrder());
+ int32_t result = int32_t(a->asyncEvaluationOrder().get()) -
+ int32_t(b->asyncEvaluationOrder().get());
*lessOrEqualp = (result <= 0);
return true;
}
@@ -2372,8 +2371,8 @@ void js::AsyncModuleExecutionFulfilled(JSContext* cx,
// Step 2. Assert: module.[[Status]] is evaluating-async.
MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync);
- // Step 3. Assert: module.[[AsyncEvaluation]] is true.
- MOZ_ASSERT(module->isAsyncEvaluating());
+ // Step 3. Assert: module.[[AsyncEvaluationOrder]] is an integer.
+ MOZ_ASSERT(module->asyncEvaluationOrder().isInteger());
// Step 4. Assert: module.[[EvaluationError]] is empty.
MOZ_ASSERT(!module->hadEvaluationError());
@@ -2406,12 +2405,12 @@ void js::AsyncModuleExecutionFulfilled(JSContext* cx,
scratch.begin(), EvalOrderComparator()));
// Step 11. Assert: All elements of sortedExecList have their
- // [[AsyncEvaluation]] field set to true,
+ // [[AsyncEvaluationOrder]] field set to an integer,
// [[PendingAsyncDependencies]] field set to 0, and
// [[EvaluationError]] field set to empty.
#ifdef DEBUG
for (ModuleObject* m : execList) {
- MOZ_ASSERT(m->isAsyncEvaluating());
+ MOZ_ASSERT(m->asyncEvaluationOrder().isInteger());
MOZ_ASSERT(m->pendingAsyncDependencies() == 0);
MOZ_ASSERT(!m->hadEvaluationError());
}
@@ -2421,9 +2420,11 @@ void js::AsyncModuleExecutionFulfilled(JSContext* cx,
ModuleObject::onTopLevelEvaluationFinished(module);
+ // Step 5. Set module.[[AsyncEvaluationOrder]] to done.
+ module->asyncEvaluationOrder().setDone(cx->runtime());
+
// Step 6. Set module.[[Status]] to evaluated.
module->setStatus(ModuleStatus::Evaluated);
- module->clearAsyncEvaluatingPostOrder();
// Step 7. If module.[[TopLevelCapability]] is not empty, then:
if (module->hasTopLevelCapability()) {
@@ -2466,9 +2467,10 @@ void js::AsyncModuleExecutionFulfilled(JSContext* cx,
RejectExecutionWithPendingException(cx, m);
} else {
// Step 12.c.iii. Else:
- // Step 12.c.iii.1. Set m.[[Status]] to evaluated.
+ // Step 12.c.iii.1. Set m.[[AsyncEvaluationOrder]] to done.
+ m->asyncEvaluationOrder().setDone(m->zone()->runtimeFromMainThread());
+ // Step 12.c.iii.2. Set m.[[Status]] to evaluated.
m->setStatus(ModuleStatus::Evaluated);
- m->clearAsyncEvaluatingPostOrder();
// Step 12.c.iii.2. If m.[[TopLevelCapability]] is not empty, then:
if (m->hasTopLevelCapability()) {
@@ -2508,7 +2510,7 @@ void js::AsyncModuleExecutionRejected(JSContext* cx,
MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync);
// Step 3. Assert: module.[[AsyncEvaluationOrder]] is an integer.
- MOZ_ASSERT(module->isAsyncEvaluating());
+ MOZ_ASSERT(module->asyncEvaluationOrder().isInteger());
// Step 4. Assert: module.[[EvaluationError]] is empty.
MOZ_ASSERT(!module->hadEvaluationError());
@@ -2522,7 +2524,7 @@ void js::AsyncModuleExecutionRejected(JSContext* cx,
MOZ_ASSERT(module->status() == ModuleStatus::Evaluated);
// Step 7. Set module.[[AsyncEvaluationOrder]] to done.
- module->clearAsyncEvaluatingPostOrder();
+ module->asyncEvaluationOrder().setDone(cx->runtime());
// Step 9. If module.[[TopLevelCapability]] is not empty, then:
if (module->hasTopLevelCapability()) {
diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp
@@ -150,7 +150,7 @@ JSRuntime::JSRuntime(JSRuntime* parentRuntime)
stackFormat_(parentRuntime ? js::StackFormat::Default
: js::StackFormat::SpiderMonkey),
wasmInstances(mutexid::WasmRuntimeInstances),
- moduleAsyncEvaluatingPostOrder(ASYNC_EVALUATING_POST_ORDER_INIT) {
+ moduleAsyncEvaluatingPostOrder(0) {
JS_COUNT_CTOR(JSRuntime);
liveRuntimesCount++;