diff options
author | Guy Bedford <guybedford@gmail.com> | 2021-03-22 18:36:48 +0200 |
---|---|---|
committer | Guy Bedford <guybedford@gmail.com> | 2021-03-29 23:33:22 +0200 |
commit | 8e46568b1e12fe00581b1292bd6c2b01373f17bd (patch) | |
tree | b87e38cb9e03c666f72bccc8f4fa2849e0826454 /deps/v8/src | |
parent | 45cdc134cd3218fa58877f1c8a5d4b840b0cd4c0 (diff) | |
download | node-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.h | 3 | ||||
-rw-r--r-- | deps/v8/src/diagnostics/objects-debug.cc | 2 | ||||
-rw-r--r-- | deps/v8/src/diagnostics/objects-printer.cc | 1 | ||||
-rw-r--r-- | deps/v8/src/execution/isolate-inl.h | 31 | ||||
-rw-r--r-- | deps/v8/src/execution/isolate.cc | 3 | ||||
-rw-r--r-- | deps/v8/src/execution/isolate.h | 18 | ||||
-rw-r--r-- | deps/v8/src/heap/factory.cc | 2 | ||||
-rw-r--r-- | deps/v8/src/objects/module-inl.h | 8 | ||||
-rw-r--r-- | deps/v8/src/objects/source-text-module.cc | 243 | ||||
-rw-r--r-- | deps/v8/src/objects/source-text-module.h | 36 | ||||
-rw-r--r-- | deps/v8/src/objects/source-text-module.tq | 2 |
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 |