summaryrefslogtreecommitdiff
path: root/deps/v8/src
diff options
context:
space:
mode:
authorGuy Bedford <guybedford@gmail.com>2021-03-22 18:36:48 +0200
committerGuy Bedford <guybedford@gmail.com>2021-03-29 23:33:22 +0200
commit8e46568b1e12fe00581b1292bd6c2b01373f17bd (patch)
treeb87e38cb9e03c666f72bccc8f4fa2849e0826454 /deps/v8/src
parent45cdc134cd3218fa58877f1c8a5d4b840b0cd4c0 (diff)
downloadnode-new-8e46568b1e12fe00581b1292bd6c2b01373f17bd.tar.gz
deps: backport v8 f19142e6
[top-level-await] Implement the new post-order requirement for async subgraphs Refs: https://github.com/v8/v8/commit/f19142e6139979da3a177cb0b9f382e459f5ccec PR-URL: https://github.com/nodejs/node/pull/37864 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'deps/v8/src')
-rw-r--r--deps/v8/src/common/globals.h3
-rw-r--r--deps/v8/src/diagnostics/objects-debug.cc2
-rw-r--r--deps/v8/src/diagnostics/objects-printer.cc1
-rw-r--r--deps/v8/src/execution/isolate-inl.h31
-rw-r--r--deps/v8/src/execution/isolate.cc3
-rw-r--r--deps/v8/src/execution/isolate.h18
-rw-r--r--deps/v8/src/heap/factory.cc2
-rw-r--r--deps/v8/src/objects/module-inl.h8
-rw-r--r--deps/v8/src/objects/source-text-module.cc243
-rw-r--r--deps/v8/src/objects/source-text-module.h36
-rw-r--r--deps/v8/src/objects/source-text-module.tq2
11 files changed, 262 insertions, 87 deletions
diff --git a/deps/v8/src/common/globals.h b/deps/v8/src/common/globals.h
index d9d502aa51..5b9dd0923f 100644
--- a/deps/v8/src/common/globals.h
+++ b/deps/v8/src/common/globals.h
@@ -356,6 +356,9 @@ constexpr int kUC16Size = sizeof(uc16); // NOLINT
// 128 bit SIMD value size.
constexpr int kSimd128Size = 16;
+// Maximum ordinal used for tracking asynchronous module evaluation order.
+constexpr unsigned kMaxModuleAsyncEvaluatingOrdinal = (1 << 30) - 1;
+
// FUNCTION_ADDR(f) gets the address of a C function f.
#define FUNCTION_ADDR(f) (reinterpret_cast<v8::internal::Address>(f))
diff --git a/deps/v8/src/diagnostics/objects-debug.cc b/deps/v8/src/diagnostics/objects-debug.cc
index 203548eb44..bf841db010 100644
--- a/deps/v8/src/diagnostics/objects-debug.cc
+++ b/deps/v8/src/diagnostics/objects-debug.cc
@@ -1523,7 +1523,7 @@ void SourceTextModule::SourceTextModuleVerify(Isolate* isolate) {
}
CHECK(!AsyncParentModuleCount());
CHECK(!pending_async_dependencies());
- CHECK(!async_evaluating());
+ CHECK(!IsAsyncEvaluating());
}
CHECK_EQ(requested_modules().length(), info().module_requests().length());
diff --git a/deps/v8/src/diagnostics/objects-printer.cc b/deps/v8/src/diagnostics/objects-printer.cc
index bd03a837a8..df848422c7 100644
--- a/deps/v8/src/diagnostics/objects-printer.cc
+++ b/deps/v8/src/diagnostics/objects-printer.cc
@@ -1731,6 +1731,7 @@ void SourceTextModule::SourceTextModulePrint(std::ostream& os) { // NOLINT
os << "\n - requested_modules: " << Brief(requested_modules());
os << "\n - import_meta: " << Brief(import_meta());
os << "\n - cycle_root: " << Brief(cycle_root());
+ os << "\n - async_evaluating_ordinal: " << async_evaluating_ordinal();
os << "\n";
}
diff --git a/deps/v8/src/execution/isolate-inl.h b/deps/v8/src/execution/isolate-inl.h
index 7a81bf7d24..28beea58ee 100644
--- a/deps/v8/src/execution/isolate-inl.h
+++ b/deps/v8/src/execution/isolate-inl.h
@@ -13,6 +13,7 @@
#include "src/objects/property-cell.h"
#include "src/objects/regexp-match-info.h"
#include "src/objects/shared-function-info.h"
+#include "src/objects/source-text-module-inl.h"
namespace v8 {
namespace internal {
@@ -119,6 +120,36 @@ bool Isolate::IsAnyInitialArrayPrototype(JSArray array) {
return IsInAnyContext(array, Context::INITIAL_ARRAY_PROTOTYPE_INDEX);
}
+void Isolate::DidFinishModuleAsyncEvaluation(unsigned ordinal) {
+ // To address overflow, the ordinal is reset when the async module with the
+ // largest vended ordinal finishes evaluating. Modules are evaluated in
+ // ascending order of their async_evaluating_ordinal.
+ //
+ // While the specification imposes a global total ordering, the intention is
+ // that for each async module, all its parents are totally ordered by when
+ // they first had their [[AsyncEvaluating]] bit set.
+ //
+ // The module with largest vended ordinal finishes evaluating implies that the
+ // async dependency as well as all other modules in that module's graph
+ // depending on async dependencies are finished evaluating.
+ //
+ // If the async dependency participates in other module graphs (e.g. via
+ // dynamic import, or other <script type=module> tags), those module graphs
+ // must have been evaluated either before or after the async dependency is
+ // settled, as the concrete Evaluate() method on cyclic module records is
+ // neither reentrant nor performs microtask checkpoints during its
+ // evaluation. If before, then all modules that depend on the async
+ // dependencies were given an ordinal that ensure they are relatively ordered,
+ // before the global ordinal was reset. If after, then the async evaluating
+ // ordering does not apply, as the dependency is no longer asynchronous.
+ //
+ // https://tc39.es/ecma262/#sec-moduleevaluation
+ if (ordinal + 1 == next_module_async_evaluating_ordinal_) {
+ next_module_async_evaluating_ordinal_ =
+ SourceTextModule::kFirstAsyncEvaluatingOrdinal;
+ }
+}
+
#define NATIVE_CONTEXT_FIELD_ACCESSOR(index, type, name) \
Handle<type> Isolate::name() { \
return Handle<type>(raw_native_context().name(), this); \
diff --git a/deps/v8/src/execution/isolate.cc b/deps/v8/src/execution/isolate.cc
index 248cba7a20..6f7efe3986 100644
--- a/deps/v8/src/execution/isolate.cc
+++ b/deps/v8/src/execution/isolate.cc
@@ -73,6 +73,7 @@
#include "src/objects/prototype.h"
#include "src/objects/slots.h"
#include "src/objects/smi.h"
+#include "src/objects/source-text-module-inl.h"
#include "src/objects/stack-frame-info-inl.h"
#include "src/objects/visitors.h"
#include "src/profiler/heap-profiler.h"
@@ -2897,6 +2898,8 @@ Isolate::Isolate(std::unique_ptr<i::IsolateAllocator> isolate_allocator)
#if V8_SFI_HAS_UNIQUE_ID
next_unique_sfi_id_(0),
#endif
+ next_module_async_evaluating_ordinal_(
+ SourceTextModule::kFirstAsyncEvaluatingOrdinal),
cancelable_task_manager_(new CancelableTaskManager()) {
TRACE_ISOLATE(constructor);
CheckIsolateLayout();
diff --git a/deps/v8/src/execution/isolate.h b/deps/v8/src/execution/isolate.h
index 6c458860e8..cd819c5087 100644
--- a/deps/v8/src/execution/isolate.h
+++ b/deps/v8/src/execution/isolate.h
@@ -1369,6 +1369,22 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
return id;
}
+ // https://github.com/tc39/proposal-top-level-await/pull/159
+ // TODO(syg): Update to actual spec link once merged.
+ //
+ // According to the spec, modules that depend on async modules (i.e. modules
+ // with top-level await) must be evaluated in order in which their
+ // [[AsyncEvaluating]] flags were set to true. V8 tracks this global total
+ // order with next_module_async_evaluating_ordinal_. Each module that sets its
+ // [[AsyncEvaluating]] to true grabs the next ordinal.
+ unsigned NextModuleAsyncEvaluatingOrdinal() {
+ unsigned ordinal = next_module_async_evaluating_ordinal_++;
+ CHECK_LT(ordinal, kMaxModuleAsyncEvaluatingOrdinal);
+ return ordinal;
+ }
+
+ inline void DidFinishModuleAsyncEvaluation(unsigned ordinal);
+
void AddNearHeapLimitCallback(v8::NearHeapLimitCallback, void* data);
void RemoveNearHeapLimitCallback(v8::NearHeapLimitCallback callback,
size_t heap_limit);
@@ -1970,6 +1986,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
std::atomic<int> next_unique_sfi_id_;
#endif
+ unsigned next_module_async_evaluating_ordinal_;
+
// Vector of callbacks before a Call starts execution.
std::vector<BeforeCallEnteredCallback> before_call_entered_callbacks_;
diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc
index 4d12c9e9da..bd7ec8915c 100644
--- a/deps/v8/src/heap/factory.cc
+++ b/deps/v8/src/heap/factory.cc
@@ -2365,7 +2365,7 @@ Handle<SourceTextModule> Factory::NewSourceTextModule(
module->set_dfs_ancestor_index(-1);
module->set_flags(0);
module->set_async(IsAsyncModule(sfi->kind()));
- module->set_async_evaluating(false);
+ module->set_async_evaluating_ordinal(SourceTextModule::kNotAsyncEvaluated);
module->set_cycle_root(roots.the_hole_value());
module->set_async_parent_modules(*async_parent_modules);
module->set_pending_async_dependencies(0);
diff --git a/deps/v8/src/objects/module-inl.h b/deps/v8/src/objects/module-inl.h
index b65c1ec4cd..90d1972cda 100644
--- a/deps/v8/src/objects/module-inl.h
+++ b/deps/v8/src/objects/module-inl.h
@@ -38,8 +38,8 @@ SMI_ACCESSORS(Module, status, kStatusOffset)
SMI_ACCESSORS(Module, hash, kHashOffset)
BOOL_ACCESSORS(SourceTextModule, flags, async, AsyncBit::kShift)
-BOOL_ACCESSORS(SourceTextModule, flags, async_evaluating,
- AsyncEvaluatingBit::kShift)
+BIT_FIELD_ACCESSORS(SourceTextModule, flags, async_evaluating_ordinal,
+ SourceTextModule::AsyncEvaluatingOrdinalBits)
ACCESSORS(SourceTextModule, async_parent_modules, ArrayList,
kAsyncParentModulesOffset)
@@ -139,6 +139,10 @@ int SourceTextModule::AsyncParentModuleCount() {
return async_parent_modules().Length();
}
+bool SourceTextModule::IsAsyncEvaluating() const {
+ return async_evaluating_ordinal() >= kFirstAsyncEvaluatingOrdinal;
+}
+
bool SourceTextModule::HasPendingAsyncDependencies() {
DCHECK_GE(pending_async_dependencies(), 0);
return pending_async_dependencies() > 0;
diff --git a/deps/v8/src/objects/source-text-module.cc b/deps/v8/src/objects/source-text-module.cc
index 4747283a15..a905444da5 100644
--- a/deps/v8/src/objects/source-text-module.cc
+++ b/deps/v8/src/objects/source-text-module.cc
@@ -77,6 +77,15 @@ class Module::ResolveSet
Zone* zone_;
};
+struct SourceTextModule::AsyncEvaluatingOrdinalCompare {
+ bool operator()(Handle<SourceTextModule> lhs,
+ Handle<SourceTextModule> rhs) const {
+ DCHECK(lhs->IsAsyncEvaluating());
+ DCHECK(rhs->IsAsyncEvaluating());
+ return lhs->async_evaluating_ordinal() < rhs->async_evaluating_ordinal();
+ }
+};
+
SharedFunctionInfo SourceTextModule::GetSharedFunctionInfo() const {
DisallowGarbageCollection no_gc;
switch (status()) {
@@ -602,6 +611,58 @@ void SourceTextModule::FetchStarExports(Isolate* isolate,
module->set_exports(*exports);
}
+void SourceTextModule::GatherAsyncParentCompletions(
+ Isolate* isolate, Zone* zone, Handle<SourceTextModule> start,
+ AsyncParentCompletionSet* exec_list) {
+ // The spec algorithm is recursive. It is transformed to an equivalent
+ // iterative one here.
+ ZoneStack<Handle<SourceTextModule>> worklist(zone);
+ worklist.push(start);
+
+ while (!worklist.empty()) {
+ Handle<SourceTextModule> module = worklist.top();
+ worklist.pop();
+
+ // 1. Assert: module.[[Status]] is evaluated.
+ DCHECK_EQ(module->status(), kEvaluated);
+
+ // 2. For each Module m of module.[[AsyncParentModules]], do
+ for (int i = module->AsyncParentModuleCount(); i-- > 0;) {
+ Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i);
+
+ // a. If execList does not contain m and
+ // m.[[CycleRoot]].[[EvaluationError]] is empty, then
+ if (exec_list->find(m) == exec_list->end() &&
+ m->GetCycleRoot(isolate)->status() != kErrored) {
+ // i. Assert: m.[[EvaluationError]] is empty.
+ DCHECK_NE(m->status(), kErrored);
+
+ // ii. Assert: m.[[AsyncEvaluating]] is true.
+ DCHECK(m->IsAsyncEvaluating());
+
+ // iii. Assert: m.[[PendingAsyncDependencies]] > 0.
+ DCHECK(m->HasPendingAsyncDependencies());
+
+ // iv. Set m.[[PendingAsyncDependencies]] to
+ // m.[[PendingAsyncDependencies]] - 1.
+ m->DecrementPendingAsyncDependencies();
+
+ // v. If m.[[PendingAsyncDependencies]] is equal to 0, then
+ if (!m->HasPendingAsyncDependencies()) {
+ // 1. Append m to execList.
+ exec_list->insert(m);
+
+ // 2. If m.[[Async]] is false,
+ // perform ! GatherAsyncParentCompletions(m, execList).
+ if (!m->async()) worklist.push(m);
+ }
+ }
+ }
+ }
+
+ // 3. Return undefined.
+}
+
Handle<JSModuleNamespace> SourceTextModule::GetModuleNamespace(
Isolate* isolate, Handle<SourceTextModule> module, int module_request) {
Handle<Module> requested_module(
@@ -654,7 +715,7 @@ MaybeHandle<Object> SourceTextModule::EvaluateMaybeAsync(
CHECK_EQ(module->status(), kEvaluated);
// b. If module.[[AsyncEvaluating]] is false, then
- if (!module->async_evaluating()) {
+ if (!module->IsAsyncEvaluating()) {
// i. Perform ! Call(capability.[[Resolve]], undefined,
// «undefined»).
JSPromise::Resolve(capability, isolate->factory()->undefined_value())
@@ -707,81 +768,97 @@ MaybeHandle<Object> SourceTextModule::Evaluate(
void SourceTextModule::AsyncModuleExecutionFulfilled(
Isolate* isolate, Handle<SourceTextModule> module) {
- // 1. Assert: module.[[Status]] is "evaluated".
- CHECK(module->status() == kEvaluated || module->status() == kErrored);
+ // 1. Assert: module.[[AsyncEvaluating]] is true.
+ DCHECK(module->IsAsyncEvaluating());
- // 2. If module.[[AsyncEvaluating]] is false,
- if (!module->async_evaluating()) {
- // a. Assert: module.[[EvaluationError]] is not undefined.
- CHECK_EQ(module->status(), kErrored);
-
- // b. Return undefined.
- return;
- }
-
- // 3. Assert: module.[[EvaluationError]] is undefined.
+ // 2. Assert: module.[[EvaluationError]] is undefined.
CHECK_EQ(module->status(), kEvaluated);
- // 4. Set module.[[AsyncEvaluating]] to false.
- module->set_async_evaluating(false);
+ // 3. Set module.[[AsyncEvaluating]] to false.
+ isolate->DidFinishModuleAsyncEvaluation(module->async_evaluating_ordinal());
+ module->set_async_evaluating_ordinal(kAsyncEvaluateDidFinish);
- // 5. For each Module m of module.[[AsyncParentModules]], do
- for (int i = 0; i < module->AsyncParentModuleCount(); i++) {
- Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i);
+ // 4. If module.[[TopLevelCapability]] is not empty, then
+ if (!module->top_level_capability().IsUndefined(isolate)) {
+ // a. Assert: module.[[CycleRoot]] is equal to module.
+ DCHECK_EQ(*module->GetCycleRoot(isolate), *module);
- // a. Decrement m.[[PendingAsyncDependencies]] by 1.
- m->DecrementPendingAsyncDependencies();
+ // i. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], undefined,
+ // «undefined»).
+ Handle<JSPromise> capability(
+ JSPromise::cast(module->top_level_capability()), isolate);
+ JSPromise::Resolve(capability, isolate->factory()->undefined_value())
+ .ToHandleChecked();
+ }
- // b. If m.[[PendingAsyncDependencies]] is 0 and m.[[EvaluationError]] is
- // undefined, then
- if (!m->HasPendingAsyncDependencies() && m->status() == kEvaluated) {
- // i. Assert: m.[[AsyncEvaluating]] is true.
- DCHECK(m->async_evaluating());
+ // 5. Let execList be a new empty List.
+ Zone zone(isolate->allocator(), ZONE_NAME);
+ AsyncParentCompletionSet exec_list(&zone);
- // ii. If m.[[CycleRoot]].[[EvaluationError]] is not undefined,
- // return undefined.
- if (m->GetCycleRoot(isolate)->status() == kErrored) {
- return;
- }
+ // 6. Perform ! GatherAsyncParentCompletions(module, execList).
+ GatherAsyncParentCompletions(isolate, &zone, module, &exec_list);
+
+ // 7. Let sortedExecList be a List of elements that are the elements of
+ // execList, in the order in which they had their [[AsyncEvaluating]]
+ // fields set to true in InnerModuleEvaluation.
+ //
+ // This step is implemented by AsyncParentCompletionSet, which is a set
+ // ordered on async_evaluating_ordinal.
- // iii. If m.[[Async]] is true, then
- if (m->async()) {
- // 1. Perform ! ExecuteAsyncModule(m).
- ExecuteAsyncModule(isolate, m);
+ // 8. Assert: All elements of sortedExecList have their [[AsyncEvaluating]]
+ // field set to true, [[PendingAsyncDependencies]] field set to 0 and
+ // [[EvaluationError]] field set to undefined.
+#ifdef DEBUG
+ for (Handle<SourceTextModule> m : exec_list) {
+ DCHECK(m->IsAsyncEvaluating());
+ DCHECK(!m->HasPendingAsyncDependencies());
+ DCHECK_NE(m->status(), kErrored);
+ }
+#endif
+
+ // 9. For each Module m of sortedExecList, do
+ for (Handle<SourceTextModule> m : exec_list) {
+ // i. If m.[[AsyncEvaluating]] is false, then
+ if (!m->IsAsyncEvaluating()) {
+ // a. Assert: m.[[EvaluatingError]] is not empty.
+ DCHECK_EQ(m->status(), kErrored);
+ } else if (m->async()) {
+ // ii. Otherwise, if m.[[Async]] is *true*, then
+ // a. Perform ! ExecuteAsyncModule(m).
+ ExecuteAsyncModule(isolate, m);
+ } else {
+ // iii. Otherwise,
+ // a. Let _result_ be m.ExecuteModule().
+ Handle<Object> unused_result;
+ // b. If _result_ is an abrupt completion,
+ if (!ExecuteModule(isolate, m).ToHandle(&unused_result)) {
+ // 1. Perform ! AsyncModuleExecutionRejected(m, result.[[Value]]).
+ Handle<Object> exception(isolate->pending_exception(), isolate);
+ isolate->clear_pending_exception();
+ AsyncModuleExecutionRejected(isolate, m, exception);
} else {
- // iv. Otherwise,
- // 1. Let result be m.ExecuteModule().
- // 2. If result is a normal completion,
- Handle<Object> unused_result;
- if (ExecuteModule(isolate, m).ToHandle(&unused_result)) {
- // a. Perform ! AsyncModuleExecutionFulfilled(m).
- AsyncModuleExecutionFulfilled(isolate, m);
- } else {
- // 3. Otherwise,
- // a. Perform ! AsyncModuleExecutionRejected(m,
- // result.[[Value]]).
- Handle<Object> exception(isolate->pending_exception(), isolate);
- isolate->clear_pending_exception();
- AsyncModuleExecutionRejected(isolate, m, exception);
+ // c. Otherwise,
+ // 1. Set m.[[AsyncEvaluating]] to false.
+ isolate->DidFinishModuleAsyncEvaluation(m->async_evaluating_ordinal());
+ m->set_async_evaluating_ordinal(kAsyncEvaluateDidFinish);
+
+ // 2. If m.[[TopLevelCapability]] is not empty, then
+ if (!m->top_level_capability().IsUndefined(isolate)) {
+ // i. Assert: m.[[CycleRoot]] is equal to m.
+ DCHECK_EQ(*m->GetCycleRoot(isolate), *m);
+
+ // ii. Perform ! Call(m.[[TopLevelCapability]].[[Resolve]],
+ // undefined, «undefined»).
+ Handle<JSPromise> capability(
+ JSPromise::cast(m->top_level_capability()), isolate);
+ JSPromise::Resolve(capability, isolate->factory()->undefined_value())
+ .ToHandleChecked();
}
}
}
}
- // 6. If module.[[TopLevelCapability]] is not undefined, then
- if (!module->top_level_capability().IsUndefined(isolate)) {
- // a. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]].
- DCHECK_EQ(module->dfs_index(), module->dfs_ancestor_index());
-
- // b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]],
- // undefined, «undefined»).
- Handle<JSPromise> capability(
- JSPromise::cast(module->top_level_capability()), isolate);
- JSPromise::Resolve(capability, isolate->factory()->undefined_value())
- .ToHandleChecked();
- }
-
- // 7. Return undefined.
+ // 10. Return undefined.
}
void SourceTextModule::AsyncModuleExecutionRejected(
@@ -793,7 +870,7 @@ void SourceTextModule::AsyncModuleExecutionRejected(
CHECK(module->status() == kEvaluated || module->status() == kErrored);
// 2. If module.[[AsyncEvaluating]] is false,
- if (!module->async_evaluating()) {
+ if (!module->IsAsyncEvaluating()) {
// a. Assert: module.[[EvaluationError]] is not undefined.
CHECK_EQ(module->status(), kErrored);
@@ -805,7 +882,8 @@ void SourceTextModule::AsyncModuleExecutionRejected(
Module::RecordError(isolate, module, exception);
// 5. Set module.[[AsyncEvaluating]] to false.
- module->set_async_evaluating(false);
+ isolate->DidFinishModuleAsyncEvaluation(module->async_evaluating_ordinal());
+ module->set_async_evaluating_ordinal(kAsyncEvaluateDidFinish);
// 6. For each Module m of module.[[AsyncParentModules]], do
for (int i = 0; i < module->AsyncParentModuleCount(); i++) {
@@ -824,8 +902,8 @@ void SourceTextModule::AsyncModuleExecutionRejected(
// 7. If module.[[TopLevelCapability]] is not undefined, then
if (!module->top_level_capability().IsUndefined(isolate)) {
- // a. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]].
- DCHECK(module->dfs_index() == module->dfs_ancestor_index());
+ // a. Assert: module.[[CycleRoot]] is equal to module.
+ DCHECK_EQ(*module->GetCycleRoot(isolate), *module);
// b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]],
// undefined, «error»).
@@ -846,7 +924,8 @@ void SourceTextModule::ExecuteAsyncModule(Isolate* isolate,
DCHECK(module->async());
// 3. Set module.[[AsyncEvaluating]] to true.
- module->set_async_evaluating(true);
+ module->set_async_evaluating_ordinal(
+ isolate->NextModuleAsyncEvaluatingOrdinal());
// 4. Let capability be ! NewPromiseCapability(%Promise%).
Handle<JSPromise> capability = isolate->factory()->NewJSPromise();
@@ -1048,7 +1127,7 @@ MaybeHandle<Object> SourceTextModule::InnerModuleEvaluation(
}
}
// v. If requiredModule.[[AsyncEvaluating]] is true, then
- if (required_module->async_evaluating()) {
+ if (required_module->IsAsyncEvaluating()) {
// 1. Set module.[[PendingAsyncDependencies]] to
// module.[[PendingAsyncDependencies]] + 1.
module->IncrementPendingAsyncDependencies();
@@ -1070,16 +1149,26 @@ MaybeHandle<Object> SourceTextModule::InnerModuleEvaluation(
// synchronous modules, but return undefined for AsyncModules.
Handle<Object> result = isolate->factory()->undefined_value();
- // 14. If module.[[PendingAsyncDependencies]] is > 0, set
- // module.[[AsyncEvaluating]] to true.
- if (module->HasPendingAsyncDependencies()) {
- module->set_async_evaluating(true);
- } else if (module->async()) {
- // 15. Otherwise, if module.[[Async]] is true,
- // perform ! ExecuteAsyncModule(module).
- SourceTextModule::ExecuteAsyncModule(isolate, module);
+ // 14. If module.[[PendingAsyncDependencies]] > 0 or module.[[Async]] is
+ // true, then
+ if (module->HasPendingAsyncDependencies() || module->async()) {
+ // a. Assert: module.[[AsyncEvaluating]] is false and was never previously
+ // set to true.
+ DCHECK_EQ(module->async_evaluating_ordinal(), kNotAsyncEvaluated);
+
+ // b. Set module.[[AsyncEvaluating]] to true.
+ // NOTE: The order in which modules transition to async evaluating is
+ // significant.
+ module->set_async_evaluating_ordinal(
+ isolate->NextModuleAsyncEvaluatingOrdinal());
+
+ // c. If module.[[PendingAsyncDependencies]] is 0,
+ // perform ! ExecuteAsyncModule(_module_).
+ if (!module->HasPendingAsyncDependencies()) {
+ SourceTextModule::ExecuteAsyncModule(isolate, module);
+ }
} else {
- // 16. Otherwise, perform ? module.ExecuteModule().
+ // 15. Otherwise, perform ? module.ExecuteModule().
ASSIGN_RETURN_ON_EXCEPTION(isolate, result, ExecuteModule(isolate, module),
Object);
}
diff --git a/deps/v8/src/objects/source-text-module.h b/deps/v8/src/objects/source-text-module.h
index 325fb7a2e3..873a0cd729 100644
--- a/deps/v8/src/objects/source-text-module.h
+++ b/deps/v8/src/objects/source-text-module.h
@@ -7,6 +7,7 @@
#include "src/objects/module.h"
#include "src/objects/promise.h"
+#include "src/zone/zone-containers.h"
#include "torque-generated/bit-fields.h"
// Has to be the last include (doesn't have include guards):
@@ -73,10 +74,16 @@ class SourceTextModule
SubclassBodyDescriptor<Module::BodyDescriptor,
FixedBodyDescriptor<kCodeOffset, kSize, kSize>>;
+ static constexpr unsigned kFirstAsyncEvaluatingOrdinal = 2;
+
private:
friend class Factory;
friend class Module;
+ struct AsyncEvaluatingOrdinalCompare;
+ using AsyncParentCompletionSet =
+ ZoneSet<Handle<SourceTextModule>, AsyncEvaluatingOrdinalCompare>;
+
// Appends a tuple of module and generator to the async parent modules
// ArrayList.
inline static void AddAsyncParentModule(Isolate* isolate,
@@ -94,6 +101,8 @@ class SourceTextModule
// Returns the number of async parent modules for a given async child.
inline int AsyncParentModuleCount();
+ inline bool IsAsyncEvaluating() const;
+
inline bool HasPendingAsyncDependencies();
inline void IncrementPendingAsyncDependencies();
inline void DecrementPendingAsyncDependencies();
@@ -101,13 +110,26 @@ class SourceTextModule
// Bits for flags.
DEFINE_TORQUE_GENERATED_SOURCE_TEXT_MODULE_FLAGS()
- // async_evaluating, top_level_capability, pending_async_dependencies, and
- // async_parent_modules are used exclusively during evaluation of async
+ // async_evaluating_ordinal, top_level_capability, pending_async_dependencies,
+ // and async_parent_modules are used exclusively during evaluation of async
// modules and the modules which depend on them.
//
- // Whether or not this module is async and evaluating or currently evaluating
- // an async child.
- DECL_BOOLEAN_ACCESSORS(async_evaluating)
+ // If >1, this module is async and evaluating or currently evaluating an async
+ // child. The integer is an ordinal for when this module first started async
+ // evaluation and is used for sorting async parent modules when determining
+ // which parent module can start executing after an async evaluation
+ // completes.
+ //
+ // If 1, this module has finished async evaluating.
+ //
+ // If 0, this module is not async or has not been async evaluated.
+ static constexpr unsigned kNotAsyncEvaluated = 0;
+ static constexpr unsigned kAsyncEvaluateDidFinish = 1;
+ STATIC_ASSERT(kNotAsyncEvaluated < kAsyncEvaluateDidFinish);
+ STATIC_ASSERT(kAsyncEvaluateDidFinish < kFirstAsyncEvaluatingOrdinal);
+ STATIC_ASSERT(kMaxModuleAsyncEvaluatingOrdinal ==
+ AsyncEvaluatingOrdinalBits::kMax);
+ DECL_PRIMITIVE_ACCESSORS(async_evaluating_ordinal, unsigned)
// The parent modules of a given async dependency, use async_parent_modules()
// to retrieve the ArrayList representation.
@@ -151,6 +173,10 @@ class SourceTextModule
Handle<SourceTextModule> module, Zone* zone,
UnorderedModuleSet* visited);
+ static void GatherAsyncParentCompletions(Isolate* isolate, Zone* zone,
+ Handle<SourceTextModule> start,
+ AsyncParentCompletionSet* exec_list);
+
// Implementation of spec concrete method Evaluate.
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> EvaluateMaybeAsync(
Isolate* isolate, Handle<SourceTextModule> module);
diff --git a/deps/v8/src/objects/source-text-module.tq b/deps/v8/src/objects/source-text-module.tq
index d49aa79b15..5a21807cc5 100644
--- a/deps/v8/src/objects/source-text-module.tq
+++ b/deps/v8/src/objects/source-text-module.tq
@@ -6,7 +6,7 @@ type SourceTextModuleInfo extends FixedArray;
bitfield struct SourceTextModuleFlags extends uint31 {
async: bool: 1 bit;
- async_evaluating: bool: 1 bit;
+ async_evaluating_ordinal: uint32: 30 bit;
}
@generateCppClass