Reland "[top-level-await] Implement top-level-await in V8"
The first land did not correctly handle exceptions for already evaluated modules. Original description: 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: I24725816ee4a6c3616c3c8b08a75a60ca9f27727 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1797658 Commit-Queue: Joshua Litt <joshualitt@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#63803}
This commit is contained in:
parent
b53ff5f4e3
commit
798cb9045c
1
BUILD.gn
1
BUILD.gn
@ -2050,6 +2050,7 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/builtins/builtins-api.cc",
|
||||
"src/builtins/builtins-array.cc",
|
||||
"src/builtins/builtins-arraybuffer.cc",
|
||||
"src/builtins/builtins-async-module.cc",
|
||||
"src/builtins/builtins-bigint.cc",
|
||||
"src/builtins/builtins-call.cc",
|
||||
"src/builtins/builtins-callsite.cc",
|
||||
|
@ -232,6 +232,10 @@ extern class WeakFixedArray extends HeapObject { length: Smi; }
|
||||
|
||||
extern class ByteArray extends FixedArrayBase {}
|
||||
|
||||
@hasSameInstanceTypeAsParent
|
||||
extern class ArrayList extends FixedArray {
|
||||
}
|
||||
|
||||
type LayoutDescriptor extends ByteArray
|
||||
generates 'TNode<LayoutDescriptor>';
|
||||
type TransitionArray extends WeakFixedArray
|
||||
@ -569,9 +573,12 @@ extern class SourceTextModule extends Module {
|
||||
// Lazily initialized on first access. It's the hole before first access and
|
||||
// a JSObject afterwards.
|
||||
import_meta: TheHole | JSObject;
|
||||
|
||||
async_parent_modules: ArrayList;
|
||||
top_level_capability: JSPromise | Undefined;
|
||||
dfs_index: Smi;
|
||||
dfs_ancestor_index: Smi;
|
||||
pending_async_dependencies: Smi;
|
||||
flags: Smi;
|
||||
}
|
||||
|
||||
@generateCppClass
|
||||
|
33
src/builtins/builtins-async-module.cc
Normal file
33
src/builtins/builtins-async-module.cc
Normal file
@ -0,0 +1,33 @@
|
||||
// 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
|
@ -1127,7 +1127,14 @@ namespace internal {
|
||||
CPP(FinalizationGroupRegister) \
|
||||
CPP(FinalizationGroupUnregister) \
|
||||
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
|
||||
#define BUILTIN_LIST_INTL(CPP, TFJ, TFS) \
|
||||
|
@ -19,19 +19,25 @@ class GeneratorBuiltinsAssembler : public CodeStubAssembler {
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
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,
|
||||
Node* value, Node* context,
|
||||
JSGeneratorObject::ResumeMode resume_mode,
|
||||
char const* const method_name);
|
||||
};
|
||||
|
||||
void GeneratorBuiltinsAssembler::GeneratorPrototypeResume(
|
||||
void GeneratorBuiltinsAssembler::InnerResume(
|
||||
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);
|
||||
|
||||
// Check if the {receiver} is running or already closed.
|
||||
TNode<Smi> receiver_continuation =
|
||||
CAST(LoadObjectField(receiver, JSGeneratorObject::kContinuationOffset));
|
||||
@ -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;
|
||||
|
||||
TNode<Int32T> argc =
|
||||
UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
|
||||
CodeStubArguments args(this, argc);
|
||||
|
||||
TNode<Object> receiver = args.GetReceiver();
|
||||
TNode<Object> value = args.GetOptionalArgumentValue(kValueArg);
|
||||
TNode<Context> context = Cast(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
|
||||
TF_BUILTIN(GeneratorPrototypeNext, GeneratorBuiltinsAssembler) {
|
||||
const int kValueArg = 0;
|
||||
|
@ -1543,10 +1543,18 @@ void Module::ModuleVerify(Isolate* isolate) {
|
||||
void SourceTextModule::SourceTextModuleVerify(Isolate* isolate) {
|
||||
TorqueGeneratedClassVerifiers::SourceTextModuleVerify(*this, isolate);
|
||||
|
||||
CHECK((status() >= kEvaluating && code().IsSourceTextModuleInfo()) ||
|
||||
(status() == kInstantiated && code().IsJSGeneratorObject()) ||
|
||||
(status() == kInstantiating && code().IsJSFunction()) ||
|
||||
(code().IsSharedFunctionInfo()));
|
||||
if (status() == kErrored) {
|
||||
CHECK(code().IsSourceTextModuleInfo());
|
||||
} else if (status() == kEvaluating || status() == kEvaluated) {
|
||||
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());
|
||||
}
|
||||
|
@ -208,7 +208,8 @@ DEFINE_IMPLICATION(harmony_import_meta, harmony_dynamic_import)
|
||||
V(harmony_regexp_sequence, "RegExp Unicode sequence properties") \
|
||||
V(harmony_weak_refs, "harmony weak references") \
|
||||
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
|
||||
#define HARMONY_INPROGRESS(V) \
|
||||
|
@ -3041,6 +3041,7 @@ Handle<SourceTextModule> Factory::NewSourceTextModule(
|
||||
Handle<FixedArray> requested_modules =
|
||||
requested_modules_length > 0 ? NewFixedArray(requested_modules_length)
|
||||
: empty_fixed_array();
|
||||
Handle<ArrayList> async_parent_modules = ArrayList::New(isolate(), 0);
|
||||
|
||||
ReadOnlyRoots roots(isolate());
|
||||
Handle<SourceTextModule> module(
|
||||
@ -3060,6 +3061,12 @@ Handle<SourceTextModule> Factory::NewSourceTextModule(
|
||||
module->set_import_meta(roots.the_hole_value());
|
||||
module->set_dfs_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;
|
||||
}
|
||||
|
||||
|
@ -870,6 +870,29 @@ void Genesis::CreateIteratorMaps(Handle<JSFunction> empty) {
|
||||
generator_next_internal->shared().set_native(false);
|
||||
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
|
||||
// maps in the native context. The "prototype" property descriptor is
|
||||
// writable, non-enumerable, and non-configurable (as per ES6 draft
|
||||
@ -4283,6 +4306,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_import_meta)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_regexp_sequence)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_optional_chaining)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_nullish)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_top_level_await)
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_intl_add_calendar_numbering_system)
|
||||
|
@ -37,21 +37,23 @@ enum ContextLookupFlags {
|
||||
// must always be allocated via Heap::AllocateContext() or
|
||||
// Factory::NewContext.
|
||||
|
||||
#define NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) \
|
||||
V(GENERATOR_NEXT_INTERNAL, JSFunction, generator_next_internal) \
|
||||
V(MAKE_ERROR_INDEX, JSFunction, make_error) \
|
||||
V(MAKE_RANGE_ERROR_INDEX, JSFunction, make_range_error) \
|
||||
V(MAKE_SYNTAX_ERROR_INDEX, JSFunction, make_syntax_error) \
|
||||
V(MAKE_TYPE_ERROR_INDEX, JSFunction, make_type_error) \
|
||||
V(MAKE_URI_ERROR_INDEX, JSFunction, make_uri_error) \
|
||||
V(OBJECT_CREATE, JSFunction, object_create) \
|
||||
V(REFLECT_APPLY_INDEX, JSFunction, reflect_apply) \
|
||||
V(REFLECT_CONSTRUCT_INDEX, JSFunction, reflect_construct) \
|
||||
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
|
||||
V(MATH_POW_INDEX, JSFunction, math_pow) \
|
||||
V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \
|
||||
promise_internal_constructor) \
|
||||
V(IS_PROMISE_INDEX, JSFunction, is_promise) \
|
||||
#define NATIVE_CONTEXT_INTRINSIC_FUNCTIONS(V) \
|
||||
V(GENERATOR_NEXT_INTERNAL, JSFunction, generator_next_internal) \
|
||||
V(ASYNC_MODULE_EVALUATE_INTERNAL, JSFunction, \
|
||||
async_module_evaluate_internal) \
|
||||
V(MAKE_ERROR_INDEX, JSFunction, make_error) \
|
||||
V(MAKE_RANGE_ERROR_INDEX, JSFunction, make_range_error) \
|
||||
V(MAKE_SYNTAX_ERROR_INDEX, JSFunction, make_syntax_error) \
|
||||
V(MAKE_TYPE_ERROR_INDEX, JSFunction, make_type_error) \
|
||||
V(MAKE_URI_ERROR_INDEX, JSFunction, make_uri_error) \
|
||||
V(OBJECT_CREATE, JSFunction, object_create) \
|
||||
V(REFLECT_APPLY_INDEX, JSFunction, reflect_apply) \
|
||||
V(REFLECT_CONSTRUCT_INDEX, JSFunction, reflect_construct) \
|
||||
V(MATH_FLOOR_INDEX, JSFunction, math_floor) \
|
||||
V(MATH_POW_INDEX, JSFunction, math_pow) \
|
||||
V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \
|
||||
promise_internal_constructor) \
|
||||
V(IS_PROMISE_INDEX, JSFunction, is_promise) \
|
||||
V(PROMISE_THEN_INDEX, JSFunction, promise_then)
|
||||
|
||||
#define NATIVE_CONTEXT_FIELDS(V) \
|
||||
@ -104,6 +106,8 @@ enum ContextLookupFlags {
|
||||
V(CALL_AS_CONSTRUCTOR_DELEGATE_INDEX, JSFunction, \
|
||||
call_as_constructor_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(CONTEXT_EXTENSION_FUNCTION_INDEX, JSFunction, context_extension_function) \
|
||||
V(DATA_PROPERTY_DESCRIPTOR_MAP_INDEX, Map, data_property_descriptor_map) \
|
||||
|
@ -38,9 +38,17 @@ SMI_ACCESSORS(Module, hash, kHashOffset)
|
||||
|
||||
TQ_SMI_ACCESSORS(SourceTextModule, dfs_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 {
|
||||
return (status() >= kEvaluating)
|
||||
return status() == kErrored
|
||||
? SourceTextModuleInfo::cast(code())
|
||||
: GetSharedFunctionInfo().scope_info().ModuleDescriptorInfo();
|
||||
}
|
||||
@ -112,6 +120,37 @@ class UnorderedModuleSet
|
||||
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 v8
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/ast/modules.h"
|
||||
#include "src/builtins/accessors.h"
|
||||
#include "src/heap/heap-inl.h"
|
||||
#include "src/objects/cell-inl.h"
|
||||
#include "src/objects/hash-table-inl.h"
|
||||
#include "src/objects/js-generator-inl.h"
|
||||
@ -50,12 +51,14 @@ void Module::SetStatus(Status new_status) {
|
||||
set_status(new_status);
|
||||
}
|
||||
|
||||
void Module::RecordError(Isolate* isolate) {
|
||||
DisallowHeapAllocation no_alloc;
|
||||
DCHECK(exception().IsTheHole(isolate));
|
||||
Object the_exception = isolate->pending_exception();
|
||||
DCHECK(!the_exception.IsTheHole(isolate));
|
||||
void Module::RecordErrorUsingPendingException(Isolate* isolate) {
|
||||
Handle<Object> the_exception(isolate->pending_exception(), isolate);
|
||||
RecordError(isolate, the_exception);
|
||||
}
|
||||
|
||||
void Module::RecordError(Isolate* isolate, Handle<Object> error) {
|
||||
DCHECK(exception().IsTheHole(isolate));
|
||||
DCHECK(!error->IsTheHole(isolate));
|
||||
if (this->IsSourceTextModule()) {
|
||||
Handle<SourceTextModule> self(SourceTextModule::cast(*this), GetIsolate());
|
||||
self->set_code(self->info());
|
||||
@ -64,7 +67,7 @@ void Module::RecordError(Isolate* isolate) {
|
||||
PrintStatusTransition(Module::kErrored);
|
||||
#endif // DEBUG
|
||||
set_status(Module::kErrored);
|
||||
set_exception(the_exception);
|
||||
set_exception(*error);
|
||||
}
|
||||
|
||||
void Module::ResetGraph(Isolate* isolate, Handle<Module> module) {
|
||||
@ -244,46 +247,35 @@ MaybeHandle<Object> Module::Evaluate(Isolate* isolate, Handle<Module> module) {
|
||||
#endif // OBJECT_PRINT
|
||||
}
|
||||
#endif // DEBUG
|
||||
if (module->status() == kErrored) {
|
||||
isolate->Throw(module->GetException());
|
||||
return MaybeHandle<Object>();
|
||||
STACK_CHECK(isolate, MaybeHandle<Object>());
|
||||
if (FLAG_harmony_top_level_await && module->IsSourceTextModule()) {
|
||||
return SourceTextModule::EvaluateMaybeAsync(
|
||||
isolate, Handle<SourceTextModule>::cast(module));
|
||||
} else {
|
||||
return Module::InnerEvaluate(isolate, module);
|
||||
}
|
||||
DCHECK_NE(module->status(), kEvaluating);
|
||||
DCHECK_GE(module->status(), kInstantiated);
|
||||
Zone zone(isolate->allocator(), ZONE_NAME);
|
||||
|
||||
ZoneForwardList<Handle<SourceTextModule>> stack(&zone);
|
||||
unsigned dfs_index = 0;
|
||||
Handle<Object> result;
|
||||
if (!Evaluate(isolate, module, &stack, &dfs_index).ToHandle(&result)) {
|
||||
for (auto& descendant : stack) {
|
||||
DCHECK_EQ(descendant->status(), kEvaluating);
|
||||
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(
|
||||
Isolate* isolate, Handle<Module> module,
|
||||
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index) {
|
||||
MaybeHandle<Object> Module::InnerEvaluate(Isolate* isolate,
|
||||
Handle<Module> module) {
|
||||
if (module->status() == kErrored) {
|
||||
isolate->Throw(module->GetException());
|
||||
return MaybeHandle<Object>();
|
||||
}
|
||||
if (module->status() >= kEvaluating) {
|
||||
} else if (module->status() == kEvaluated) {
|
||||
return isolate->factory()->undefined_value();
|
||||
}
|
||||
DCHECK_EQ(module->status(), kInstantiated);
|
||||
STACK_CHECK(isolate, MaybeHandle<Object>());
|
||||
|
||||
// InnerEvaluate can be called both to evaluate top level modules without
|
||||
// the harmony_top_level_await flag and recursively to evaluate
|
||||
// SyntheticModules in the dependency graphs of SourceTextModules.
|
||||
//
|
||||
// However, SyntheticModules transition directly to 'Evaluated,' so we should
|
||||
// never see an 'Evaluating' module at this point.
|
||||
CHECK_EQ(module->status(), kInstantiated);
|
||||
|
||||
if (module->IsSourceTextModule()) {
|
||||
return SourceTextModule::Evaluate(
|
||||
isolate, Handle<SourceTextModule>::cast(module), stack, dfs_index);
|
||||
return SourceTextModule::Evaluate(isolate,
|
||||
Handle<SourceTextModule>::cast(module));
|
||||
} else {
|
||||
return SyntheticModule::Evaluate(isolate,
|
||||
Handle<SyntheticModule>::cast(module));
|
||||
|
@ -112,18 +112,19 @@ class Module : public HeapObject {
|
||||
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index,
|
||||
Zone* zone);
|
||||
|
||||
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> Evaluate(
|
||||
Isolate* isolate, Handle<Module> module,
|
||||
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index);
|
||||
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> InnerEvaluate(
|
||||
Isolate* isolate, Handle<Module> module);
|
||||
|
||||
// Set module's status back to kUninstantiated and reset other internal state.
|
||||
// This is used when instantiation fails.
|
||||
static void Reset(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 RecordError(Isolate* isolate);
|
||||
void RecordErrorUsingPendingException(Isolate* isolate);
|
||||
void RecordError(Isolate* isolate, Handle<Object> error);
|
||||
|
||||
#ifdef DEBUG
|
||||
// For --trace-module-status.
|
||||
|
@ -78,8 +78,6 @@ class Module::ResolveSet
|
||||
|
||||
SharedFunctionInfo SourceTextModule::GetSharedFunctionInfo() const {
|
||||
DisallowHeapAllocation no_alloc;
|
||||
DCHECK_NE(status(), Module::kEvaluating);
|
||||
DCHECK_NE(status(), Module::kEvaluated);
|
||||
switch (status()) {
|
||||
case kUninstantiated:
|
||||
case kPreInstantiating:
|
||||
@ -89,10 +87,10 @@ SharedFunctionInfo SourceTextModule::GetSharedFunctionInfo() const {
|
||||
DCHECK(code().IsJSFunction());
|
||||
return JSFunction::cast(code()).shared();
|
||||
case kInstantiated:
|
||||
DCHECK(code().IsJSGeneratorObject());
|
||||
return JSGeneratorObject::cast(code()).function().shared();
|
||||
case kEvaluating:
|
||||
case kEvaluated:
|
||||
DCHECK(code().IsJSGeneratorObject());
|
||||
return JSGeneratorObject::cast(code()).function().shared();
|
||||
case kErrored:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -580,48 +578,325 @@ Handle<JSModuleNamespace> SourceTextModule::GetModuleNamespace(
|
||||
return Module::GetModuleNamespace(isolate, requested_module);
|
||||
}
|
||||
|
||||
MaybeHandle<Object> SourceTextModule::Evaluate(
|
||||
Isolate* isolate, Handle<SourceTextModule> module,
|
||||
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index) {
|
||||
Handle<JSGeneratorObject> generator(JSGeneratorObject::cast(module->code()),
|
||||
isolate);
|
||||
module->set_code(
|
||||
generator->function().shared().scope_info().ModuleDescriptorInfo());
|
||||
module->SetStatus(kEvaluating);
|
||||
module->set_dfs_index(*dfs_index);
|
||||
module->set_dfs_ancestor_index(*dfs_index);
|
||||
stack->push_front(module);
|
||||
(*dfs_index)++;
|
||||
MaybeHandle<Object> SourceTextModule::EvaluateMaybeAsync(
|
||||
Isolate* isolate, Handle<SourceTextModule> module) {
|
||||
// In the event of errored evaluation, return a rejected promise.
|
||||
if (module->status() == kErrored) {
|
||||
// If we have a top level capability we assume it has already been
|
||||
// rejected, and return it here. Otherwise create a new promise and
|
||||
// reject it with the module's exception.
|
||||
if (module->top_level_capability().IsJSPromise()) {
|
||||
Handle<JSPromise> top_level_capability(
|
||||
JSPromise::cast(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;
|
||||
}
|
||||
|
||||
// Recursion.
|
||||
Handle<FixedArray> requested_modules(module->requested_modules(), isolate);
|
||||
for (int i = 0, length = requested_modules->length(); i < length; ++i) {
|
||||
Handle<Module> requested_module(Module::cast(requested_modules->get(i)),
|
||||
isolate);
|
||||
RETURN_ON_EXCEPTION(
|
||||
isolate, Module::Evaluate(isolate, requested_module, stack, dfs_index),
|
||||
Object);
|
||||
// Start of Evaluate () Concrete Method
|
||||
// 2. Assert: module.[[Status]] is "linked" or "evaluated".
|
||||
CHECK(module->status() == kInstantiated || module->status() == kEvaluated);
|
||||
|
||||
DCHECK_GE(requested_module->status(), kEvaluating);
|
||||
DCHECK_NE(requested_module->status(), kErrored);
|
||||
SLOW_DCHECK(
|
||||
// {requested_module} is evaluating iff it's on the {stack}.
|
||||
(requested_module->status() == kEvaluating) ==
|
||||
std::count_if(stack->begin(), stack->end(), [&](Handle<Module> m) {
|
||||
return *m == *requested_module;
|
||||
}));
|
||||
// 3. If module.[[Status]] is "evaluated", set module to
|
||||
// GetAsyncCycleRoot(module).
|
||||
if (module->status() == kEvaluated) {
|
||||
module = GetAsyncCycleRoot(isolate, module);
|
||||
}
|
||||
|
||||
if (requested_module->status() == kEvaluating) {
|
||||
// SyntheticModules go straight to kEvaluated so this must be a
|
||||
// SourceTextModule
|
||||
module->set_dfs_ancestor_index(
|
||||
std::min(module->dfs_ancestor_index(),
|
||||
Handle<SourceTextModule>::cast(requested_module)
|
||||
->dfs_ancestor_index()));
|
||||
// 4. If module.[[TopLevelCapability]] is not undefined, then
|
||||
// a. Return module.[[TopLevelCapability]].[[Promise]].
|
||||
if (module->top_level_capability().IsJSPromise()) {
|
||||
return handle(JSPromise::cast(module->top_level_capability()), isolate);
|
||||
}
|
||||
DCHECK(module->top_level_capability().IsUndefined());
|
||||
|
||||
// 6. Let capability be ! NewPromiseCapability(%Promise%).
|
||||
Handle<JSPromise> capability = isolate->factory()->NewJSPromise();
|
||||
|
||||
// 7. Set module.[[TopLevelCapability]] to capability.
|
||||
module->set_top_level_capability(*capability);
|
||||
DCHECK(module->top_level_capability().IsJSPromise());
|
||||
|
||||
// 9. If result is an abrupt completion, then
|
||||
Handle<Object> unused_result;
|
||||
if (!Evaluate(isolate, module).ToHandle(&unused_result)) {
|
||||
// d. Perform ! Call(capability.[[Reject]], undefined,
|
||||
// «result.[[Value]]»).
|
||||
isolate->clear_pending_exception();
|
||||
JSPromise::Reject(capability, handle(module->exception(), isolate));
|
||||
} else {
|
||||
// 10. Otherwise,
|
||||
// a. Assert: module.[[Status]] is "evaluated"...
|
||||
CHECK_EQ(module->status(), kEvaluated);
|
||||
|
||||
// b. If module.[[AsyncEvaluating]] is false, then
|
||||
if (!module->async_evaluating()) {
|
||||
// i. Perform ! Call(capability.[[Resolve]], undefined,
|
||||
// «undefined»).
|
||||
JSPromise::Resolve(capability, isolate->factory()->undefined_value())
|
||||
.ToHandleChecked();
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluation of module body.
|
||||
// 11. Return capability.[[Promise]].
|
||||
return capability;
|
||||
}
|
||||
|
||||
MaybeHandle<Object> SourceTextModule::Evaluate(
|
||||
Isolate* isolate, Handle<SourceTextModule> module) {
|
||||
// Evaluate () Concrete Method continued from EvaluateMaybeAsync.
|
||||
CHECK(module->status() == kInstantiated || module->status() == kEvaluated);
|
||||
|
||||
// 5. Let stack be a new empty List.
|
||||
Zone zone(isolate->allocator(), ZONE_NAME);
|
||||
ZoneForwardList<Handle<SourceTextModule>> stack(&zone);
|
||||
unsigned dfs_index = 0;
|
||||
|
||||
// 8. Let result be InnerModuleEvaluation(module, stack, 0).
|
||||
// 9. If result is an abrupt completion, then
|
||||
Handle<Object> result;
|
||||
if (!InnerModuleEvaluation(isolate, module, &stack, &dfs_index)
|
||||
.ToHandle(&result)) {
|
||||
// a. For each Cyclic Module Record m in stack, do
|
||||
for (auto& descendant : stack) {
|
||||
// i. Assert: m.[[Status]] is "evaluating".
|
||||
CHECK_EQ(descendant->status(), kEvaluating);
|
||||
// ii. Set m.[[Status]] to "evaluated".
|
||||
// iii. Set m.[[EvaluationError]] to result.
|
||||
descendant->RecordErrorUsingPendingException(isolate);
|
||||
}
|
||||
DCHECK_EQ(module->exception(), isolate->pending_exception());
|
||||
} else {
|
||||
// 10. Otherwise,
|
||||
// c. Assert: stack is empty.
|
||||
DCHECK(stack.empty());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SourceTextModule::AsyncModuleExecutionFulfilled(
|
||||
Isolate* isolate, Handle<SourceTextModule> module) {
|
||||
// 1. Assert: module.[[Status]] is "evaluated".
|
||||
CHECK(module->status() == kEvaluated || module->status() == kErrored);
|
||||
|
||||
// 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.
|
||||
CHECK_EQ(module->status(), kEvaluated);
|
||||
|
||||
// 4. Set module.[[AsyncEvaluating]] to false.
|
||||
module->set_async_evaluating(false);
|
||||
|
||||
// 5. For each Module m of module.[[AsyncParentModules]], do
|
||||
for (int i = 0; i < module->AsyncParentModuleCount(); i++) {
|
||||
Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i);
|
||||
|
||||
// a. If module.[[DFSIndex]] is not equal to module.[[DFSAncestorIndex]],
|
||||
// then
|
||||
if (module->dfs_index() != module->dfs_ancestor_index()) {
|
||||
// i. Assert: m.[[DFSAncestorIndex]] is equal to
|
||||
// module.[[DFSAncestorIndex]].
|
||||
DCHECK_LE(m->dfs_ancestor_index(), module->dfs_ancestor_index());
|
||||
}
|
||||
// b. Decrement m.[[PendingAsyncDependencies]] by 1.
|
||||
m->DecrementPendingAsyncDependencies();
|
||||
|
||||
// c. 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());
|
||||
|
||||
// ii. Let cycleRoot be ! GetAsyncCycleRoot(m).
|
||||
auto cycle_root = GetAsyncCycleRoot(isolate, m);
|
||||
|
||||
// iii. If cycleRoot.[[EvaluationError]] is not undefined,
|
||||
// return undefined.
|
||||
if (cycle_root->status() == kErrored) {
|
||||
return;
|
||||
}
|
||||
|
||||
// iv. If m.[[Async]] is true, then
|
||||
if (m->async()) {
|
||||
// 1. Perform ! ExecuteAsyncModule(m).
|
||||
ExecuteAsyncModule(isolate, m);
|
||||
} else {
|
||||
// v. 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
}
|
||||
|
||||
void SourceTextModule::AsyncModuleExecutionRejected(
|
||||
Isolate* isolate, Handle<SourceTextModule> module,
|
||||
Handle<Object> exception) {
|
||||
// 1. Assert: module.[[Status]] is "evaluated".
|
||||
CHECK(module->status() == kEvaluated || module->status() == kErrored);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 4. Set module.[[EvaluationError]] to ThrowCompletion(error).
|
||||
module->RecordError(isolate, exception);
|
||||
|
||||
// 5. Set module.[[AsyncEvaluating]] to false.
|
||||
module->set_async_evaluating(false);
|
||||
|
||||
// 6. For each Module m of module.[[AsyncParentModules]], do
|
||||
for (int i = 0; i < module->AsyncParentModuleCount(); i++) {
|
||||
Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i);
|
||||
|
||||
// a. If module.[[DFSIndex]] is not equal to module.[[DFSAncestorIndex]],
|
||||
// then
|
||||
if (module->dfs_index() != module->dfs_ancestor_index()) {
|
||||
// i. Assert: m.[[DFSAncestorIndex]] is equal to
|
||||
// module.[[DFSAncestorIndex]].
|
||||
DCHECK_EQ(m->dfs_ancestor_index(), module->dfs_ancestor_index());
|
||||
}
|
||||
// b. Perform ! AsyncModuleExecutionRejected(m, error).
|
||||
AsyncModuleExecutionRejected(isolate, m, exception);
|
||||
}
|
||||
|
||||
// 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());
|
||||
|
||||
// b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]],
|
||||
// undefined, «error»).
|
||||
Handle<JSPromise> capability(
|
||||
JSPromise::cast(module->top_level_capability()), isolate);
|
||||
JSPromise::Reject(capability, exception);
|
||||
}
|
||||
|
||||
// 8. Return undefined.
|
||||
}
|
||||
|
||||
void SourceTextModule::ExecuteAsyncModule(Isolate* isolate,
|
||||
Handle<SourceTextModule> module) {
|
||||
// 1. Assert: module.[[Status]] is "evaluating" or "evaluated".
|
||||
CHECK(module->status() == kEvaluating || module->status() == kEvaluated);
|
||||
|
||||
// 2. Assert: module.[[Async]] is true.
|
||||
DCHECK(module->async());
|
||||
|
||||
// 3. Set module.[[AsyncEvaluating]] to true.
|
||||
module->set_async_evaluating(true);
|
||||
|
||||
// 4. Let capability be ! NewPromiseCapability(%Promise%).
|
||||
Handle<JSPromise> capability = isolate->factory()->NewJSPromise();
|
||||
|
||||
// 5. Let stepsFulfilled be the steps of a CallAsyncModuleFulfilled
|
||||
Handle<JSFunction> steps_fulfilled(
|
||||
isolate->native_context()->call_async_module_fulfilled(), isolate);
|
||||
|
||||
ScopedVector<Handle<Object>> empty_argv(0);
|
||||
|
||||
// 6. Let onFulfilled be CreateBuiltinFunction(stepsFulfilled,
|
||||
// «[[Module]]»).
|
||||
// 7. Set onFulfilled.[[Module]] to module.
|
||||
Handle<JSBoundFunction> on_fulfilled =
|
||||
isolate->factory()
|
||||
->NewJSBoundFunction(steps_fulfilled, module, empty_argv)
|
||||
.ToHandleChecked();
|
||||
|
||||
// 8. Let stepsRejected be the steps of a CallAsyncModuleRejected.
|
||||
Handle<JSFunction> steps_rejected(
|
||||
isolate->native_context()->call_async_module_rejected(), isolate);
|
||||
|
||||
// 9. Let onRejected be CreateBuiltinFunction(stepsRejected, «[[Module]]»).
|
||||
// 10. Set onRejected.[[Module]] to module.
|
||||
Handle<JSBoundFunction> on_rejected =
|
||||
isolate->factory()
|
||||
->NewJSBoundFunction(steps_rejected, module, empty_argv)
|
||||
.ToHandleChecked();
|
||||
|
||||
// 11. Perform ! PerformPromiseThen(capability.[[Promise]],
|
||||
// onFulfilled, onRejected).
|
||||
Handle<Object> argv[] = {on_fulfilled, on_rejected};
|
||||
Execution::CallBuiltin(isolate, isolate->promise_then(), capability,
|
||||
arraysize(argv), argv)
|
||||
.ToHandleChecked();
|
||||
|
||||
// 12. Perform ! module.ExecuteModule(capability).
|
||||
// Note: In V8 we have broken module.ExecuteModule into
|
||||
// ExecuteModule for synchronous module execution and
|
||||
// InnerExecuteAsyncModule for asynchronous execution.
|
||||
InnerExecuteAsyncModule(isolate, module, capability).ToHandleChecked();
|
||||
|
||||
// 13. Return.
|
||||
}
|
||||
|
||||
MaybeHandle<Object> SourceTextModule::InnerExecuteAsyncModule(
|
||||
Isolate* isolate, Handle<SourceTextModule> module,
|
||||
Handle<JSPromise> capability) {
|
||||
// If we have an async module, then it has an associated
|
||||
// JSAsyncFunctionObject, which we then evaluate with the passed in promise
|
||||
// capability.
|
||||
Handle<JSAsyncFunctionObject> async_function_object(
|
||||
JSAsyncFunctionObject::cast(module->code()), isolate);
|
||||
async_function_object->set_promise(*capability);
|
||||
Handle<JSFunction> resume(
|
||||
isolate->native_context()->async_module_evaluate_internal(), isolate);
|
||||
Handle<Object> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, result,
|
||||
Execution::Call(isolate, resume, async_function_object, 0, nullptr),
|
||||
Object);
|
||||
return result;
|
||||
}
|
||||
|
||||
MaybeHandle<Object> SourceTextModule::ExecuteModule(
|
||||
Isolate* isolate, Handle<SourceTextModule> module) {
|
||||
// Synchronous modules have an associated JSGeneratorObject.
|
||||
Handle<JSGeneratorObject> generator(JSGeneratorObject::cast(module->code()),
|
||||
isolate);
|
||||
Handle<JSFunction> resume(
|
||||
isolate->native_context()->generator_next_internal(), isolate);
|
||||
Handle<Object> result;
|
||||
@ -629,9 +904,192 @@ MaybeHandle<Object> SourceTextModule::Evaluate(
|
||||
isolate, result, Execution::Call(isolate, resume, generator, 0, nullptr),
|
||||
Object);
|
||||
DCHECK(JSIteratorResult::cast(*result).done().BooleanValue(isolate));
|
||||
return handle(JSIteratorResult::cast(*result).value(), isolate);
|
||||
}
|
||||
|
||||
MaybeHandle<Object> SourceTextModule::InnerModuleEvaluation(
|
||||
Isolate* isolate, Handle<SourceTextModule> module,
|
||||
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index) {
|
||||
STACK_CHECK(isolate, MaybeHandle<Object>());
|
||||
|
||||
// InnerModuleEvaluation(module, stack, index)
|
||||
// 2. If module.[[Status]] is "evaluated", then
|
||||
// a. If module.[[EvaluationError]] is undefined, return index.
|
||||
// (We return undefined instead)
|
||||
if (module->status() == kEvaluated || module->status() == kEvaluating) {
|
||||
return isolate->factory()->undefined_value();
|
||||
}
|
||||
|
||||
// b. Otherwise return module.[[EvaluationError]].
|
||||
// (We throw on isolate and return a MaybeHandle<Object>
|
||||
// instead)
|
||||
if (module->status() == kErrored) {
|
||||
isolate->Throw(module->exception());
|
||||
return MaybeHandle<Object>();
|
||||
}
|
||||
|
||||
// 4. Assert: module.[[Status]] is "linked".
|
||||
CHECK_EQ(module->status(), kInstantiated);
|
||||
|
||||
// 5. Set module.[[Status]] to "evaluating".
|
||||
module->SetStatus(kEvaluating);
|
||||
|
||||
// 6. Set module.[[DFSIndex]] to index.
|
||||
module->set_dfs_index(*dfs_index);
|
||||
|
||||
// 7. Set module.[[DFSAncestorIndex]] to index.
|
||||
module->set_dfs_ancestor_index(*dfs_index);
|
||||
|
||||
// 8. Set module.[[PendingAsyncDependencies]] to 0.
|
||||
DCHECK(!module->HasPendingAsyncDependencies());
|
||||
|
||||
// 9. Set module.[[AsyncParentModules]] to a new empty List.
|
||||
Handle<ArrayList> async_parent_modules = ArrayList::New(isolate, 0);
|
||||
module->set_async_parent_modules(*async_parent_modules);
|
||||
|
||||
// 10. Set index to index + 1.
|
||||
(*dfs_index)++;
|
||||
|
||||
// 11. Append module to stack.
|
||||
stack->push_front(module);
|
||||
|
||||
// Recursion.
|
||||
Handle<FixedArray> requested_modules(module->requested_modules(), isolate);
|
||||
|
||||
// 12. For each String required that is an element of
|
||||
// module.[[RequestedModules]], do
|
||||
for (int i = 0, length = requested_modules->length(); i < length; ++i) {
|
||||
Handle<Module> requested_module(Module::cast(requested_modules->get(i)),
|
||||
isolate);
|
||||
// d. If requiredModule is a Cyclic Module Record, then
|
||||
if (requested_module->IsSourceTextModule()) {
|
||||
Handle<SourceTextModule> required_module(
|
||||
SourceTextModule::cast(*requested_module), isolate);
|
||||
RETURN_ON_EXCEPTION(
|
||||
isolate,
|
||||
InnerModuleEvaluation(isolate, required_module, stack, dfs_index),
|
||||
Object);
|
||||
|
||||
// i. Assert: requiredModule.[[Status]] is either "evaluating" or
|
||||
// "evaluated".
|
||||
// (We also assert the module cannot be errored, because if it was
|
||||
// we would have already returned from InnerModuleEvaluation)
|
||||
CHECK_GE(required_module->status(), kEvaluating);
|
||||
CHECK_NE(required_module->status(), kErrored);
|
||||
|
||||
// ii. Assert: requiredModule.[[Status]] is "evaluating" if and
|
||||
// only if requiredModule is in stack.
|
||||
SLOW_DCHECK(
|
||||
(requested_module->status() == kEvaluating) ==
|
||||
std::count_if(stack->begin(), stack->end(), [&](Handle<Module> m) {
|
||||
return *m == *requested_module;
|
||||
}));
|
||||
|
||||
// iii. If requiredModule.[[Status]] is "evaluating", then
|
||||
if (required_module->status() == kEvaluating) {
|
||||
// 1. Set module.[[DFSAncestorIndex]] to
|
||||
// min(
|
||||
// module.[[DFSAncestorIndex]],
|
||||
// requiredModule.[[DFSAncestorIndex]]).
|
||||
module->set_dfs_ancestor_index(
|
||||
std::min(module->dfs_ancestor_index(),
|
||||
required_module->dfs_ancestor_index()));
|
||||
} else {
|
||||
// iv. Otherwise,
|
||||
// 1. Set requiredModule to GetAsyncCycleRoot(requiredModule).
|
||||
required_module = GetAsyncCycleRoot(isolate, required_module);
|
||||
|
||||
// 2. Assert: requiredModule.[[Status]] is "evaluated".
|
||||
CHECK_GE(required_module->status(), kEvaluated);
|
||||
|
||||
// 3. If requiredModule.[[EvaluationError]] is not undefined,
|
||||
// return module.[[EvaluationError]].
|
||||
// (If there was an exception on the original required module
|
||||
// we would have already returned. This check handles the case
|
||||
// where the AsyncCycleRoot has an error. Instead of returning
|
||||
// the exception, we throw on isolate and return a
|
||||
// MaybeHandle<Object>)
|
||||
if (required_module->status() == kErrored) {
|
||||
isolate->Throw(required_module->exception());
|
||||
return MaybeHandle<Object>();
|
||||
}
|
||||
}
|
||||
// v. If requiredModule.[[AsyncEvaluating]] is true, then
|
||||
if (required_module->async_evaluating()) {
|
||||
// 1. Set module.[[PendingAsyncDependencies]] to
|
||||
// module.[[PendingAsyncDependencies]] + 1.
|
||||
module->IncrementPendingAsyncDependencies();
|
||||
|
||||
// 2. Append module to requiredModule.[[AsyncParentModules]].
|
||||
required_module->AddAsyncParentModule(isolate, module);
|
||||
}
|
||||
} else {
|
||||
RETURN_ON_EXCEPTION(isolate, Module::Evaluate(isolate, requested_module),
|
||||
Object);
|
||||
}
|
||||
}
|
||||
|
||||
// The spec returns the module index for proper numbering of dependencies.
|
||||
// However, we pass the module index by pointer instead.
|
||||
//
|
||||
// Before async modules v8 returned the value result from calling next
|
||||
// on the module's implicit iterator. We preserve this behavior for
|
||||
// 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);
|
||||
} else {
|
||||
// 16. Otherwise, perform ? module.ExecuteModule().
|
||||
ASSIGN_RETURN_ON_EXCEPTION(isolate, result, ExecuteModule(isolate, module),
|
||||
Object);
|
||||
}
|
||||
|
||||
CHECK(MaybeTransitionComponent(isolate, module, stack, kEvaluated));
|
||||
return handle(JSIteratorResult::cast(*result).value(), isolate);
|
||||
return result;
|
||||
}
|
||||
|
||||
Handle<SourceTextModule> SourceTextModule::GetAsyncCycleRoot(
|
||||
Isolate* isolate, Handle<SourceTextModule> module) {
|
||||
// 1. Assert: module.[[Status]] is "evaluated".
|
||||
CHECK_GE(module->status(), kEvaluated);
|
||||
|
||||
// 2. If module.[[AsyncParentModules]] is an empty List, return module.
|
||||
if (module->AsyncParentModuleCount() == 0) {
|
||||
return module;
|
||||
}
|
||||
|
||||
// 3. Repeat, while module.[[DFSIndex]] is greater than
|
||||
// module.[[DFSAncestorIndex]],
|
||||
while (module->dfs_index() > module->dfs_ancestor_index()) {
|
||||
// a. Assert: module.[[AsyncParentModules]] is a non-empty List.
|
||||
DCHECK_GT(module->AsyncParentModuleCount(), 0);
|
||||
|
||||
// b. Let nextCycleModule be the first element of
|
||||
// module.[[AsyncParentModules]].
|
||||
Handle<SourceTextModule> next_cycle_module =
|
||||
module->GetAsyncParentModule(isolate, 0);
|
||||
|
||||
// c. Assert: nextCycleModule.[[DFSAncestorIndex]] is less than or equal
|
||||
// to module.[[DFSAncestorIndex]].
|
||||
DCHECK_LE(next_cycle_module->dfs_ancestor_index(),
|
||||
module->dfs_ancestor_index());
|
||||
|
||||
// d. Set module to nextCycleModule
|
||||
module = next_cycle_module;
|
||||
}
|
||||
|
||||
// 4. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]].
|
||||
DCHECK_EQ(module->dfs_index(), module->dfs_ancestor_index());
|
||||
|
||||
// 5. Return module.
|
||||
return module;
|
||||
}
|
||||
|
||||
void SourceTextModule::Reset(Isolate* isolate,
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define V8_OBJECTS_SOURCE_TEXT_MODULE_H_
|
||||
|
||||
#include "src/objects/module.h"
|
||||
#include "src/objects/promise.h"
|
||||
|
||||
// Has to be the last include (doesn't have include guards):
|
||||
#include "src/objects/object-macros.h"
|
||||
@ -28,6 +29,10 @@ class SourceTextModule
|
||||
// kErrored.
|
||||
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.
|
||||
inline SourceTextModuleInfo info() const;
|
||||
|
||||
@ -41,6 +46,14 @@ class SourceTextModule
|
||||
static int ImportIndex(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
|
||||
// exist yet, it is created.
|
||||
static Handle<JSModuleNamespace> GetModuleNamespace(
|
||||
@ -54,12 +67,54 @@ class SourceTextModule
|
||||
friend class Factory;
|
||||
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?
|
||||
DECL_INT_ACCESSORS(dfs_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,
|
||||
int cell_index, Handle<FixedArray> names);
|
||||
static void CreateIndirectExport(Isolate* isolate,
|
||||
@ -95,7 +150,16 @@ class SourceTextModule
|
||||
Handle<SourceTextModule> module, Zone* zone,
|
||||
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(
|
||||
Isolate* isolate, Handle<SourceTextModule> module);
|
||||
|
||||
// Implementation of spec abstract operation InnerModuleEvaluation.
|
||||
static V8_WARN_UNUSED_RESULT MaybeHandle<Object> InnerModuleEvaluation(
|
||||
Isolate* isolate, Handle<SourceTextModule> module,
|
||||
ZoneForwardList<Handle<SourceTextModule>>* stack, unsigned* dfs_index);
|
||||
|
||||
@ -103,6 +167,24 @@ class SourceTextModule
|
||||
Isolate* isolate, Handle<SourceTextModule> module,
|
||||
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);
|
||||
|
||||
TQ_OBJECT_CONSTRUCTORS(SourceTextModule)
|
||||
|
@ -96,7 +96,7 @@ MaybeHandle<Object> SyntheticModule::Evaluate(Isolate* isolate,
|
||||
Utils::ToLocal(Handle<Module>::cast(module)))
|
||||
.ToLocal(&result)) {
|
||||
isolate->PromoteScheduledException();
|
||||
module->RecordError(isolate);
|
||||
module->RecordErrorUsingPendingException(isolate);
|
||||
return MaybeHandle<Object>();
|
||||
}
|
||||
|
||||
|
@ -23765,7 +23765,13 @@ TEST(ModuleCodeCache) {
|
||||
// Evaluate for possible lazy compilation.
|
||||
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());
|
||||
}
|
||||
|
||||
// Now create the cache. Note that it is freed, obscurely, when
|
||||
// ScriptCompiler::Source goes out of scope below.
|
||||
@ -23796,7 +23802,13 @@ TEST(ModuleCodeCache) {
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
isolate->Dispose();
|
||||
}
|
||||
@ -23975,7 +23987,13 @@ TEST(ImportFromSyntheticModule) {
|
||||
.ToChecked();
|
||||
|
||||
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) {
|
||||
@ -24005,7 +24023,15 @@ TEST(ImportFromSyntheticModuleThrow) {
|
||||
CHECK_EQ(module->GetStatus(), Module::kInstantiated);
|
||||
TryCatch try_catch(isolate);
|
||||
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(try_catch.HasCaught());
|
||||
}
|
||||
@ -24038,7 +24064,13 @@ TEST(CodeCacheModuleScriptMismatch) {
|
||||
// Evaluate for possible lazy compilation.
|
||||
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());
|
||||
}
|
||||
|
||||
// Now create the cache. Note that it is freed, obscurely, when
|
||||
// ScriptCompiler::Source goes out of scope below.
|
||||
@ -24134,7 +24166,13 @@ TEST(CodeCacheScriptModuleMismatch) {
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
isolate->Dispose();
|
||||
}
|
||||
@ -24170,10 +24208,14 @@ TEST(InvalidCodeCacheDataInCompileModule) {
|
||||
.ToChecked();
|
||||
|
||||
CHECK(cached_data->rejected);
|
||||
CHECK_EQ(42, module->Evaluate(context)
|
||||
.ToLocalChecked()
|
||||
->Int32Value(context)
|
||||
.FromJust());
|
||||
Local<Value> completion_value = module->Evaluate(context).ToLocalChecked();
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
void TestInvalidCacheData(v8::ScriptCompiler::CompileOptions option) {
|
||||
@ -25818,7 +25860,14 @@ TEST(ImportMeta) {
|
||||
module->InstantiateModule(context.local(), UnexpectedModuleResolveCallback)
|
||||
.ToChecked();
|
||||
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) {
|
||||
|
@ -14,6 +14,7 @@ using v8::Isolate;
|
||||
using v8::Local;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Module;
|
||||
using v8::Promise;
|
||||
using v8::ScriptCompiler;
|
||||
using v8::ScriptOrigin;
|
||||
using v8::String;
|
||||
@ -196,149 +197,369 @@ static MaybeLocal<Module> CompileSpecifierAsModuleResolveCallback(
|
||||
}
|
||||
|
||||
TEST(ModuleEvaluation) {
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
bool prev_top_level_await = i::FLAG_harmony_top_level_await;
|
||||
for (auto top_level_await : {true, false}) {
|
||||
i::FLAG_harmony_top_level_await = top_level_await;
|
||||
|
||||
Local<String> source_text = v8_str(
|
||||
"import 'Object.expando = 5';"
|
||||
"import 'Object.expando *= 2';");
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
|
||||
CHECK(module
|
||||
->InstantiateModule(env.local(),
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
CHECK(!module->Evaluate(env.local()).IsEmpty());
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
ExpectInt32("Object.expando", 10);
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
Local<String> source_text = v8_str(
|
||||
"import 'Object.expando = 5';"
|
||||
"import 'Object.expando *= 2';");
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
|
||||
CHECK(module
|
||||
->InstantiateModule(env.local(),
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
|
||||
MaybeLocal<Value> result = module->Evaluate(env.local());
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
if (i::FLAG_harmony_top_level_await) {
|
||||
Local<Promise> promise = Local<Promise>::Cast(result.ToLocalChecked());
|
||||
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
|
||||
CHECK(promise->Result()->IsUndefined());
|
||||
} else {
|
||||
CHECK(!result.IsEmpty());
|
||||
ExpectInt32("Object.expando", 10);
|
||||
}
|
||||
CHECK(!try_catch.HasCaught());
|
||||
}
|
||||
i::FLAG_harmony_top_level_await = prev_top_level_await;
|
||||
}
|
||||
|
||||
TEST(ModuleEvaluationError) {
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
TEST(ModuleEvaluationError1) {
|
||||
bool prev_top_level_await = i::FLAG_harmony_top_level_await;
|
||||
for (auto top_level_await : {true, false}) {
|
||||
i::FLAG_harmony_top_level_await = top_level_await;
|
||||
|
||||
Local<String> source_text =
|
||||
v8_str("Object.x = (Object.x || 0) + 1; throw 'boom';");
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
|
||||
CHECK(module
|
||||
->InstantiateModule(env.local(),
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
CHECK(module->Evaluate(env.local()).IsEmpty());
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
|
||||
CHECK_EQ(Module::kErrored, module->GetStatus());
|
||||
Local<Value> exception = module->GetException();
|
||||
CHECK(exception->StrictEquals(v8_str("boom")));
|
||||
ExpectInt32("Object.x", 1);
|
||||
Local<String> source_text =
|
||||
v8_str("Object.x = (Object.x || 0) + 1; throw 'boom';");
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
|
||||
CHECK(module
|
||||
->InstantiateModule(env.local(),
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
|
||||
MaybeLocal<Value> result_1;
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
result_1 = module->Evaluate(env.local());
|
||||
CHECK_EQ(Module::kErrored, module->GetStatus());
|
||||
Local<Value> exception = module->GetException();
|
||||
CHECK(exception->StrictEquals(v8_str("boom")));
|
||||
ExpectInt32("Object.x", 1);
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
|
||||
}
|
||||
|
||||
MaybeLocal<Value> result_2;
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
result_2 = module->Evaluate(env.local());
|
||||
CHECK_EQ(Module::kErrored, module->GetStatus());
|
||||
Local<Value> exception = module->GetException();
|
||||
CHECK(exception->StrictEquals(v8_str("boom")));
|
||||
ExpectInt32("Object.x", 1);
|
||||
|
||||
if (i::FLAG_harmony_top_level_await) {
|
||||
// With top level await we do not rethrow the exception.
|
||||
CHECK(!inner_try_catch.HasCaught());
|
||||
} else {
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
|
||||
}
|
||||
}
|
||||
if (i::FLAG_harmony_top_level_await) {
|
||||
// With top level await, errored evaluation returns a rejected promise
|
||||
// with the exception.
|
||||
Local<Promise> promise_1 =
|
||||
Local<Promise>::Cast(result_1.ToLocalChecked());
|
||||
Local<Promise> promise_2 =
|
||||
Local<Promise>::Cast(result_2.ToLocalChecked());
|
||||
CHECK_EQ(promise_1->State(), v8::Promise::kRejected);
|
||||
CHECK_EQ(promise_2->State(), v8::Promise::kRejected);
|
||||
CHECK_EQ(promise_1->Result(), module->GetException());
|
||||
CHECK_EQ(promise_2->Result(), module->GetException());
|
||||
} else {
|
||||
CHECK(result_1.IsEmpty() && result_2.IsEmpty());
|
||||
}
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
}
|
||||
i::FLAG_harmony_top_level_await = prev_top_level_await;
|
||||
}
|
||||
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
CHECK(module->Evaluate(env.local()).IsEmpty());
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
|
||||
CHECK_EQ(Module::kErrored, module->GetStatus());
|
||||
Local<Value> exception = module->GetException();
|
||||
CHECK(exception->StrictEquals(v8_str("boom")));
|
||||
ExpectInt32("Object.x", 1);
|
||||
static Local<Module> failure_module;
|
||||
static Local<Module> dependent_module;
|
||||
MaybeLocal<Module> ResolveCallbackForModuleEvaluationError2(
|
||||
Local<Context> context, Local<String> specifier, Local<Module> referrer) {
|
||||
if (specifier->StrictEquals(v8_str("./failure.js"))) {
|
||||
return failure_module;
|
||||
} else {
|
||||
CHECK(specifier->StrictEquals(v8_str("./dependent.js")));
|
||||
return dependent_module;
|
||||
}
|
||||
}
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
TEST(ModuleEvaluationError2) {
|
||||
bool prev_top_level_await = i::FLAG_harmony_top_level_await;
|
||||
for (auto top_level_await : {true, false}) {
|
||||
i::FLAG_harmony_top_level_await = top_level_await;
|
||||
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
Local<String> failure_text = v8_str("throw 'boom';");
|
||||
ScriptOrigin failure_origin =
|
||||
ModuleOrigin(v8_str("failure.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source failure_source(failure_text, failure_origin);
|
||||
failure_module = ScriptCompiler::CompileModule(isolate, &failure_source)
|
||||
.ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, failure_module->GetStatus());
|
||||
CHECK(failure_module
|
||||
->InstantiateModule(env.local(),
|
||||
ResolveCallbackForModuleEvaluationError2)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, failure_module->GetStatus());
|
||||
|
||||
MaybeLocal<Value> result_1;
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
result_1 = failure_module->Evaluate(env.local());
|
||||
CHECK_EQ(Module::kErrored, failure_module->GetStatus());
|
||||
Local<Value> exception = failure_module->GetException();
|
||||
CHECK(exception->StrictEquals(v8_str("boom")));
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
|
||||
}
|
||||
|
||||
Local<String> dependent_text =
|
||||
v8_str("import './failure.js'; export const c = 123;");
|
||||
ScriptOrigin dependent_origin =
|
||||
ModuleOrigin(v8_str("dependent.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source dependent_source(dependent_text, dependent_origin);
|
||||
dependent_module = ScriptCompiler::CompileModule(isolate, &dependent_source)
|
||||
.ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, dependent_module->GetStatus());
|
||||
CHECK(dependent_module
|
||||
->InstantiateModule(env.local(),
|
||||
ResolveCallbackForModuleEvaluationError2)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, dependent_module->GetStatus());
|
||||
|
||||
MaybeLocal<Value> result_2;
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
result_2 = dependent_module->Evaluate(env.local());
|
||||
CHECK_EQ(Module::kErrored, dependent_module->GetStatus());
|
||||
Local<Value> exception = dependent_module->GetException();
|
||||
CHECK(exception->StrictEquals(v8_str("boom")));
|
||||
CHECK_EQ(exception, failure_module->GetException());
|
||||
|
||||
if (i::FLAG_harmony_top_level_await) {
|
||||
// With top level await we do not rethrow the exception.
|
||||
CHECK(!inner_try_catch.HasCaught());
|
||||
} else {
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()->StrictEquals(v8_str("boom")));
|
||||
}
|
||||
}
|
||||
|
||||
if (i::FLAG_harmony_top_level_await) {
|
||||
// With top level await, errored evaluation returns a rejected promise
|
||||
// with the exception.
|
||||
Local<Promise> promise_1 =
|
||||
Local<Promise>::Cast(result_1.ToLocalChecked());
|
||||
Local<Promise> promise_2 =
|
||||
Local<Promise>::Cast(result_2.ToLocalChecked());
|
||||
CHECK_EQ(promise_1->State(), v8::Promise::kRejected);
|
||||
CHECK_EQ(promise_2->State(), v8::Promise::kRejected);
|
||||
CHECK_EQ(promise_1->Result(), failure_module->GetException());
|
||||
CHECK_EQ(promise_2->Result(), failure_module->GetException());
|
||||
} else {
|
||||
CHECK(result_1.IsEmpty() && result_2.IsEmpty());
|
||||
}
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
}
|
||||
i::FLAG_harmony_top_level_await = prev_top_level_await;
|
||||
}
|
||||
|
||||
TEST(ModuleEvaluationCompletion1) {
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
bool prev_top_level_await = i::FLAG_harmony_top_level_await;
|
||||
for (auto top_level_await : {true, false}) {
|
||||
i::FLAG_harmony_top_level_await = top_level_await;
|
||||
|
||||
const char* sources[] = {
|
||||
"",
|
||||
"var a = 1",
|
||||
"import '42'",
|
||||
"export * from '42'",
|
||||
"export {} from '42'",
|
||||
"export {}",
|
||||
"var a = 1; export {a}",
|
||||
"export function foo() {}",
|
||||
"export class C extends null {}",
|
||||
"export let a = 1",
|
||||
"export default 1",
|
||||
"export default function foo() {}",
|
||||
"export default function () {}",
|
||||
"export default (function () {})",
|
||||
"export default class C extends null {}",
|
||||
"export default (class C extends null {})",
|
||||
"for (var i = 0; i < 5; ++i) {}",
|
||||
};
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
for (auto src : sources) {
|
||||
Local<String> source_text = v8_str(src);
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
|
||||
CHECK(module
|
||||
->InstantiateModule(env.local(),
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined());
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined());
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
const char* sources[] = {
|
||||
"",
|
||||
"var a = 1",
|
||||
"import '42'",
|
||||
"export * from '42'",
|
||||
"export {} from '42'",
|
||||
"export {}",
|
||||
"var a = 1; export {a}",
|
||||
"export function foo() {}",
|
||||
"export class C extends null {}",
|
||||
"export let a = 1",
|
||||
"export default 1",
|
||||
"export default function foo() {}",
|
||||
"export default function () {}",
|
||||
"export default (function () {})",
|
||||
"export default class C extends null {}",
|
||||
"export default (class C extends null {})",
|
||||
"for (var i = 0; i < 5; ++i) {}",
|
||||
};
|
||||
|
||||
for (auto src : sources) {
|
||||
Local<String> source_text = v8_str(src);
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
|
||||
CHECK(module
|
||||
->InstantiateModule(env.local(),
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
|
||||
// Evaluate twice.
|
||||
Local<Value> result_1 = module->Evaluate(env.local()).ToLocalChecked();
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
Local<Value> result_2 = module->Evaluate(env.local()).ToLocalChecked();
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
|
||||
if (i::FLAG_harmony_top_level_await) {
|
||||
Local<Promise> promise = Local<Promise>::Cast(result_1);
|
||||
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
|
||||
CHECK(promise->Result()->IsUndefined());
|
||||
|
||||
// Second evaluation should return the same promise.
|
||||
Local<Promise> promise_too = Local<Promise>::Cast(result_2);
|
||||
CHECK_EQ(promise, promise_too);
|
||||
CHECK_EQ(promise_too->State(), v8::Promise::kFulfilled);
|
||||
CHECK(promise_too->Result()->IsUndefined());
|
||||
} else {
|
||||
CHECK(result_1->IsUndefined());
|
||||
CHECK(result_2->IsUndefined());
|
||||
}
|
||||
}
|
||||
CHECK(!try_catch.HasCaught());
|
||||
}
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
i::FLAG_harmony_top_level_await = prev_top_level_await;
|
||||
}
|
||||
|
||||
TEST(ModuleEvaluationCompletion2) {
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
bool prev_top_level_await = i::FLAG_harmony_top_level_await;
|
||||
for (auto top_level_await : {true, false}) {
|
||||
i::FLAG_harmony_top_level_await = top_level_await;
|
||||
|
||||
const char* sources[] = {
|
||||
"'gaga'; ",
|
||||
"'gaga'; var a = 1",
|
||||
"'gaga'; import '42'",
|
||||
"'gaga'; export * from '42'",
|
||||
"'gaga'; export {} from '42'",
|
||||
"'gaga'; export {}",
|
||||
"'gaga'; var a = 1; export {a}",
|
||||
"'gaga'; export function foo() {}",
|
||||
"'gaga'; export class C extends null {}",
|
||||
"'gaga'; export let a = 1",
|
||||
"'gaga'; export default 1",
|
||||
"'gaga'; export default function foo() {}",
|
||||
"'gaga'; export default function () {}",
|
||||
"'gaga'; export default (function () {})",
|
||||
"'gaga'; export default class C extends null {}",
|
||||
"'gaga'; export default (class C extends null {})",
|
||||
};
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
for (auto src : sources) {
|
||||
Local<String> source_text = v8_str(src);
|
||||
const char* sources[] = {
|
||||
"'gaga'; ",
|
||||
"'gaga'; var a = 1",
|
||||
"'gaga'; import '42'",
|
||||
"'gaga'; export * from '42'",
|
||||
"'gaga'; export {} from '42'",
|
||||
"'gaga'; export {}",
|
||||
"'gaga'; var a = 1; export {a}",
|
||||
"'gaga'; export function foo() {}",
|
||||
"'gaga'; export class C extends null {}",
|
||||
"'gaga'; export let a = 1",
|
||||
"'gaga'; export default 1",
|
||||
"'gaga'; export default function foo() {}",
|
||||
"'gaga'; export default function () {}",
|
||||
"'gaga'; export default (function () {})",
|
||||
"'gaga'; export default class C extends null {}",
|
||||
"'gaga'; export default (class C extends null {})",
|
||||
};
|
||||
|
||||
for (auto src : sources) {
|
||||
Local<String> source_text = v8_str(src);
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
|
||||
CHECK(module
|
||||
->InstantiateModule(env.local(),
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
|
||||
Local<Value> result_1 = module->Evaluate(env.local()).ToLocalChecked();
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
|
||||
Local<Value> result_2 = module->Evaluate(env.local()).ToLocalChecked();
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
if (i::FLAG_harmony_top_level_await) {
|
||||
Local<Promise> promise = Local<Promise>::Cast(result_1);
|
||||
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
|
||||
CHECK(promise->Result()->IsUndefined());
|
||||
|
||||
// Second Evaluation should return the same promise.
|
||||
Local<Promise> promise_too = Local<Promise>::Cast(result_2);
|
||||
CHECK_EQ(promise, promise_too);
|
||||
CHECK_EQ(promise_too->State(), v8::Promise::kFulfilled);
|
||||
CHECK(promise_too->Result()->IsUndefined());
|
||||
} else {
|
||||
CHECK(result_1->StrictEquals(v8_str("gaga")));
|
||||
CHECK(result_2->IsUndefined());
|
||||
}
|
||||
}
|
||||
CHECK(!try_catch.HasCaught());
|
||||
}
|
||||
i::FLAG_harmony_top_level_await = prev_top_level_await;
|
||||
}
|
||||
|
||||
TEST(ModuleNamespace) {
|
||||
bool prev_top_level_await = i::FLAG_harmony_top_level_await;
|
||||
for (auto top_level_await : {true, false}) {
|
||||
i::FLAG_harmony_top_level_await = top_level_await;
|
||||
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
Local<v8::Object> ReferenceError =
|
||||
CompileRun("ReferenceError")->ToObject(env.local()).ToLocalChecked();
|
||||
|
||||
Local<String> source_text = v8_str(
|
||||
"import {a, b} from 'export var a = 1; export let b = 2';"
|
||||
"export function geta() {return a};"
|
||||
"export function getb() {return b};"
|
||||
"export let radio = 3;"
|
||||
"export var gaga = 4;");
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
@ -349,126 +570,92 @@ TEST(ModuleEvaluationCompletion2) {
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
CHECK(module->Evaluate(env.local())
|
||||
.ToLocalChecked()
|
||||
->StrictEquals(v8_str("gaga")));
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
CHECK(module->Evaluate(env.local()).ToLocalChecked()->IsUndefined());
|
||||
Local<Value> ns = module->GetModuleNamespace();
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
Local<v8::Object> nsobj = ns->ToObject(env.local()).ToLocalChecked();
|
||||
|
||||
// a, b
|
||||
CHECK(nsobj->Get(env.local(), v8_str("a")).ToLocalChecked()->IsUndefined());
|
||||
CHECK(nsobj->Get(env.local(), v8_str("b")).ToLocalChecked()->IsUndefined());
|
||||
|
||||
// geta
|
||||
{
|
||||
auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked();
|
||||
auto a = geta.As<v8::Function>()
|
||||
->Call(env.local(), geta, 0, nullptr)
|
||||
.ToLocalChecked();
|
||||
CHECK(a->IsUndefined());
|
||||
}
|
||||
|
||||
// getb
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked();
|
||||
CHECK(getb.As<v8::Function>()
|
||||
->Call(env.local(), getb, 0, nullptr)
|
||||
.IsEmpty());
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()
|
||||
->InstanceOf(env.local(), ReferenceError)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// radio
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
// https://bugs.chromium.org/p/v8/issues/detail?id=7235
|
||||
// CHECK(nsobj->Get(env.local(), v8_str("radio")).IsEmpty());
|
||||
CHECK(nsobj->Get(env.local(), v8_str("radio"))
|
||||
.ToLocalChecked()
|
||||
->IsUndefined());
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()
|
||||
->InstanceOf(env.local(), ReferenceError)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// gaga
|
||||
{
|
||||
auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked();
|
||||
CHECK(gaga->IsUndefined());
|
||||
}
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
module->Evaluate(env.local()).ToLocalChecked();
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
|
||||
// geta
|
||||
{
|
||||
auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked();
|
||||
auto a = geta.As<v8::Function>()
|
||||
->Call(env.local(), geta, 0, nullptr)
|
||||
.ToLocalChecked();
|
||||
CHECK_EQ(1, a->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
|
||||
// getb
|
||||
{
|
||||
auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked();
|
||||
auto b = getb.As<v8::Function>()
|
||||
->Call(env.local(), getb, 0, nullptr)
|
||||
.ToLocalChecked();
|
||||
CHECK_EQ(2, b->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
|
||||
// radio
|
||||
{
|
||||
auto radio = nsobj->Get(env.local(), v8_str("radio")).ToLocalChecked();
|
||||
CHECK_EQ(3, radio->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
|
||||
// gaga
|
||||
{
|
||||
auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked();
|
||||
CHECK_EQ(4, gaga->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
CHECK(!try_catch.HasCaught());
|
||||
}
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
}
|
||||
|
||||
TEST(ModuleNamespace) {
|
||||
Isolate* isolate = CcTest::isolate();
|
||||
HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
v8::TryCatch try_catch(isolate);
|
||||
|
||||
Local<v8::Object> ReferenceError =
|
||||
CompileRun("ReferenceError")->ToObject(env.local()).ToLocalChecked();
|
||||
|
||||
Local<String> source_text = v8_str(
|
||||
"import {a, b} from 'export var a = 1; export let b = 2';"
|
||||
"export function geta() {return a};"
|
||||
"export function getb() {return b};"
|
||||
"export let radio = 3;"
|
||||
"export var gaga = 4;");
|
||||
ScriptOrigin origin = ModuleOrigin(v8_str("file.js"), CcTest::isolate());
|
||||
ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
CHECK_EQ(Module::kUninstantiated, module->GetStatus());
|
||||
CHECK(module
|
||||
->InstantiateModule(env.local(),
|
||||
CompileSpecifierAsModuleResolveCallback)
|
||||
.FromJust());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
Local<Value> ns = module->GetModuleNamespace();
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
Local<v8::Object> nsobj = ns->ToObject(env.local()).ToLocalChecked();
|
||||
|
||||
// a, b
|
||||
CHECK(nsobj->Get(env.local(), v8_str("a")).ToLocalChecked()->IsUndefined());
|
||||
CHECK(nsobj->Get(env.local(), v8_str("b")).ToLocalChecked()->IsUndefined());
|
||||
|
||||
// geta
|
||||
{
|
||||
auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked();
|
||||
auto a = geta.As<v8::Function>()
|
||||
->Call(env.local(), geta, 0, nullptr)
|
||||
.ToLocalChecked();
|
||||
CHECK(a->IsUndefined());
|
||||
}
|
||||
|
||||
// getb
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked();
|
||||
CHECK(
|
||||
getb.As<v8::Function>()->Call(env.local(), getb, 0, nullptr).IsEmpty());
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()
|
||||
->InstanceOf(env.local(), ReferenceError)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// radio
|
||||
{
|
||||
v8::TryCatch inner_try_catch(isolate);
|
||||
// https://bugs.chromium.org/p/v8/issues/detail?id=7235
|
||||
// CHECK(nsobj->Get(env.local(), v8_str("radio")).IsEmpty());
|
||||
CHECK(nsobj->Get(env.local(), v8_str("radio"))
|
||||
.ToLocalChecked()
|
||||
->IsUndefined());
|
||||
CHECK(inner_try_catch.HasCaught());
|
||||
CHECK(inner_try_catch.Exception()
|
||||
->InstanceOf(env.local(), ReferenceError)
|
||||
.FromJust());
|
||||
}
|
||||
|
||||
// gaga
|
||||
{
|
||||
auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked();
|
||||
CHECK(gaga->IsUndefined());
|
||||
}
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
CHECK_EQ(Module::kInstantiated, module->GetStatus());
|
||||
module->Evaluate(env.local()).ToLocalChecked();
|
||||
CHECK_EQ(Module::kEvaluated, module->GetStatus());
|
||||
|
||||
// geta
|
||||
{
|
||||
auto geta = nsobj->Get(env.local(), v8_str("geta")).ToLocalChecked();
|
||||
auto a = geta.As<v8::Function>()
|
||||
->Call(env.local(), geta, 0, nullptr)
|
||||
.ToLocalChecked();
|
||||
CHECK_EQ(1, a->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
|
||||
// getb
|
||||
{
|
||||
auto getb = nsobj->Get(env.local(), v8_str("getb")).ToLocalChecked();
|
||||
auto b = getb.As<v8::Function>()
|
||||
->Call(env.local(), getb, 0, nullptr)
|
||||
.ToLocalChecked();
|
||||
CHECK_EQ(2, b->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
|
||||
// radio
|
||||
{
|
||||
auto radio = nsobj->Get(env.local(), v8_str("radio")).ToLocalChecked();
|
||||
CHECK_EQ(3, radio->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
|
||||
// gaga
|
||||
{
|
||||
auto gaga = nsobj->Get(env.local(), v8_str("gaga")).ToLocalChecked();
|
||||
CHECK_EQ(4, gaga->Int32Value(env.local()).FromJust());
|
||||
}
|
||||
|
||||
CHECK(!try_catch.HasCaught());
|
||||
i::FLAG_harmony_top_level_await = prev_top_level_await;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
Loading…
Reference in New Issue
Block a user