tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mjs/src/builtin/ModuleObject.cpp | 131++++++++++++++++++++++++++++++++++---------------------------------------------
Mjs/src/builtin/ModuleObject.h | 54++++++++++++++++++++++++++++++++++--------------------
Mjs/src/jit-test/tests/modules/async-eval-state.js | 91+++++++++++++++++++++++++++++++------------------------------------------------
Mjs/src/jit-test/tests/modules/shell-wrapper.js | 4++--
Mjs/src/shell/ShellModuleObjectWrapper.cpp | 22++++++++++++++--------
Mjs/src/vm/Modules.cpp | 96++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mjs/src/vm/Runtime.cpp | 2+-
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++;