Commit 591d1c9d authored by Joshua Litt's avatar Joshua Litt Committed by Commit Bot

[top-level-await] Implement top-level-await in V8

Implements AsyncModules in SourceTextModule. However, there is no
support in the parser or D8 for actually creating / resolving
AsyncModules. Also adds a flag '--top-level-await,' but the only
external facing change with the flag enabled is that Module::Evaluate
returns a promise.

Bug: v8:9344
Change-Id: Idc722efc1e2aa780d04bdb985bb7920ab969d34e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1728037Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Joshua Litt <joshualitt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63686}
parent cb1c2b0f
...@@ -2049,6 +2049,7 @@ v8_source_set("v8_base_without_compiler") { ...@@ -2049,6 +2049,7 @@ v8_source_set("v8_base_without_compiler") {
"src/builtins/builtins-api.cc", "src/builtins/builtins-api.cc",
"src/builtins/builtins-array.cc", "src/builtins/builtins-array.cc",
"src/builtins/builtins-arraybuffer.cc", "src/builtins/builtins-arraybuffer.cc",
"src/builtins/builtins-async-module.cc",
"src/builtins/builtins-bigint.cc", "src/builtins/builtins-bigint.cc",
"src/builtins/builtins-call.cc", "src/builtins/builtins-call.cc",
"src/builtins/builtins-callsite.cc", "src/builtins/builtins-callsite.cc",
......
...@@ -232,6 +232,10 @@ extern class WeakFixedArray extends HeapObject { length: Smi; } ...@@ -232,6 +232,10 @@ extern class WeakFixedArray extends HeapObject { length: Smi; }
extern class ByteArray extends FixedArrayBase {} extern class ByteArray extends FixedArrayBase {}
@hasSameInstanceTypeAsParent
extern class ArrayList extends FixedArray {
}
type LayoutDescriptor extends ByteArray type LayoutDescriptor extends ByteArray
generates 'TNode<LayoutDescriptor>'; generates 'TNode<LayoutDescriptor>';
type TransitionArray extends WeakFixedArray type TransitionArray extends WeakFixedArray
...@@ -569,9 +573,12 @@ extern class SourceTextModule extends Module { ...@@ -569,9 +573,12 @@ extern class SourceTextModule extends Module {
// Lazily initialized on first access. It's the hole before first access and // Lazily initialized on first access. It's the hole before first access and
// a JSObject afterwards. // a JSObject afterwards.
import_meta: TheHole | JSObject; import_meta: TheHole | JSObject;
async_parent_modules: ArrayList;
top_level_capability: JSPromise | Undefined;
dfs_index: Smi; dfs_index: Smi;
dfs_ancestor_index: Smi; dfs_ancestor_index: Smi;
pending_async_dependencies: Smi;
flags: Smi;
} }
@generateCppClass @generateCppClass
......
// Copyright 2019 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.
#include "src/builtins/builtins-utils-inl.h"
#include "src/objects/module-inl.h"
#include "src/objects/objects-inl.h"
namespace v8 {
namespace internal {
BUILTIN(CallAsyncModuleFulfilled) {
HandleScope handle_scope(isolate);
Handle<SourceTextModule> module(
isolate->global_handles()->Create(*args.at<SourceTextModule>(0)));
SourceTextModule::AsyncModuleExecutionFulfilled(isolate, module);
return ReadOnlyRoots(isolate).undefined_value();
}
BUILTIN(CallAsyncModuleRejected) {
HandleScope handle_scope(isolate);
// Arguments should be a SourceTextModule and an exception object.
DCHECK_EQ(args.length(), 2);
Handle<SourceTextModule> module(
isolate->global_handles()->Create(*args.at<SourceTextModule>(0)));
Handle<Object> exception(args.at(1));
SourceTextModule::AsyncModuleExecutionRejected(isolate, module, exception);
return ReadOnlyRoots(isolate).undefined_value();
}
} // namespace internal
} // namespace v8
...@@ -1130,7 +1130,14 @@ namespace internal { ...@@ -1130,7 +1130,14 @@ namespace internal {
CPP(FinalizationGroupRegister) \ CPP(FinalizationGroupRegister) \
CPP(FinalizationGroupUnregister) \ CPP(FinalizationGroupUnregister) \
CPP(WeakRefConstructor) \ CPP(WeakRefConstructor) \
CPP(WeakRefDeref) CPP(WeakRefDeref) \
\
/* Async modules */ \
TFJ(AsyncModuleEvaluate, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
\
/* CallAsyncModule* are spec anonymyous functions */ \
CPP(CallAsyncModuleFulfilled) \
CPP(CallAsyncModuleRejected)
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
#define BUILTIN_LIST_INTL(CPP, TFJ, TFS) \ #define BUILTIN_LIST_INTL(CPP, TFJ, TFS) \
......
...@@ -19,19 +19,25 @@ class GeneratorBuiltinsAssembler : public CodeStubAssembler { ...@@ -19,19 +19,25 @@ class GeneratorBuiltinsAssembler : public CodeStubAssembler {
: CodeStubAssembler(state) {} : CodeStubAssembler(state) {}
protected: protected:
// Currently, AsyncModules in V8 are built on top of JSAsyncFunctionObjects
// with an initial yield. Thus, we need some way to 'resume' the
// underlying JSAsyncFunctionObject owned by an AsyncModule. To support this
// the body of resume is factored out below, and shared by JSGeneratorObject
// prototype methods as well as AsyncModuleEvaluate. The only difference
// between AsyncModuleEvaluate and JSGeneratorObject::PrototypeNext is
// the expected reciever.
void InnerResume(CodeStubArguments* args, Node* receiver, Node* value,
Node* context, JSGeneratorObject::ResumeMode resume_mode,
char const* const method_name);
void GeneratorPrototypeResume(CodeStubArguments* args, Node* receiver, void GeneratorPrototypeResume(CodeStubArguments* args, Node* receiver,
Node* value, Node* context, Node* value, Node* context,
JSGeneratorObject::ResumeMode resume_mode, JSGeneratorObject::ResumeMode resume_mode,
char const* const method_name); char const* const method_name);
}; };
void GeneratorBuiltinsAssembler::GeneratorPrototypeResume( void GeneratorBuiltinsAssembler::InnerResume(
CodeStubArguments* args, Node* receiver, Node* value, Node* context, CodeStubArguments* args, Node* receiver, Node* value, Node* context,
JSGeneratorObject::ResumeMode resume_mode, char const* const method_name) { JSGeneratorObject::ResumeMode resume_mode, char const* const method_name) {
// Check if the {receiver} is actually a JSGeneratorObject.
ThrowIfNotInstanceType(context, receiver, JS_GENERATOR_OBJECT_TYPE,
method_name);
// Check if the {receiver} is running or already closed. // Check if the {receiver} is running or already closed.
TNode<Smi> receiver_continuation = TNode<Smi> receiver_continuation =
CAST(LoadObjectField(receiver, JSGeneratorObject::kContinuationOffset)); CAST(LoadObjectField(receiver, JSGeneratorObject::kContinuationOffset));
...@@ -111,6 +117,35 @@ void GeneratorBuiltinsAssembler::GeneratorPrototypeResume( ...@@ -111,6 +117,35 @@ void GeneratorBuiltinsAssembler::GeneratorPrototypeResume(
} }
} }
void GeneratorBuiltinsAssembler::GeneratorPrototypeResume(
CodeStubArguments* args, Node* receiver, Node* value, Node* context,
JSGeneratorObject::ResumeMode resume_mode, char const* const method_name) {
// Check if the {receiver} is actually a JSGeneratorObject.
ThrowIfNotInstanceType(context, receiver, JS_GENERATOR_OBJECT_TYPE,
method_name);
InnerResume(args, receiver, value, context, resume_mode, method_name);
}
TF_BUILTIN(AsyncModuleEvaluate, GeneratorBuiltinsAssembler) {
const int kValueArg = 0;
Node* argc =
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
CodeStubArguments args(this, argc);
Node* receiver = args.GetReceiver();
Node* value = args.GetOptionalArgumentValue(kValueArg);
Node* context = Parameter(Descriptor::kContext);
// AsyncModules act like JSAsyncFunctions. Thus we check here
// that the {receiver} is a JSAsyncFunction.
char const* const method_name = "[AsyncModule].evaluate";
ThrowIfNotInstanceType(context, receiver, JS_ASYNC_FUNCTION_OBJECT_TYPE,
method_name);
InnerResume(&args, receiver, value, context, JSGeneratorObject::kNext,
method_name);
}
// ES6 #sec-generator.prototype.next // ES6 #sec-generator.prototype.next
TF_BUILTIN(GeneratorPrototypeNext, GeneratorBuiltinsAssembler) { TF_BUILTIN(GeneratorPrototypeNext, GeneratorBuiltinsAssembler) {
const int kValueArg = 0; const int kValueArg = 0;
......
...@@ -1543,10 +1543,18 @@ void Module::ModuleVerify(Isolate* isolate) { ...@@ -1543,10 +1543,18 @@ void Module::ModuleVerify(Isolate* isolate) {
void SourceTextModule::SourceTextModuleVerify(Isolate* isolate) { void SourceTextModule::SourceTextModuleVerify(Isolate* isolate) {
TorqueGeneratedClassVerifiers::SourceTextModuleVerify(*this, isolate); TorqueGeneratedClassVerifiers::SourceTextModuleVerify(*this, isolate);
CHECK((status() >= kEvaluating && code().IsSourceTextModuleInfo()) || if (status() == kErrored) {
(status() == kInstantiated && code().IsJSGeneratorObject()) || CHECK(code().IsSourceTextModuleInfo());
(status() == kInstantiating && code().IsJSFunction()) || } else if (status() == kEvaluating || status() == kEvaluated) {
(code().IsSharedFunctionInfo())); CHECK(code().IsJSGeneratorObject());
} else {
CHECK((status() == kInstantiated && code().IsJSGeneratorObject()) ||
(status() == kInstantiating && code().IsJSFunction()) ||
(status() == kPreInstantiating && code().IsSharedFunctionInfo()) ||
(status() == kUninstantiated && code().IsSharedFunctionInfo()));
CHECK(top_level_capability().IsUndefined() && !AsyncParentModuleCount() &&
!pending_async_dependencies() && !async_evaluating());
}
CHECK_EQ(requested_modules().length(), info().module_requests().length()); CHECK_EQ(requested_modules().length(), info().module_requests().length());
} }
......
...@@ -208,7 +208,8 @@ DEFINE_IMPLICATION(harmony_import_meta, harmony_dynamic_import) ...@@ -208,7 +208,8 @@ DEFINE_IMPLICATION(harmony_import_meta, harmony_dynamic_import)
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \ V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
V(harmony_weak_refs, "harmony weak references") \ V(harmony_weak_refs, "harmony weak references") \
V(harmony_regexp_match_indices, "harmony regexp match indices") \ V(harmony_regexp_match_indices, "harmony regexp match indices") \
V(harmony_nullish, "harmony nullish operator") V(harmony_nullish, "harmony nullish operator") \
V(harmony_top_level_await, "harmony top level await")
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
#define HARMONY_INPROGRESS(V) \ #define HARMONY_INPROGRESS(V) \
......
...@@ -3051,6 +3051,7 @@ Handle<SourceTextModule> Factory::NewSourceTextModule( ...@@ -3051,6 +3051,7 @@ Handle<SourceTextModule> Factory::NewSourceTextModule(
Handle<FixedArray> requested_modules = Handle<FixedArray> requested_modules =
requested_modules_length > 0 ? NewFixedArray(requested_modules_length) requested_modules_length > 0 ? NewFixedArray(requested_modules_length)
: empty_fixed_array(); : empty_fixed_array();
Handle<ArrayList> async_parent_modules = ArrayList::New(isolate(), 0);
ReadOnlyRoots roots(isolate()); ReadOnlyRoots roots(isolate());
Handle<SourceTextModule> module( Handle<SourceTextModule> module(
...@@ -3070,6 +3071,12 @@ Handle<SourceTextModule> Factory::NewSourceTextModule( ...@@ -3070,6 +3071,12 @@ Handle<SourceTextModule> Factory::NewSourceTextModule(
module->set_import_meta(roots.the_hole_value()); module->set_import_meta(roots.the_hole_value());
module->set_dfs_index(-1); module->set_dfs_index(-1);
module->set_dfs_ancestor_index(-1); module->set_dfs_ancestor_index(-1);
module->set_top_level_capability(roots.undefined_value());
module->set_flags(0);
module->set_async(false);
module->set_async_evaluating(false);
module->set_async_parent_modules(*async_parent_modules);
module->set_pending_async_dependencies(0);
return module; return module;
} }
......
...@@ -870,6 +870,29 @@ void Genesis::CreateIteratorMaps(Handle<JSFunction> empty) { ...@@ -870,6 +870,29 @@ void Genesis::CreateIteratorMaps(Handle<JSFunction> empty) {
generator_next_internal->shared().set_native(false); generator_next_internal->shared().set_native(false);
native_context()->set_generator_next_internal(*generator_next_internal); native_context()->set_generator_next_internal(*generator_next_internal);
// Internal version of async module functions, flagged as non-native such
// that they don't show up in Error traces.
{
Handle<JSFunction> async_module_evaluate_internal =
SimpleCreateFunction(isolate(), factory()->next_string(),
Builtins::kAsyncModuleEvaluate, 1, false);
async_module_evaluate_internal->shared().set_native(false);
native_context()->set_async_module_evaluate_internal(
*async_module_evaluate_internal);
Handle<JSFunction> call_async_module_fulfilled =
SimpleCreateFunction(isolate(), factory()->empty_string(),
Builtins::kCallAsyncModuleFulfilled, 1, false);
native_context()->set_call_async_module_fulfilled(
*call_async_module_fulfilled);
Handle<JSFunction> call_async_module_rejected =
SimpleCreateFunction(isolate(), factory()->empty_string(),
Builtins::kCallAsyncModuleRejected, 1, false);
native_context()->set_call_async_module_rejected(
*call_async_module_rejected);
}
// Create maps for generator functions and their prototypes. Store those // Create maps for generator functions and their prototypes. Store those
// maps in the native context. The "prototype" property descriptor is // maps in the native context. The "prototype" property descriptor is
// writable, non-enumerable, and non-configurable (as per ES6 draft // writable, non-enumerable, and non-configurable (as per ES6 draft
...@@ -4268,6 +4291,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_meta) ...@@ -4268,6 +4291,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_meta)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_sequence) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_sequence)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_optional_chaining) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_optional_chaining)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_nullish) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_nullish)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_top_level_await)
#ifdef V8_INTL_SUPPORT #ifdef V8_INTL_SUPPORT
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_add_calendar_numbering_system) EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_add_calendar_numbering_system)
......
...@@ -37,21 +37,23 @@ enum ContextLookupFlags { ...@@ -37,21 +37,23 @@ enum ContextLookupFlags {
// must always be allocated via Heap::AllocateContext() or // must always be allocated via Heap::AllocateContext() or
// Factory::NewContext. // Factory::NewContext.
#define NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) \ #define NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) \
V(GENERATOR_NEXT_INTERNAL, JSFunction, generator_next_internal) \ V(GENERATOR_NEXT_INTERNAL, JSFunction, generator_next_internal) \
V(MAKE_ERROR_INDEX, JSFunction, make_error) \ V(ASYNC_MODULE_EVALUATE_INTERNAL, JSFunction, \
V(MAKE_RANGE_ERROR_INDEX, JSFunction, make_range_error) \ async_module_evaluate_internal) \
V(MAKE_SYNTAX_ERROR_INDEX, JSFunction, make_syntax_error) \ V(MAKE_ERROR_INDEX, JSFunction, make_error) \
V(MAKE_TYPE_ERROR_INDEX, JSFunction, make_type_error) \ V(MAKE_RANGE_ERROR_INDEX, JSFunction, make_range_error) \
V(MAKE_URI_ERROR_INDEX, JSFunction, make_uri_error) \ V(MAKE_SYNTAX_ERROR_INDEX, JSFunction, make_syntax_error) \
V(OBJECT_CREATE, JSFunction, object_create) \ V(MAKE_TYPE_ERROR_INDEX, JSFunction, make_type_error) \
V(REFLECT_APPLY_INDEX, JSFunction, reflect_apply) \ V(MAKE_URI_ERROR_INDEX, JSFunction, make_uri_error) \
V(REFLECT_CONSTRUCT_INDEX, JSFunction, reflect_construct) \ V(OBJECT_CREATE, JSFunction, object_create) \
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \ V(REFLECT_APPLY_INDEX, JSFunction, reflect_apply) \
V(MATH_POW_INDEX, JSFunction, math_pow) \ V(REFLECT_CONSTRUCT_INDEX, JSFunction, reflect_construct) \
V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \ V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
promise_internal_constructor) \ V(MATH_POW_INDEX, JSFunction, math_pow) \
V(IS_PROMISE_INDEX, JSFunction, is_promise) \ V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \
promise_internal_constructor) \
V(IS_PROMISE_INDEX, JSFunction, is_promise) \
V(PROMISE_THEN_INDEX, JSFunction, promise_then) V(PROMISE_THEN_INDEX, JSFunction, promise_then)
#define NATIVE_CONTEXT_FIELDS(V) \ #define NATIVE_CONTEXT_FIELDS(V) \
...@@ -104,6 +106,8 @@ enum ContextLookupFlags { ...@@ -104,6 +106,8 @@ enum ContextLookupFlags {
V(CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, JSFunction, \ V(CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, JSFunction, \
call_as_constructor_delegate) \ call_as_constructor_delegate) \
V(CALL_AS_FUNCTION_DELEGATE_INDEX, JSFunction, call_as_function_delegate) \ V(CALL_AS_FUNCTION_DELEGATE_INDEX, JSFunction, call_as_function_delegate) \
V(CALL_ASYNC_MODULE_FULFILLED, JSFunction, call_async_module_fulfilled) \
V(CALL_ASYNC_MODULE_REJECTED, JSFunction, call_async_module_rejected) \
V(CALLSITE_FUNCTION_INDEX, JSFunction, callsite_function) \ V(CALLSITE_FUNCTION_INDEX, JSFunction, callsite_function) \
V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \ V(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
V(DATA_PROPERTY_DESCRIPTOR_MAP_INDEX, Map, data_property_descriptor_map) \ V(DATA_PROPERTY_DESCRIPTOR_MAP_INDEX, Map, data_property_descriptor_map) \
......
...@@ -38,9 +38,17 @@ SMI_ACCESSORS(Module, hash, kHashOffset) ...@@ -38,9 +38,17 @@ SMI_ACCESSORS(Module, hash, kHashOffset)
TQ_SMI_ACCESSORS(SourceTextModule, dfs_index) TQ_SMI_ACCESSORS(SourceTextModule, dfs_index)
TQ_SMI_ACCESSORS(SourceTextModule, dfs_ancestor_index) TQ_SMI_ACCESSORS(SourceTextModule, dfs_ancestor_index)
TQ_SMI_ACCESSORS(SourceTextModule, flags)
BOOL_ACCESSORS(SourceTextModule, flags, async, kAsyncBit)
BOOL_ACCESSORS(SourceTextModule, flags, async_evaluating, kAsyncEvaluatingBit)
TQ_SMI_ACCESSORS(SourceTextModule, pending_async_dependencies)
ACCESSORS(SourceTextModule, async_parent_modules, ArrayList,
kAsyncParentModulesOffset)
ACCESSORS(SourceTextModule, top_level_capability, HeapObject,
kTopLevelCapabilityOffset)
SourceTextModuleInfo SourceTextModule::info() const { SourceTextModuleInfo SourceTextModule::info() const {
return (status() >= kEvaluating) return status() == kErrored
? SourceTextModuleInfo::cast(code()) ? SourceTextModuleInfo::cast(code())
: GetSharedFunctionInfo().scope_info().ModuleDescriptorInfo(); : GetSharedFunctionInfo().scope_info().ModuleDescriptorInfo();
} }
...@@ -112,6 +120,37 @@ class UnorderedModuleSet ...@@ -112,6 +120,37 @@ class UnorderedModuleSet
ZoneAllocator<Handle<Module>>(zone)) {} ZoneAllocator<Handle<Module>>(zone)) {}
}; };
void SourceTextModule::AddAsyncParentModule(Isolate* isolate,
Handle<SourceTextModule> module) {
Handle<ArrayList> new_array_list =
ArrayList::Add(isolate, handle(async_parent_modules(), isolate), module);
set_async_parent_modules(*new_array_list);
}
Handle<SourceTextModule> SourceTextModule::GetAsyncParentModule(
Isolate* isolate, int index) {
Handle<SourceTextModule> module(
SourceTextModule::cast(async_parent_modules().Get(index)), isolate);
return module;
}
int SourceTextModule::AsyncParentModuleCount() {
return async_parent_modules().Length();
}
bool SourceTextModule::HasPendingAsyncDependencies() {
DCHECK_GE(pending_async_dependencies(), 0);
return pending_async_dependencies() > 0;
}
void SourceTextModule::IncrementPendingAsyncDependencies() {
set_pending_async_dependencies(pending_async_dependencies() + 1);
}
void SourceTextModule::DecrementPendingAsyncDependencies() {
set_pending_async_dependencies(pending_async_dependencies() - 1);
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "src/api/api-inl.h" #include "src/api/api-inl.h"
#include "src/ast/modules.h" #include "src/ast/modules.h"
#include "src/builtins/accessors.h" #include "src/builtins/accessors.h"
#include "src/heap/heap-inl.h"
#include "src/objects/cell-inl.h" #include "src/objects/cell-inl.h"
#include "src/objects/hash-table-inl.h" #include "src/objects/hash-table-inl.h"
#include "src/objects/js-generator-inl.h" #include "src/objects/js-generator-inl.h"
...@@ -50,12 +51,14 @@ void Module::SetStatus(Status new_status) { ...@@ -50,12 +51,14 @@ void Module::SetStatus(Status new_status) {
set_status(new_status); set_status(new_status);
} }
void Module::RecordError(Isolate* isolate) { void Module::RecordErrorUsingPendingException(Isolate* isolate) {
DisallowHeapAllocation no_alloc; Handle<Object> the_exception(isolate->pending_exception(), isolate);
DCHECK(exception().IsTheHole(isolate)); RecordError(isolate, the_exception);
Object the_exception = isolate->pending_exception(); }
DCHECK(!the_exception.IsTheHole(isolate));
void Module::RecordError(Isolate* isolate, Handle<Object> error) {
DCHECK(exception().IsTheHole(isolate));
DCHECK(!error->IsTheHole(isolate));
if (this->IsSourceTextModule()) { if (this->IsSourceTextModule()) {
Handle<SourceTextModule> self(SourceTextModule::cast(*this), GetIsolate()); Handle<SourceTextModule> self(SourceTextModule::cast(*this), GetIsolate());
self->set_code(self->info()); self->set_code(self->info());
...@@ -64,7 +67,7 @@ void Module::RecordError(Isolate* isolate) { ...@@ -64,7 +67,7 @@ void Module::RecordError(Isolate* isolate) {
PrintStatusTransition(Module::kErrored); PrintStatusTransition(Module::kErrored);
#endif // DEBUG #endif // DEBUG
set_status(Module::kErrored); set_status(Module::kErrored);
set_exception(the_exception); set_exception(*error);
} }
void Module::ResetGraph(Isolate* isolate, Handle<Module> module) { void Module::ResetGraph(Isolate* isolate, Handle<Module> module) {
...@@ -244,47 +247,65 @@ MaybeHandle<Object> Module::Evaluate(Isolate* isolate, Handle<Module> module) { ...@@ -244,47 +247,65 @@ MaybeHandle<Object> Module::Evaluate(Isolate* isolate, Handle<Module> module) {
#endif // OBJECT_PRINT #endif // OBJECT_PRINT
} }
#endif // DEBUG #endif // DEBUG
STACK_CHECK(isolate, MaybeHandle<Object>());
if (FLAG_harmony_top_level_await) {
return Module::InnerEvaluateMaybeAsync(isolate, module);
} else {
return Module::InnerEvaluate(isolate, module);
}
}
MaybeHandle<Object> Module::InnerEvaluate(Isolate* isolate,
Handle<Module> module) {
if (module->status() == kErrored) { if (module->status() == kErrored) {
isolate->Throw(module->GetException()); isolate->Throw(module->GetException());
return MaybeHandle<Object>(); return MaybeHandle<Object>();
} else if (module->status() == kEvaluated) {
return isolate->factory()->undefined_value();
} }
DCHECK_NE(module->status(), kEvaluating); CHECK_NE(module->status(), kEvaluating);
DCHECK_GE(module->status(), kInstantiated); CHECK_GE(module->status(), kInstantiated);
Zone zone(isolate->allocator(), ZONE_NAME);
ZoneForwardList<Handle<SourceTextModule>> stack(&zone); if (module->IsSourceTextModule()) {
unsigned dfs_index = 0; return SourceTextModule::Evaluate(isolate,
Handle<Object> result; Handle<SourceTextModule>::cast(module));
if (!Evaluate(isolate, module, &stack, &dfs_index).ToHandle(&result)) { } else {
for (auto& descendant : stack) { return SyntheticModule::Evaluate(isolate,
DCHECK_EQ(descendant->status(), kEvaluating); Handle<SyntheticModule>::cast(module));
descendant->RecordError(isolate);
}
DCHECK_EQ(module->GetException(), isolate->pending_exception());
return MaybeHandle<Object>();
} }
DCHECK_EQ(module->status(), kEvaluated);
DCHECK(stack.empty());
return result;
} }
MaybeHandle<Object> Module::Evaluate( MaybeHandle<Object> Module::InnerEvaluateMaybeAsync(Isolate* isolate,
Isolate* isolate, Handle<Module> module, Handle<Module> module) {
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index) { // In the event of errored evaluation, return a rejected promise.
if (module->status() == kErrored) { if (module->status() == kErrored) {
isolate->Throw(module->GetException()); // If we have an already evaluated SourceTextModule with a top level
return MaybeHandle<Object>(); // capability we assume it has already been rejected, and return it
} // here. Otherwise create a new promise and reject it with the module's
if (module->status() >= kEvaluating) { // exception.
return isolate->factory()->undefined_value(); if (module->IsSourceTextModule()) {
Handle<SourceTextModule> source_text_module(
Handle<SourceTextModule>::cast(module));
if (source_text_module->top_level_capability().IsJSPromise()) {
Handle<JSPromise> top_level_capability(
JSPromise::cast(source_text_module->top_level_capability()),
isolate);
DCHECK(top_level_capability->status() == Promise::kRejected &&
top_level_capability->result() == module->exception());
return top_level_capability;
}
}
Handle<JSPromise> capability = isolate->factory()->NewJSPromise();
JSPromise::Reject(capability, handle(module->exception(), isolate));
return capability;
} }
DCHECK_EQ(module->status(), kInstantiated);
STACK_CHECK(isolate, MaybeHandle<Object>());
if (module->IsSourceTextModule()) { if (module->IsSourceTextModule()) {
return SourceTextModule::Evaluate( return SourceTextModule::EvaluateMaybeAsync(
isolate, Handle<SourceTextModule>::cast(module), stack, dfs_index); isolate, Handle<SourceTextModule>::cast(module));
} else { } else {
DCHECK_NE(module->status(), kEvaluating);
DCHECK_GE(module->status(), kInstantiated);
return SyntheticModule::Evaluate(isolate, return SyntheticModule::Evaluate(isolate,
Handle<SyntheticModule>::cast(module)); Handle<SyntheticModule>::cast(module));
} }
......
...@@ -112,18 +112,21 @@ class Module : public HeapObject { ...@@ -112,18 +112,21 @@ class Module : public HeapObject {
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index, ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index,
Zone* zone); Zone* zone);
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> Evaluate( static V8_WARN_UNUSED_RESULT MaybeHandle<Object> InnerEvaluate(
Isolate* isolate, Handle<Module> module, Isolate* isolate, Handle<Module> module);
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index); static V8_WARN_UNUSED_RESULT MaybeHandle<Object> InnerEvaluateMaybeAsync(
Isolate* isolate, Handle<Module> module);
// Set module's status back to kUninstantiated and reset other internal state. // Set module's status back to kUninstantiated and reset other internal state.
// This is used when instantiation fails. // This is used when instantiation fails.
static void Reset(Isolate* isolate, Handle<Module> module); static void Reset(Isolate* isolate, Handle<Module> module);
static void ResetGraph(Isolate* isolate, Handle<Module> module); static void ResetGraph(Isolate* isolate, Handle<Module> module);
// To set status to kErrored, RecordError should be used. // To set status to kErrored, RecordError or RecordErrorUsingPendingException
// should be used.
void SetStatus(Status status); void SetStatus(Status status);
void RecordError(Isolate* isolate); void RecordErrorUsingPendingException(Isolate* isolate);
void RecordError(Isolate* isolate, Handle<Object> error);
#ifdef DEBUG #ifdef DEBUG
// For --trace-module-status. // For --trace-module-status.
......
This diff is collapsed.
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define V8_OBJECTS_SOURCE_TEXT_MODULE_H_ #define V8_OBJECTS_SOURCE_TEXT_MODULE_H_
#include "src/objects/module.h" #include "src/objects/module.h"
#include "src/objects/promise.h"
// Has to be the last include (doesn't have include guards): // Has to be the last include (doesn't have include guards):
#include "src/objects/object-macros.h" #include "src/objects/object-macros.h"
...@@ -28,6 +29,10 @@ class SourceTextModule ...@@ -28,6 +29,10 @@ class SourceTextModule
// kErrored. // kErrored.
SharedFunctionInfo GetSharedFunctionInfo() const; SharedFunctionInfo GetSharedFunctionInfo() const;
// Whether or not this module is an async module. Set during module creation
// and does not change afterwards.
DECL_BOOLEAN_ACCESSORS(async)
// Get the SourceTextModuleInfo associated with the code. // Get the SourceTextModuleInfo associated with the code.
inline SourceTextModuleInfo info() const; inline SourceTextModuleInfo info() const;
...@@ -41,6 +46,14 @@ class SourceTextModule ...@@ -41,6 +46,14 @@ class SourceTextModule
static int ImportIndex(int cell_index); static int ImportIndex(int cell_index);
static int ExportIndex(int cell_index); static int ExportIndex(int cell_index);
// Used by builtins to fulfill or reject the promise associated
// with async SourceTextModules.
static void AsyncModuleExecutionFulfilled(Isolate* isolate,
Handle<SourceTextModule> module);
static void AsyncModuleExecutionRejected(Isolate* isolate,
Handle<SourceTextModule> module,
Handle<Object> exception);
// Get the namespace object for [module_request] of [module]. If it doesn't // Get the namespace object for [module_request] of [module]. If it doesn't
// exist yet, it is created. // exist yet, it is created.
static Handle<JSModuleNamespace> GetModuleNamespace( static Handle<JSModuleNamespace> GetModuleNamespace(
...@@ -54,12 +67,54 @@ class SourceTextModule ...@@ -54,12 +67,54 @@ class SourceTextModule
friend class Factory; friend class Factory;
friend class Module; friend class Module;
// Appends a tuple of module and generator to the async parent modules
// ArrayList.
inline void AddAsyncParentModule(Isolate* isolate,
Handle<SourceTextModule> module);
// Returns a SourceTextModule, the
// ith parent in depth first traversal order of a given async child.
inline Handle<SourceTextModule> GetAsyncParentModule(Isolate* isolate,
int index);
// Returns the number of async parent modules for a given async child.
inline int AsyncParentModuleCount();
inline bool HasPendingAsyncDependencies();
inline void IncrementPendingAsyncDependencies();
inline void DecrementPendingAsyncDependencies();
// TODO(neis): Don't store those in the module object? // TODO(neis): Don't store those in the module object?
DECL_INT_ACCESSORS(dfs_index) DECL_INT_ACCESSORS(dfs_index)
DECL_INT_ACCESSORS(dfs_ancestor_index) DECL_INT_ACCESSORS(dfs_ancestor_index)
// Helpers for Instantiate and Evaluate. // Storage for boolean flags.
DECL_INT_ACCESSORS(flags)
// Bits for flags.
static const int kAsyncBit = 0;
static const int kAsyncEvaluatingBit = 1;
// async_evaluating, 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)
// The top level promise capability of this module. Will only be defined
// for cycle roots.
DECL_ACCESSORS(top_level_capability, HeapObject)
// The number of currently evaluating async dependencies of this module.
DECL_INT_ACCESSORS(pending_async_dependencies)
// The parent modules of a given async dependency, use async_parent_modules()
// to retrieve the ArrayList representation.
DECL_ACCESSORS(async_parent_modules, ArrayList)
// Helpers for Instantiate and Evaluate.
static void CreateExport(Isolate* isolate, Handle<SourceTextModule> module, static void CreateExport(Isolate* isolate, Handle<SourceTextModule> module,
int cell_index, Handle<FixedArray> names); int cell_index, Handle<FixedArray> names);
static void CreateIndirectExport(Isolate* isolate, static void CreateIndirectExport(Isolate* isolate,
...@@ -95,7 +150,16 @@ class SourceTextModule ...@@ -95,7 +150,16 @@ class SourceTextModule
Handle<SourceTextModule> module, Zone* zone, Handle<SourceTextModule> module, Zone* zone,
UnorderedModuleSet* visited); UnorderedModuleSet* visited);
// Implementation of spec concrete method Evaluate.
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> EvaluateMaybeAsync(
Isolate* isolate, Handle<SourceTextModule> module);
// Continued implementation of spec concrete method Evaluate.
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> Evaluate( static V8_WARN_UNUSED_RESULT MaybeHandle<Object> Evaluate(
Isolate* isolate, Handle<SourceTextModule> module);
// Implementation of spec abstract operation InnerModuleEvaluation.
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> InnerModuleEvaluation(
Isolate* isolate, Handle<SourceTextModule> module, Isolate* isolate, Handle<SourceTextModule> module,
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index); ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index);
...@@ -103,6 +167,24 @@ class SourceTextModule ...@@ -103,6 +167,24 @@ class SourceTextModule
Isolate* isolate, Handle<SourceTextModule> module, Isolate* isolate, Handle<SourceTextModule> module,
ZoneForwardList<Handle<SourceTextModule>>* stack, Status new_status); ZoneForwardList<Handle<SourceTextModule>>* stack, Status new_status);
// Implementation of spec GetAsyncCycleRoot.
static V8_WARN_UNUSED_RESULT Handle<SourceTextModule> GetAsyncCycleRoot(
Isolate* isolate, Handle<SourceTextModule> module);
// Implementation of spec ExecuteModule is broken up into
// InnerExecuteAsyncModule for asynchronous modules and ExecuteModule
// for synchronous modules.
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> InnerExecuteAsyncModule(
Isolate* isolate, Handle<SourceTextModule> module,
Handle<JSPromise> capability);
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> ExecuteModule(
Isolate* isolate, Handle<SourceTextModule> module);
// Implementation of spec ExecuteAsyncModule.
static void ExecuteAsyncModule(Isolate* isolate,
Handle<SourceTextModule> module);
static void Reset(Isolate* isolate, Handle<SourceTextModule> module); static void Reset(Isolate* isolate, Handle<SourceTextModule> module);
TQ_OBJECT_CONSTRUCTORS(SourceTextModule) TQ_OBJECT_CONSTRUCTORS(SourceTextModule)
......
...@@ -96,7 +96,7 @@ MaybeHandle<Object> SyntheticModule::Evaluate(Isolate* isolate, ...@@ -96,7 +96,7 @@ MaybeHandle<Object> SyntheticModule::Evaluate(Isolate* isolate,
Utils::ToLocal(Handle<Module>::cast(module))) Utils::ToLocal(Handle<Module>::cast(module)))
.ToLocal(&result)) { .ToLocal(&result)) {
isolate->PromoteScheduledException(); isolate->PromoteScheduledException();
module->RecordError(isolate); module->RecordErrorUsingPendingException(isolate);
return MaybeHandle<Object>(); return MaybeHandle<Object>();
} }
......
...@@ -23765,7 +23765,13 @@ TEST(ModuleCodeCache) { ...@@ -23765,7 +23765,13 @@ TEST(ModuleCodeCache) {
// Evaluate for possible lazy compilation. // Evaluate for possible lazy compilation.
Local<Value> completion_value = Local<Value> completion_value =
module->Evaluate(context).ToLocalChecked(); module->Evaluate(context).ToLocalChecked();
CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); if (i::FLAG_harmony_top_level_await) {
Local<v8::Promise> promise(Local<v8::Promise>::Cast(completion_value));
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
CHECK(promise->Result()->IsUndefined());
} else {
CHECK_EQ(42, completion_value->Int32Value(context).FromJust());
}
// Now create the cache. Note that it is freed, obscurely, when // Now create the cache. Note that it is freed, obscurely, when
// ScriptCompiler::Source goes out of scope below. // ScriptCompiler::Source goes out of scope below.
...@@ -23796,7 +23802,13 @@ TEST(ModuleCodeCache) { ...@@ -23796,7 +23802,13 @@ TEST(ModuleCodeCache) {
Local<Value> completion_value = Local<Value> completion_value =
module->Evaluate(context).ToLocalChecked(); module->Evaluate(context).ToLocalChecked();
CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); if (i::FLAG_harmony_top_level_await) {
Local<v8::Promise> promise(Local<v8::Promise>::Cast(completion_value));
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
CHECK(promise->Result()->IsUndefined());
} else {
CHECK_EQ(42, completion_value->Int32Value(context).FromJust());
}
} }
isolate->Dispose(); isolate->Dispose();
} }
...@@ -23975,7 +23987,13 @@ TEST(ImportFromSyntheticModule) { ...@@ -23975,7 +23987,13 @@ TEST(ImportFromSyntheticModule) {
.ToChecked(); .ToChecked();
Local<Value> completion_value = module->Evaluate(context).ToLocalChecked(); Local<Value> completion_value = module->Evaluate(context).ToLocalChecked();
CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); if (i::FLAG_harmony_top_level_await) {
Local<v8::Promise> promise(Local<v8::Promise>::Cast(completion_value));
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
CHECK(promise->Result()->IsUndefined());
} else {
CHECK_EQ(42, completion_value->Int32Value(context).FromJust());
}
} }
TEST(ImportFromSyntheticModuleThrow) { TEST(ImportFromSyntheticModuleThrow) {
...@@ -24005,7 +24023,15 @@ TEST(ImportFromSyntheticModuleThrow) { ...@@ -24005,7 +24023,15 @@ TEST(ImportFromSyntheticModuleThrow) {
CHECK_EQ(module->GetStatus(), Module::kInstantiated); CHECK_EQ(module->GetStatus(), Module::kInstantiated);
TryCatch try_catch(isolate); TryCatch try_catch(isolate);
v8::MaybeLocal<Value> completion_value = module->Evaluate(context); v8::MaybeLocal<Value> completion_value = module->Evaluate(context);
CHECK(completion_value.IsEmpty()); if (i::FLAG_harmony_top_level_await) {
Local<v8::Promise> promise(
Local<v8::Promise>::Cast(completion_value.ToLocalChecked()));
CHECK_EQ(promise->State(), v8::Promise::kRejected);
CHECK_EQ(promise->Result(), try_catch.Exception());
} else {
CHECK(completion_value.IsEmpty());
}
CHECK_EQ(module->GetStatus(), Module::kErrored); CHECK_EQ(module->GetStatus(), Module::kErrored);
CHECK(try_catch.HasCaught()); CHECK(try_catch.HasCaught());
} }
...@@ -24038,7 +24064,13 @@ TEST(CodeCacheModuleScriptMismatch) { ...@@ -24038,7 +24064,13 @@ TEST(CodeCacheModuleScriptMismatch) {
// Evaluate for possible lazy compilation. // Evaluate for possible lazy compilation.
Local<Value> completion_value = Local<Value> completion_value =
module->Evaluate(context).ToLocalChecked(); module->Evaluate(context).ToLocalChecked();
CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); if (i::FLAG_harmony_top_level_await) {
Local<v8::Promise> promise(Local<v8::Promise>::Cast(completion_value));
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
CHECK(promise->Result()->IsUndefined());
} else {
CHECK_EQ(42, completion_value->Int32Value(context).FromJust());
}
// Now create the cache. Note that it is freed, obscurely, when // Now create the cache. Note that it is freed, obscurely, when
// ScriptCompiler::Source goes out of scope below. // ScriptCompiler::Source goes out of scope below.
...@@ -24134,7 +24166,13 @@ TEST(CodeCacheScriptModuleMismatch) { ...@@ -24134,7 +24166,13 @@ TEST(CodeCacheScriptModuleMismatch) {
Local<Value> completion_value = Local<Value> completion_value =
module->Evaluate(context).ToLocalChecked(); module->Evaluate(context).ToLocalChecked();
CHECK_EQ(42, completion_value->Int32Value(context).FromJust()); if (i::FLAG_harmony_top_level_await) {
Local<v8::Promise> promise(Local<v8::Promise>::Cast(completion_value));
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
CHECK(promise->Result()->IsUndefined());
} else {
CHECK_EQ(42, completion_value->Int32Value(context).FromJust());
}
} }
isolate->Dispose(); isolate->Dispose();
} }
...@@ -24170,10 +24208,14 @@ TEST(InvalidCodeCacheDataInCompileModule) { ...@@ -24170,10 +24208,14 @@ TEST(InvalidCodeCacheDataInCompileModule) {
.ToChecked(); .ToChecked();
CHECK(cached_data->rejected); CHECK(cached_data->rejected);
CHECK_EQ(42, module->Evaluate(context) Local<Value> completion_value = module->Evaluate(context).ToLocalChecked();
.ToLocalChecked() if (i::FLAG_harmony_top_level_await) {
->Int32Value(context) Local<v8::Promise> promise(Local<v8::Promise>::Cast(completion_value));
.FromJust()); CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
CHECK(promise->Result()->IsUndefined());
} else {
CHECK_EQ(42, completion_value->Int32Value(context).FromJust());
}
} }
void TestInvalidCacheData(v8::ScriptCompiler::CompileOptions option) { void TestInvalidCacheData(v8::ScriptCompiler::CompileOptions option) {
...@@ -25818,7 +25860,14 @@ TEST(ImportMeta) { ...@@ -25818,7 +25860,14 @@ TEST(ImportMeta) {
module->InstantiateModule(context.local(), UnexpectedModuleResolveCallback) module->InstantiateModule(context.local(), UnexpectedModuleResolveCallback)
.ToChecked(); .ToChecked();
Local<Value> result = module->Evaluate(context.local()).ToLocalChecked(); Local<Value> result = module->Evaluate(context.local()).ToLocalChecked();
CHECK(result->StrictEquals(Local<v8::Value>::Cast(v8::Utils::ToLocal(meta)))); if (i::FLAG_harmony_top_level_await) {
Local<v8::Promise> promise(Local<v8::Promise>::Cast(result));
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
CHECK(promise->Result()->IsUndefined());
} else {
CHECK(
result->StrictEquals(Local<v8::Value>::Cast(v8::Utils::ToLocal(meta))));
}
} }
TEST(GetModuleNamespace) { TEST(GetModuleNamespace) {
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment