diff options
author | Guy Bedford <guybedford@gmail.com> | 2021-03-22 19:36:48 +0300 |
---|---|---|
committer | Guy Bedford <guybedford@gmail.com> | 2021-04-14 19:15:32 +0300 |
commit | 0c1e878c4ca08f9ea66aa2036031f54938d1e3df (patch) | |
tree | 2008aacc220f5bf7645d3961e95035b36c6c13d7 /deps | |
parent | dd5da301c8c5706ba95ffcb9520679838d032194 (diff) |
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
Backport-PR-URL: https://github.com/nodejs/node/pull/37973
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'deps')
-rw-r--r-- | deps/v8/src/common/globals.h | 3 | ||||
-rw-r--r-- | deps/v8/src/diagnostics/objects-debug.cc | 3 | ||||
-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 | ||||
-rw-r--r-- | deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-subgraph.mjs | 11 | ||||
-rw-r--r-- | deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-1.mjs | 12 | ||||
-rw-r--r-- | deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-2.mjs | 12 | ||||
-rw-r--r-- | deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-async.mjs | 12 | ||||
-rw-r--r-- | deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-start.mjs | 14 | ||||
-rw-r--r-- | deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-x.mjs | 12 |
17 files changed, 336 insertions, 87 deletions
diff --git a/deps/v8/src/common/globals.h b/deps/v8/src/common/globals.h index 4309b702347..70b17ab6495 100644 --- a/deps/v8/src/common/globals.h +++ b/deps/v8/src/common/globals.h @@ -334,6 +334,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 6df2aec1ed3..698b92c6162 100644 --- a/deps/v8/src/diagnostics/objects-debug.cc +++ b/deps/v8/src/diagnostics/objects-debug.cc @@ -1351,7 +1351,8 @@ void SourceTextModule::SourceTextModuleVerify(Isolate* isolate) { (status() == kPreInstantiating && code().IsSharedFunctionInfo()) || (status() == kUninstantiated && code().IsSharedFunctionInfo())); CHECK(top_level_capability().IsUndefined() && !AsyncParentModuleCount() && - !pending_async_dependencies() && !async_evaluating()); + !pending_async_dependencies()); + 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 74aee11d4ce..e23c4674f84 100644 --- a/deps/v8/src/diagnostics/objects-printer.cc +++ b/deps/v8/src/diagnostics/objects-printer.cc @@ -1622,6 +1622,7 @@ void SourceTextModule::SourceTextModulePrint(std::ostream& os) { // NOLINT os << "\n - script: " << Brief(script()); 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 163ab4ea8cb..e39852f2a84 100644 --- a/deps/v8/src/execution/isolate-inl.h +++ b/deps/v8/src/execution/isolate-inl.h @@ -12,6 +12,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.h" namespace v8 { namespace internal { @@ -117,6 +118,36 @@ Isolate::ExceptionScope::~ExceptionScope() { isolate_->set_pending_exception(*pending_exception_); } +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 2008bccb404..ff940d9da66 100644 --- a/deps/v8/src/execution/isolate.cc +++ b/deps/v8/src/execution/isolate.cc @@ -66,6 +66,7 @@ #include "src/objects/prototype.h" #include "src/objects/slots.h" #include "src/objects/smi.h" +#include "src/objects/source-text-module.h" #include "src/objects/stack-frame-info-inl.h" #include "src/objects/visitors.h" #include "src/profiler/heap-profiler.h" @@ -2792,6 +2793,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 78c6ca4b47d..624a5849699 100644 --- a/deps/v8/src/execution/isolate.h +++ b/deps/v8/src/execution/isolate.h @@ -1264,6 +1264,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); @@ -1788,6 +1804,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 7ce1c1e54eb..ba78b952a4e 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -2485,7 +2485,7 @@ Handle<SourceTextModule> Factory::NewSourceTextModule( module->set_top_level_capability(roots.undefined_value()); module->set_flags(0); module->set_async(IsAsyncModule(code->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 d270a9cdfc3..2ce37f231f8 100644 --- a/deps/v8/src/objects/module-inl.h +++ b/deps/v8/src/objects/module-inl.h @@ -37,8 +37,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) ACCESSORS(SourceTextModule, top_level_capability, HeapObject, @@ -141,6 +141,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 692e5c85674..1251157ee35 100644 --- a/deps/v8/src/objects/source-text-module.cc +++ b/deps/v8/src/objects/source-text-module.cc @@ -76,6 +76,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 { DisallowHeapAllocation no_alloc; switch (status()) { @@ -580,6 +589,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( @@ -663,7 +724,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()) @@ -716,81 +777,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( @@ -802,7 +879,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); @@ -814,7 +891,8 @@ void SourceTextModule::AsyncModuleExecutionRejected( module->RecordError(isolate, 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++) { @@ -833,8 +911,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»). @@ -855,7 +933,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(); @@ -1057,7 +1136,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(); @@ -1079,16 +1158,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 aceccd64ba2..c20cd6df536 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-tq.h" // Has to be the last include (doesn't have include guards): @@ -69,10 +70,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, @@ -90,6 +97,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(); @@ -97,13 +106,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 top level promise capability of this module. Will only be defined // for cycle roots. @@ -149,6 +171,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 a9e3ee3db9a..f2eb8e2ce6f 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 diff --git a/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-subgraph.mjs b/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-subgraph.mjs new file mode 100644 index 00000000000..317349b0630 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-subgraph.mjs @@ -0,0 +1,11 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-start.mjs" + +assertEquals(globalThis.test_order, [ + 'async before', 'async after', '1', '2', 'x', 'start' +]); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-1.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-1.mjs new file mode 100644 index 00000000000..d4bc01d8419 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-1.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-async.mjs" + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('1'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-2.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-2.mjs new file mode 100644 index 00000000000..ee37caffd4a --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-2.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-async.mjs" + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('2'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-async.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-async.mjs new file mode 100644 index 00000000000..447f801a8ad --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-async.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('async before'); +await 0; +globalThis.test_order.push('async after'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-start.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-start.mjs new file mode 100644 index 00000000000..861907a9dd7 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-start.mjs @@ -0,0 +1,14 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-1.mjs" +import "modules-skip-async-subgraph-2.mjs" +import "modules-skip-async-subgraph-x.mjs" + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('start'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-x.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-x.mjs new file mode 100644 index 00000000000..813a9bb870e --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-x.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-1.mjs" + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('x'); |