Reland "[async] Optimize await and AsyncFromSyncIterator"
This is a reland of 21c0d77e15
Original change's description:
> [async] Optimize await and AsyncFromSyncIterator
>
> Simplify the promise wrapping in await and
> %AsyncFromSyncIteratorPrototype%.next/return/throw to reuse the PromiseResolve
> primitive. Now await takes 1 tick instead of 3 on the microtask queue.
>
> Change-Id: I7e99b8689eb8fcb09c48915b11c1e06684dc0f1a
> Reviewed-on: https://chromium-review.googlesource.com/1090272
> Commit-Queue: Maya Lekova <mslekova@chromium.org>
> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
> Reviewed-by: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
> Reviewed-by: Mathias Bynens <mathias@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#53853}
Cq-Include-Trybots: luci.chromium.try:linux_chromium_headless_rel;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: Ifa5b2fb8b2fb84b635b2dc1b6455d6aaf154cbfd
Reviewed-on: https://chromium-review.googlesource.com/1106977
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53900}
This commit is contained in:
parent
5e57f660ae
commit
ef8c18613a
@ -4543,6 +4543,57 @@ void Genesis::InitializeGlobal_harmony_bigint() {
|
||||
Builtins::kDataViewPrototypeSetBigUint64, 2, false);
|
||||
}
|
||||
|
||||
void Genesis::InitializeGlobal_harmony_await_optimization() {
|
||||
if (!FLAG_harmony_await_optimization) return;
|
||||
|
||||
// async/await
|
||||
Handle<JSFunction> await_caught_function = SimpleCreateFunction(
|
||||
isolate(), factory()->empty_string(),
|
||||
Builtins::kAsyncFunctionAwaitCaughtOptimized, 2, false);
|
||||
native_context()->set_async_function_await_caught(*await_caught_function);
|
||||
|
||||
Handle<JSFunction> await_uncaught_function = SimpleCreateFunction(
|
||||
isolate(), factory()->empty_string(),
|
||||
Builtins::kAsyncFunctionAwaitUncaughtOptimized, 2, false);
|
||||
native_context()->set_async_function_await_uncaught(*await_uncaught_function);
|
||||
|
||||
// async generators
|
||||
Handle<JSObject> async_iterator_prototype =
|
||||
factory()->NewJSObject(isolate()->object_function(), TENURED);
|
||||
|
||||
SimpleInstallFunction(
|
||||
isolate(), async_iterator_prototype, factory()->async_iterator_symbol(),
|
||||
"[Symbol.asyncIterator]", Builtins::kReturnReceiver, 0, true);
|
||||
|
||||
Handle<JSObject> async_from_sync_iterator_prototype =
|
||||
factory()->NewJSObject(isolate()->object_function(), TENURED);
|
||||
SimpleInstallFunction(
|
||||
isolate(), async_from_sync_iterator_prototype, factory()->next_string(),
|
||||
Builtins::kAsyncFromSyncIteratorPrototypeNextOptimized, 1, true);
|
||||
SimpleInstallFunction(
|
||||
isolate(), async_from_sync_iterator_prototype, factory()->return_string(),
|
||||
Builtins::kAsyncFromSyncIteratorPrototypeReturnOptimized, 1, true);
|
||||
SimpleInstallFunction(
|
||||
isolate(), async_from_sync_iterator_prototype, factory()->throw_string(),
|
||||
Builtins::kAsyncFromSyncIteratorPrototypeThrowOptimized, 1, true);
|
||||
|
||||
JSObject::AddProperty(
|
||||
isolate(), async_from_sync_iterator_prototype,
|
||||
factory()->to_string_tag_symbol(),
|
||||
factory()->NewStringFromAsciiChecked("Async-from-Sync Iterator"),
|
||||
static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY));
|
||||
|
||||
JSObject::ForceSetPrototype(async_from_sync_iterator_prototype,
|
||||
async_iterator_prototype);
|
||||
|
||||
Handle<Map> async_from_sync_iterator_map = factory()->NewMap(
|
||||
JS_ASYNC_FROM_SYNC_ITERATOR_TYPE, JSAsyncFromSyncIterator::kSize);
|
||||
Map::SetPrototype(async_from_sync_iterator_map,
|
||||
async_from_sync_iterator_prototype);
|
||||
native_context()->set_async_from_sync_iterator_map(
|
||||
*async_from_sync_iterator_map);
|
||||
}
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
|
||||
void Genesis::InitializeGlobal_harmony_locale() {
|
||||
|
@ -20,6 +20,10 @@ class AsyncFunctionBuiltinsAssembler : public AsyncBuiltinsAssembler {
|
||||
void AsyncFunctionAwait(Node* const context, Node* const generator,
|
||||
Node* const awaited, Node* const outer_promise,
|
||||
const bool is_predicted_as_caught);
|
||||
void AsyncFunctionAwaitOptimized(Node* const context, Node* const generator,
|
||||
Node* const awaited,
|
||||
Node* const outer_promise,
|
||||
const bool is_predicted_as_caught);
|
||||
|
||||
void AsyncFunctionAwaitResumeClosure(
|
||||
Node* const context, Node* const sent_value,
|
||||
@ -139,6 +143,43 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
|
||||
Goto(&after_debug_hook);
|
||||
}
|
||||
|
||||
void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwaitOptimized(
|
||||
Node* const context, Node* const generator, Node* const awaited,
|
||||
Node* const outer_promise, const bool is_predicted_as_caught) {
|
||||
CSA_SLOW_ASSERT(this, HasInstanceType(generator, JS_GENERATOR_OBJECT_TYPE));
|
||||
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
|
||||
|
||||
ContextInitializer init_closure_context = [&](Node* context) {
|
||||
StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
|
||||
generator);
|
||||
};
|
||||
|
||||
// TODO(jgruber): AsyncBuiltinsAssembler::Await currently does not reuse
|
||||
// the awaited promise if it is already a promise. Reuse is non-spec compliant
|
||||
// but part of our old behavior gives us a couple of percent
|
||||
// performance boost.
|
||||
// TODO(jgruber): Use a faster specialized version of
|
||||
// InternalPerformPromiseThen.
|
||||
|
||||
Label after_debug_hook(this), call_debug_hook(this, Label::kDeferred);
|
||||
GotoIf(HasAsyncEventDelegate(), &call_debug_hook);
|
||||
Goto(&after_debug_hook);
|
||||
BIND(&after_debug_hook);
|
||||
|
||||
AwaitOptimized(
|
||||
context, generator, awaited, outer_promise, AwaitContext::kLength,
|
||||
init_closure_context, Context::ASYNC_FUNCTION_AWAIT_RESOLVE_SHARED_FUN,
|
||||
Context::ASYNC_FUNCTION_AWAIT_REJECT_SHARED_FUN, is_predicted_as_caught);
|
||||
|
||||
// Return outer promise to avoid adding an load of the outer promise before
|
||||
// suspending in BytecodeGenerator.
|
||||
Return(outer_promise);
|
||||
|
||||
BIND(&call_debug_hook);
|
||||
CallRuntime(Runtime::kDebugAsyncFunctionSuspended, context, outer_promise);
|
||||
Goto(&after_debug_hook);
|
||||
}
|
||||
|
||||
// Called by the parser from the desugaring of 'await' when catch
|
||||
// prediction indicates that there is a locally surrounding catch block.
|
||||
TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
|
||||
@ -154,6 +195,19 @@ TF_BUILTIN(AsyncFunctionAwaitCaught, AsyncFunctionBuiltinsAssembler) {
|
||||
kIsPredictedAsCaught);
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncFunctionAwaitCaughtOptimized, AsyncFunctionBuiltinsAssembler) {
|
||||
CSA_ASSERT_JS_ARGC_EQ(this, 3);
|
||||
Node* const generator = Parameter(Descriptor::kGenerator);
|
||||
Node* const awaited = Parameter(Descriptor::kAwaited);
|
||||
Node* const outer_promise = Parameter(Descriptor::kOuterPromise);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
static const bool kIsPredictedAsCaught = true;
|
||||
|
||||
AsyncFunctionAwaitOptimized(context, generator, awaited, outer_promise,
|
||||
kIsPredictedAsCaught);
|
||||
}
|
||||
|
||||
// Called by the parser from the desugaring of 'await' when catch
|
||||
// prediction indicates no locally surrounding catch block.
|
||||
TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
|
||||
@ -169,6 +223,20 @@ TF_BUILTIN(AsyncFunctionAwaitUncaught, AsyncFunctionBuiltinsAssembler) {
|
||||
kIsPredictedAsCaught);
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncFunctionAwaitUncaughtOptimized,
|
||||
AsyncFunctionBuiltinsAssembler) {
|
||||
CSA_ASSERT_JS_ARGC_EQ(this, 3);
|
||||
Node* const generator = Parameter(Descriptor::kGenerator);
|
||||
Node* const awaited = Parameter(Descriptor::kAwaited);
|
||||
Node* const outer_promise = Parameter(Descriptor::kOuterPromise);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
static const bool kIsPredictedAsCaught = false;
|
||||
|
||||
AsyncFunctionAwaitOptimized(context, generator, awaited, outer_promise,
|
||||
kIsPredictedAsCaught);
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncFunctionPromiseCreate, AsyncFunctionBuiltinsAssembler) {
|
||||
CSA_ASSERT_JS_ARGC_EQ(this, 0);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
@ -147,6 +147,120 @@ Node* AsyncBuiltinsAssembler::Await(
|
||||
on_resolve, on_reject, throwaway);
|
||||
}
|
||||
|
||||
Node* AsyncBuiltinsAssembler::AwaitOptimized(
|
||||
Node* context, Node* generator, Node* value, Node* outer_promise,
|
||||
int context_length, const ContextInitializer& init_closure_context,
|
||||
Node* on_resolve_context_index, Node* on_reject_context_index,
|
||||
Node* is_predicted_as_caught) {
|
||||
DCHECK_GE(context_length, Context::MIN_CONTEXT_SLOTS);
|
||||
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
Node* const promise_fun =
|
||||
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
|
||||
CSA_ASSERT(this, IsFunctionWithPrototypeSlotMap(LoadMap(promise_fun)));
|
||||
CSA_ASSERT(this, IsConstructor(promise_fun));
|
||||
|
||||
static const int kThrowawayPromiseOffset =
|
||||
FixedArray::SizeFor(context_length);
|
||||
static const int kResolveClosureOffset =
|
||||
kThrowawayPromiseOffset + JSPromise::kSizeWithEmbedderFields;
|
||||
static const int kRejectClosureOffset =
|
||||
kResolveClosureOffset + JSFunction::kSizeWithoutPrototype;
|
||||
static const int kTotalSize =
|
||||
kRejectClosureOffset + JSFunction::kSizeWithoutPrototype;
|
||||
|
||||
// 2. Let promise be ? PromiseResolve(« promise »).
|
||||
Node* const promise =
|
||||
CallBuiltin(Builtins::kPromiseResolve, context, promise_fun, value);
|
||||
|
||||
Node* const base = AllocateInNewSpace(kTotalSize);
|
||||
Node* const closure_context = base;
|
||||
{
|
||||
// Initialize closure context
|
||||
InitializeFunctionContext(native_context, closure_context, context_length);
|
||||
init_closure_context(closure_context);
|
||||
}
|
||||
|
||||
Node* const promise_map =
|
||||
LoadObjectField(promise_fun, JSFunction::kPrototypeOrInitialMapOffset);
|
||||
// Assert that the JSPromise map has an instance size is
|
||||
// JSPromise::kSizeWithEmbedderFields.
|
||||
CSA_ASSERT(this, WordEqual(LoadMapInstanceSizeInWords(promise_map),
|
||||
IntPtrConstant(JSPromise::kSizeWithEmbedderFields /
|
||||
kPointerSize)));
|
||||
Node* const throwaway = InnerAllocate(base, kThrowawayPromiseOffset);
|
||||
{
|
||||
// Initialize throwawayPromise
|
||||
StoreMapNoWriteBarrier(throwaway, promise_map);
|
||||
InitializeJSObjectFromMap(
|
||||
throwaway, promise_map,
|
||||
IntPtrConstant(JSPromise::kSizeWithEmbedderFields));
|
||||
PromiseInit(throwaway);
|
||||
}
|
||||
|
||||
Node* const on_resolve = InnerAllocate(base, kResolveClosureOffset);
|
||||
{
|
||||
// Initialize resolve handler
|
||||
InitializeNativeClosure(closure_context, native_context, on_resolve,
|
||||
on_resolve_context_index);
|
||||
}
|
||||
|
||||
Node* const on_reject = InnerAllocate(base, kRejectClosureOffset);
|
||||
{
|
||||
// Initialize reject handler
|
||||
InitializeNativeClosure(closure_context, native_context, on_reject,
|
||||
on_reject_context_index);
|
||||
}
|
||||
|
||||
{
|
||||
// Add PromiseHooks if needed
|
||||
Label next(this);
|
||||
GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
|
||||
CallRuntime(Runtime::kAwaitPromisesInit, context, promise, outer_promise,
|
||||
throwaway);
|
||||
Goto(&next);
|
||||
BIND(&next);
|
||||
}
|
||||
|
||||
// The Promise will be thrown away and not handled, but it shouldn't trigger
|
||||
// unhandled reject events as its work is done
|
||||
PromiseSetHasHandler(throwaway);
|
||||
|
||||
Label do_perform_promise_then(this);
|
||||
GotoIfNot(IsDebugActive(), &do_perform_promise_then);
|
||||
{
|
||||
Label common(this);
|
||||
GotoIf(TaggedIsSmi(value), &common);
|
||||
GotoIfNot(HasInstanceType(value, JS_PROMISE_TYPE), &common);
|
||||
{
|
||||
// Mark the reject handler callback to be a forwarding edge, rather
|
||||
// than a meaningful catch handler
|
||||
Node* const key =
|
||||
HeapConstant(factory()->promise_forwarding_handler_symbol());
|
||||
CallRuntime(Runtime::kSetProperty, context, on_reject, key,
|
||||
TrueConstant(), SmiConstant(LanguageMode::kStrict));
|
||||
|
||||
GotoIf(IsFalse(is_predicted_as_caught), &common);
|
||||
PromiseSetHandledHint(value);
|
||||
}
|
||||
|
||||
Goto(&common);
|
||||
BIND(&common);
|
||||
// Mark the dependency to outer Promise in case the throwaway Promise is
|
||||
// found on the Promise stack
|
||||
CSA_SLOW_ASSERT(this, HasInstanceType(outer_promise, JS_PROMISE_TYPE));
|
||||
|
||||
Node* const key = HeapConstant(factory()->promise_handled_by_symbol());
|
||||
CallRuntime(Runtime::kSetProperty, context, throwaway, key, outer_promise,
|
||||
SmiConstant(LanguageMode::kStrict));
|
||||
}
|
||||
|
||||
Goto(&do_perform_promise_then);
|
||||
BIND(&do_perform_promise_then);
|
||||
return CallBuiltin(Builtins::kPerformPromiseThen, native_context, promise,
|
||||
on_resolve, on_reject, throwaway);
|
||||
}
|
||||
|
||||
void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
|
||||
Node* native_context,
|
||||
Node* function,
|
||||
|
@ -28,6 +28,12 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
|
||||
const ContextInitializer& init_closure_context,
|
||||
Node* on_resolve_context_index, Node* on_reject_context_index,
|
||||
Node* is_predicted_as_caught);
|
||||
Node* AwaitOptimized(Node* context, Node* generator, Node* value,
|
||||
Node* outer_promise, int context_length,
|
||||
const ContextInitializer& init_closure_context,
|
||||
Node* on_resolve_context_index,
|
||||
Node* on_reject_context_index,
|
||||
Node* is_predicted_as_caught);
|
||||
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
|
||||
int context_length,
|
||||
const ContextInitializer& init_closure_context,
|
||||
@ -38,6 +44,17 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
|
||||
IntPtrConstant(on_reject_context_index),
|
||||
is_predicted_as_caught);
|
||||
}
|
||||
Node* AwaitOptimized(Node* context, Node* generator, Node* value,
|
||||
Node* outer_promise, int context_length,
|
||||
const ContextInitializer& init_closure_context,
|
||||
int on_resolve_context_index,
|
||||
int on_reject_context_index,
|
||||
Node* is_predicted_as_caught) {
|
||||
return AwaitOptimized(
|
||||
context, generator, value, outer_promise, context_length,
|
||||
init_closure_context, IntPtrConstant(on_resolve_context_index),
|
||||
IntPtrConstant(on_reject_context_index), is_predicted_as_caught);
|
||||
}
|
||||
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
|
||||
int context_length,
|
||||
const ContextInitializer& init_closure_context,
|
||||
@ -48,6 +65,17 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
|
||||
on_reject_context_index,
|
||||
BooleanConstant(is_predicted_as_caught));
|
||||
}
|
||||
Node* AwaitOptimized(Node* context, Node* generator, Node* value,
|
||||
Node* outer_promise, int context_length,
|
||||
const ContextInitializer& init_closure_context,
|
||||
int on_resolve_context_index,
|
||||
int on_reject_context_index,
|
||||
bool is_predicted_as_caught) {
|
||||
return AwaitOptimized(context, generator, value, outer_promise,
|
||||
context_length, init_closure_context,
|
||||
on_resolve_context_index, on_reject_context_index,
|
||||
BooleanConstant(is_predicted_as_caught));
|
||||
}
|
||||
|
||||
// Return a new built-in function object as defined in
|
||||
// Async Iterator Value Unwrap Functions
|
||||
|
@ -36,6 +36,13 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
|
||||
const char* operation_name,
|
||||
Label::Type reject_label_type = Label::kDeferred,
|
||||
Node* const initial_exception_value = nullptr);
|
||||
void Generate_AsyncFromSyncIteratorMethodOptimized(
|
||||
Node* const context, Node* const iterator, Node* const sent_value,
|
||||
const SyncIteratorNodeGenerator& get_method,
|
||||
const UndefinedMethodHandler& if_method_undefined,
|
||||
const char* operation_name,
|
||||
Label::Type reject_label_type = Label::kDeferred,
|
||||
Node* const initial_exception_value = nullptr);
|
||||
|
||||
void Generate_AsyncFromSyncIteratorMethod(
|
||||
Node* const context, Node* const iterator, Node* const sent_value,
|
||||
@ -50,6 +57,19 @@ class AsyncFromSyncBuiltinsAssembler : public AsyncBuiltinsAssembler {
|
||||
context, iterator, sent_value, get_method, if_method_undefined,
|
||||
operation_name, reject_label_type, initial_exception_value);
|
||||
}
|
||||
void Generate_AsyncFromSyncIteratorMethodOptimized(
|
||||
Node* const context, Node* const iterator, Node* const sent_value,
|
||||
Handle<String> name, const UndefinedMethodHandler& if_method_undefined,
|
||||
const char* operation_name,
|
||||
Label::Type reject_label_type = Label::kDeferred,
|
||||
Node* const initial_exception_value = nullptr) {
|
||||
auto get_method = [=](Node* const sync_iterator) {
|
||||
return GetProperty(context, sync_iterator, name);
|
||||
};
|
||||
return Generate_AsyncFromSyncIteratorMethodOptimized(
|
||||
context, iterator, sent_value, get_method, if_method_undefined,
|
||||
operation_name, reject_label_type, initial_exception_value);
|
||||
}
|
||||
|
||||
// Load "value" and "done" from an iterator result object. If an exception
|
||||
// is thrown at any point, jumps to te `if_exception` label with exception
|
||||
@ -157,6 +177,73 @@ void AsyncFromSyncBuiltinsAssembler::Generate_AsyncFromSyncIteratorMethod(
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncFromSyncBuiltinsAssembler::
|
||||
Generate_AsyncFromSyncIteratorMethodOptimized(
|
||||
Node* const context, Node* const iterator, Node* const sent_value,
|
||||
const SyncIteratorNodeGenerator& get_method,
|
||||
const UndefinedMethodHandler& if_method_undefined,
|
||||
const char* operation_name, Label::Type reject_label_type,
|
||||
Node* const initial_exception_value) {
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
Node* const promise = AllocateAndInitJSPromise(context);
|
||||
|
||||
VARIABLE(var_exception, MachineRepresentation::kTagged,
|
||||
initial_exception_value == nullptr ? UndefinedConstant()
|
||||
: initial_exception_value);
|
||||
Label reject_promise(this, reject_label_type);
|
||||
|
||||
ThrowIfNotAsyncFromSyncIterator(context, iterator, &reject_promise,
|
||||
&var_exception, operation_name);
|
||||
|
||||
Node* const sync_iterator =
|
||||
LoadObjectField(iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset);
|
||||
|
||||
Node* const method = get_method(sync_iterator);
|
||||
|
||||
if (if_method_undefined) {
|
||||
Label if_isnotundefined(this);
|
||||
|
||||
GotoIfNot(IsUndefined(method), &if_isnotundefined);
|
||||
if_method_undefined(native_context, promise, &reject_promise);
|
||||
|
||||
BIND(&if_isnotundefined);
|
||||
}
|
||||
|
||||
Node* const iter_result = CallJS(CodeFactory::Call(isolate()), context,
|
||||
method, sync_iterator, sent_value);
|
||||
GotoIfException(iter_result, &reject_promise, &var_exception);
|
||||
|
||||
Node* value;
|
||||
Node* done;
|
||||
std::tie(value, done) = LoadIteratorResult(
|
||||
context, native_context, iter_result, &reject_promise, &var_exception);
|
||||
|
||||
Node* const promise_fun =
|
||||
LoadContextElement(native_context, Context::PROMISE_FUNCTION_INDEX);
|
||||
CSA_ASSERT(this, IsConstructor(promise_fun));
|
||||
|
||||
// Let valueWrapper be ? PromiseResolve(« value »).
|
||||
Node* const valueWrapper = CallBuiltin(Builtins::kPromiseResolve,
|
||||
native_context, promise_fun, value);
|
||||
|
||||
// Let onFulfilled be a new built-in function object as defined in
|
||||
// Async Iterator Value Unwrap Functions.
|
||||
// Set onFulfilled.[[Done]] to throwDone.
|
||||
Node* const on_fulfilled = CreateUnwrapClosure(native_context, done);
|
||||
|
||||
// Perform ! PerformPromiseThen(valueWrapper,
|
||||
// onFulfilled, undefined, promiseCapability).
|
||||
Return(CallBuiltin(Builtins::kPerformPromiseThen, context, valueWrapper,
|
||||
on_fulfilled, UndefinedConstant(), promise));
|
||||
|
||||
BIND(&reject_promise);
|
||||
{
|
||||
Node* const exception = var_exception.value();
|
||||
CallBuiltin(Builtins::kRejectPromise, context, promise, exception,
|
||||
TrueConstant());
|
||||
Return(promise);
|
||||
}
|
||||
}
|
||||
std::pair<Node*, Node*> AsyncFromSyncBuiltinsAssembler::LoadIteratorResult(
|
||||
Node* const context, Node* const native_context, Node* const iter_result,
|
||||
Label* if_exception, Variable* var_exception) {
|
||||
@ -246,6 +333,20 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeNext, AsyncFromSyncBuiltinsAssembler) {
|
||||
"[Async-from-Sync Iterator].prototype.next");
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncFromSyncIteratorPrototypeNextOptimized,
|
||||
AsyncFromSyncBuiltinsAssembler) {
|
||||
Node* const iterator = Parameter(Descriptor::kReceiver);
|
||||
Node* const value = Parameter(Descriptor::kValue);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
auto get_method = [=](Node* const unused) {
|
||||
return LoadObjectField(iterator, JSAsyncFromSyncIterator::kNextOffset);
|
||||
};
|
||||
Generate_AsyncFromSyncIteratorMethodOptimized(
|
||||
context, iterator, value, get_method, UndefinedMethodHandler(),
|
||||
"[Async-from-Sync Iterator].prototype.next");
|
||||
}
|
||||
|
||||
// https://tc39.github.io/proposal-async-iteration/
|
||||
// Section #sec-%asyncfromsynciteratorprototype%.return
|
||||
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
|
||||
@ -273,6 +374,31 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturn,
|
||||
"[Async-from-Sync Iterator].prototype.return");
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncFromSyncIteratorPrototypeReturnOptimized,
|
||||
AsyncFromSyncBuiltinsAssembler) {
|
||||
Node* const iterator = Parameter(Descriptor::kReceiver);
|
||||
Node* const value = Parameter(Descriptor::kValue);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
auto if_return_undefined = [=](Node* const native_context,
|
||||
Node* const promise, Label* if_exception) {
|
||||
// If return is undefined, then
|
||||
// Let iterResult be ! CreateIterResultObject(value, true)
|
||||
Node* const iter_result = CallBuiltin(Builtins::kCreateIterResultObject,
|
||||
context, value, TrueConstant());
|
||||
|
||||
// Perform ! Call(promiseCapability.[[Resolve]], undefined, « iterResult »).
|
||||
// IfAbruptRejectPromise(nextDone, promiseCapability).
|
||||
// Return promiseCapability.[[Promise]].
|
||||
CallBuiltin(Builtins::kResolvePromise, context, promise, iter_result);
|
||||
Return(promise);
|
||||
};
|
||||
|
||||
Generate_AsyncFromSyncIteratorMethodOptimized(
|
||||
context, iterator, value, factory()->return_string(), if_return_undefined,
|
||||
"[Async-from-Sync Iterator].prototype.return");
|
||||
}
|
||||
|
||||
// https://tc39.github.io/proposal-async-iteration/
|
||||
// Section #sec-%asyncfromsynciteratorprototype%.throw
|
||||
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
|
||||
@ -290,5 +416,20 @@ TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrow,
|
||||
reason);
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncFromSyncIteratorPrototypeThrowOptimized,
|
||||
AsyncFromSyncBuiltinsAssembler) {
|
||||
Node* const iterator = Parameter(Descriptor::kReceiver);
|
||||
Node* const reason = Parameter(Descriptor::kReason);
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
|
||||
auto if_throw_undefined = [=](Node* const native_context, Node* const promise,
|
||||
Label* if_exception) { Goto(if_exception); };
|
||||
|
||||
Generate_AsyncFromSyncIteratorMethodOptimized(
|
||||
context, iterator, reason, factory()->throw_string(), if_throw_undefined,
|
||||
"[Async-from-Sync Iterator].prototype.throw", Label::kNonDeferred,
|
||||
reason);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -420,8 +420,12 @@ namespace internal {
|
||||
/* AsyncFunction */ \
|
||||
TFJ(AsyncFunctionAwaitCaught, 3, kReceiver, kGenerator, kAwaited, \
|
||||
kOuterPromise) \
|
||||
TFJ(AsyncFunctionAwaitCaughtOptimized, 3, kReceiver, kGenerator, kAwaited, \
|
||||
kOuterPromise) \
|
||||
TFJ(AsyncFunctionAwaitUncaught, 3, kReceiver, kGenerator, kAwaited, \
|
||||
kOuterPromise) \
|
||||
TFJ(AsyncFunctionAwaitUncaughtOptimized, 3, kReceiver, kGenerator, kAwaited, \
|
||||
kOuterPromise) \
|
||||
TFJ(AsyncFunctionAwaitRejectClosure, 1, kReceiver, kSentError) \
|
||||
TFJ(AsyncFunctionAwaitResolveClosure, 1, kReceiver, kSentValue) \
|
||||
TFJ(AsyncFunctionPromiseCreate, 0, kReceiver) \
|
||||
@ -1271,10 +1275,13 @@ namespace internal {
|
||||
/* See tc39.github.io/proposal-async-iteration/ */ \
|
||||
/* #sec-%asyncfromsynciteratorprototype%-object) */ \
|
||||
TFJ(AsyncFromSyncIteratorPrototypeNext, 1, kReceiver, kValue) \
|
||||
TFJ(AsyncFromSyncIteratorPrototypeNextOptimized, 1, kReceiver, kValue) \
|
||||
/* #sec-%asyncfromsynciteratorprototype%.throw */ \
|
||||
TFJ(AsyncFromSyncIteratorPrototypeThrow, 1, kReceiver, kReason) \
|
||||
TFJ(AsyncFromSyncIteratorPrototypeThrowOptimized, 1, kReceiver, kReason) \
|
||||
/* #sec-%asyncfromsynciteratorprototype%.return */ \
|
||||
TFJ(AsyncFromSyncIteratorPrototypeReturn, 1, kReceiver, kValue) \
|
||||
TFJ(AsyncFromSyncIteratorPrototypeReturnOptimized, 1, kReceiver, kValue) \
|
||||
/* #sec-async-iterator-value-unwrap-functions */ \
|
||||
TFJ(AsyncIteratorValueUnwrap, 1, kReceiver, kValue) \
|
||||
\
|
||||
@ -1355,9 +1362,14 @@ namespace internal {
|
||||
#define BUILTIN_PROMISE_REJECTION_PREDICTION_LIST(V) \
|
||||
V(AsyncFromSyncIteratorPrototypeNext) \
|
||||
V(AsyncFromSyncIteratorPrototypeReturn) \
|
||||
V(AsyncFromSyncIteratorPrototypeNextOptimized) \
|
||||
V(AsyncFromSyncIteratorPrototypeThrowOptimized) \
|
||||
V(AsyncFromSyncIteratorPrototypeReturnOptimized) \
|
||||
V(AsyncFromSyncIteratorPrototypeThrow) \
|
||||
V(AsyncFunctionAwaitCaught) \
|
||||
V(AsyncFunctionAwaitCaughtOptimized) \
|
||||
V(AsyncFunctionAwaitUncaught) \
|
||||
V(AsyncFunctionAwaitUncaughtOptimized) \
|
||||
V(AsyncGeneratorResolve) \
|
||||
V(AsyncGeneratorAwaitCaught) \
|
||||
V(AsyncGeneratorAwaitUncaught) \
|
||||
|
@ -208,10 +208,11 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_private_fields)
|
||||
// Update bootstrapper.cc whenever adding a new feature flag.
|
||||
|
||||
// Features that are still work in progress (behind individual flags).
|
||||
#define HARMONY_INPROGRESS_BASE(V) \
|
||||
V(harmony_do_expressions, "harmony do-expressions") \
|
||||
V(harmony_class_fields, "harmony fields in class literals") \
|
||||
V(harmony_static_fields, "harmony static fields in class literals")
|
||||
#define HARMONY_INPROGRESS_BASE(V) \
|
||||
V(harmony_do_expressions, "harmony do-expressions") \
|
||||
V(harmony_class_fields, "harmony fields in class literals") \
|
||||
V(harmony_static_fields, "harmony static fields in class literals") \
|
||||
V(harmony_await_optimization, "harmony await taking 1 tick")
|
||||
|
||||
#ifdef V8_INTL_SUPPORT
|
||||
#define HARMONY_INPROGRESS(V) \
|
||||
|
@ -207,4 +207,3 @@ floodWithTimeouts (testStepIntoAtReturn.js:137:10)
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:137:10)
|
||||
test (testStepIntoAtReturn.js:142:8)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
|
@ -0,0 +1,215 @@
|
||||
stepOut async function
|
||||
|
||||
Running test: testTrivial
|
||||
Check that we have proper async stack at return
|
||||
bar (testTrivial.js:30:8)
|
||||
-- async function --
|
||||
bar (testTrivial.js:29:22)
|
||||
foo (testTrivial.js:25:14)
|
||||
-- async function --
|
||||
foo (testTrivial.js:24:22)
|
||||
test (testTrivial.js:20:14)
|
||||
-- async function --
|
||||
test (testTrivial.js:19:22)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
foo (testTrivial.js:26:6)
|
||||
-- async function --
|
||||
foo (testTrivial.js:24:22)
|
||||
test (testTrivial.js:20:14)
|
||||
-- async function --
|
||||
test (testTrivial.js:19:22)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
test (testTrivial.js:21:6)
|
||||
-- async function --
|
||||
test (testTrivial.js:19:22)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
|
||||
Running test: testStepOutPrecision
|
||||
Check that stepOut go to resumed outer generator
|
||||
bar (testStepOutPrecision.js:63:8)
|
||||
-- async function --
|
||||
bar (testStepOutPrecision.js:62:22)
|
||||
foo (testStepOutPrecision.js:57:14)
|
||||
-- async function --
|
||||
foo (testStepOutPrecision.js:56:22)
|
||||
test (testStepOutPrecision.js:50:14)
|
||||
-- async function --
|
||||
test (testStepOutPrecision.js:49:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
foo (testStepOutPrecision.js:58:8)
|
||||
-- async function --
|
||||
foo (testStepOutPrecision.js:56:22)
|
||||
test (testStepOutPrecision.js:50:14)
|
||||
-- async function --
|
||||
test (testStepOutPrecision.js:49:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
test (testStepOutPrecision.js:51:8)
|
||||
-- async function --
|
||||
test (testStepOutPrecision.js:49:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
floodWithTimeouts (testStepOutPrecision.js:42:15)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
test (testStepOutPrecision.js:48:8)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
test (testStepOutPrecision.js:52:8)
|
||||
-- async function --
|
||||
test (testStepOutPrecision.js:49:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
floodWithTimeouts (testStepOutPrecision.js:42:15)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepOutPrecision.js:43:10)
|
||||
test (testStepOutPrecision.js:48:8)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
|
||||
Running test: testStepIntoAtReturn
|
||||
Check that stepInto at return go to resumed outer generator
|
||||
bar (testStepIntoAtReturn.js:95:8)
|
||||
-- async function --
|
||||
bar (testStepIntoAtReturn.js:94:22)
|
||||
foo (testStepIntoAtReturn.js:90:14)
|
||||
-- async function --
|
||||
foo (testStepIntoAtReturn.js:89:22)
|
||||
test (testStepIntoAtReturn.js:84:14)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:83:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
bar (testStepIntoAtReturn.js:96:6)
|
||||
-- async function --
|
||||
bar (testStepIntoAtReturn.js:94:22)
|
||||
foo (testStepIntoAtReturn.js:90:14)
|
||||
-- async function --
|
||||
foo (testStepIntoAtReturn.js:89:22)
|
||||
test (testStepIntoAtReturn.js:84:14)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:83:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
foo (testStepIntoAtReturn.js:91:6)
|
||||
-- async function --
|
||||
foo (testStepIntoAtReturn.js:89:22)
|
||||
test (testStepIntoAtReturn.js:84:14)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:83:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
test (testStepIntoAtReturn.js:85:8)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:83:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
test (testStepIntoAtReturn.js:86:6)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:83:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:76:15)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:77:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:77:10)
|
||||
test (testStepIntoAtReturn.js:82:8)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
|
||||
Running test: testStepOverAtReturn
|
||||
Check that stepOver at return go to resumed outer generator
|
||||
bar (testStepIntoAtReturn.js:126:8)
|
||||
-- async function --
|
||||
bar (testStepIntoAtReturn.js:125:22)
|
||||
foo (testStepIntoAtReturn.js:121:14)
|
||||
-- async function --
|
||||
foo (testStepIntoAtReturn.js:120:22)
|
||||
test (testStepIntoAtReturn.js:115:14)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:114:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
bar (testStepIntoAtReturn.js:127:6)
|
||||
-- async function --
|
||||
bar (testStepIntoAtReturn.js:125:22)
|
||||
foo (testStepIntoAtReturn.js:121:14)
|
||||
-- async function --
|
||||
foo (testStepIntoAtReturn.js:120:22)
|
||||
test (testStepIntoAtReturn.js:115:14)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:114:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
foo (testStepIntoAtReturn.js:122:6)
|
||||
-- async function --
|
||||
foo (testStepIntoAtReturn.js:120:22)
|
||||
test (testStepIntoAtReturn.js:115:14)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:114:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
test (testStepIntoAtReturn.js:116:8)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:114:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
test (testStepIntoAtReturn.js:117:6)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:114:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:107:15)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:108:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:108:10)
|
||||
test (testStepIntoAtReturn.js:113:8)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
|
||||
Running test: testStepOutFromNotAwaitedCall
|
||||
Checks stepOut from not awaited call
|
||||
bar (testStepIntoAtReturn.js:160:8)
|
||||
-- async function --
|
||||
bar (testStepIntoAtReturn.js:159:22)
|
||||
foo (testStepIntoAtReturn.js:154:8)
|
||||
-- async function --
|
||||
foo (testStepIntoAtReturn.js:153:22)
|
||||
test (testStepIntoAtReturn.js:146:14)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:145:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
test (testStepIntoAtReturn.js:147:8)
|
||||
-- async function --
|
||||
test (testStepIntoAtReturn.js:145:14)
|
||||
(anonymous) (:0:0)
|
||||
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:138:15)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:139:10)
|
||||
-- setTimeout --
|
||||
floodWithTimeouts (testStepIntoAtReturn.js:139:10)
|
||||
test (testStepIntoAtReturn.js:144:8)
|
||||
(anonymous) (:0:0)
|
||||
|
187
test/inspector/debugger/async-function-step-out-optimized.js
Normal file
187
test/inspector/debugger/async-function-step-out-optimized.js
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright 2018 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-await-optimization
|
||||
|
||||
let {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('stepOut async function');
|
||||
|
||||
session.setupScriptMap();
|
||||
|
||||
Protocol.Runtime.enable();
|
||||
|
||||
InspectorTest.runAsyncTestSuite([
|
||||
async function testTrivial() {
|
||||
InspectorTest.log('Check that we have proper async stack at return');
|
||||
contextGroup.addInlineScript(`
|
||||
async function test() {
|
||||
await Promise.resolve();
|
||||
await foo();
|
||||
}
|
||||
|
||||
async function foo() {
|
||||
await Promise.resolve();
|
||||
await bar();
|
||||
}
|
||||
|
||||
async function bar() {
|
||||
await Promise.resolve();
|
||||
debugger;
|
||||
}`, 'testTrivial.js');
|
||||
await runTestAndStepAction('stepOut');
|
||||
},
|
||||
|
||||
async function testStepOutPrecision() {
|
||||
InspectorTest.log('Check that stepOut go to resumed outer generator');
|
||||
contextGroup.addInlineScript(`
|
||||
function wait() {
|
||||
return new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
function floodWithTimeouts(a) {
|
||||
if (!a.stop)
|
||||
setTimeout(floodWithTimeouts.bind(this, a), 0);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
let a = {};
|
||||
floodWithTimeouts(a)
|
||||
await wait();
|
||||
await foo();
|
||||
await wait();
|
||||
a.stop = true;
|
||||
}
|
||||
|
||||
async function foo() {
|
||||
await Promise.resolve();
|
||||
await bar();
|
||||
await wait();
|
||||
}
|
||||
|
||||
async function bar() {
|
||||
await Promise.resolve();
|
||||
debugger;
|
||||
await wait();
|
||||
}`, 'testStepOutPrecision.js');
|
||||
await runTestAndStepAction('stepOut');
|
||||
},
|
||||
|
||||
async function testStepIntoAtReturn() {
|
||||
InspectorTest.log('Check that stepInto at return go to resumed outer generator');
|
||||
contextGroup.addInlineScript(`
|
||||
function wait() {
|
||||
return new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
function floodWithTimeouts(a) {
|
||||
if (!a.stop)
|
||||
setTimeout(floodWithTimeouts.bind(this, a), 0);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
let a = {};
|
||||
floodWithTimeouts(a)
|
||||
await wait();
|
||||
await foo();
|
||||
a.stop = true;
|
||||
}
|
||||
|
||||
async function foo() {
|
||||
await Promise.resolve();
|
||||
await bar();
|
||||
}
|
||||
|
||||
async function bar() {
|
||||
await Promise.resolve();
|
||||
debugger;
|
||||
}`, 'testStepIntoAtReturn.js');
|
||||
await runTestAndStepAction('stepInto');
|
||||
},
|
||||
|
||||
async function testStepOverAtReturn() {
|
||||
InspectorTest.log('Check that stepOver at return go to resumed outer generator');
|
||||
contextGroup.addInlineScript(`
|
||||
function wait() {
|
||||
return new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
function floodWithTimeouts(a) {
|
||||
if (!a.stop)
|
||||
setTimeout(floodWithTimeouts.bind(this, a), 0);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
let a = {};
|
||||
floodWithTimeouts(a)
|
||||
await wait();
|
||||
await foo();
|
||||
a.stop = true;
|
||||
}
|
||||
|
||||
async function foo() {
|
||||
await Promise.resolve();
|
||||
await bar();
|
||||
}
|
||||
|
||||
async function bar() {
|
||||
await Promise.resolve();
|
||||
debugger;
|
||||
}`, 'testStepIntoAtReturn.js');
|
||||
await runTestAndStepAction('stepOver');
|
||||
},
|
||||
|
||||
async function testStepOutFromNotAwaitedCall() {
|
||||
InspectorTest.log('Checks stepOut from not awaited call');
|
||||
contextGroup.addInlineScript(`
|
||||
function wait() {
|
||||
return new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
function floodWithTimeouts(a) {
|
||||
if (!a.stop)
|
||||
setTimeout(floodWithTimeouts.bind(this, a), 0);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
let a = {};
|
||||
floodWithTimeouts(a)
|
||||
await wait();
|
||||
await foo();
|
||||
a.stop = true;
|
||||
}
|
||||
|
||||
async function foo() {
|
||||
let a = {};
|
||||
floodWithTimeouts(a);
|
||||
await Promise.resolve();
|
||||
bar();
|
||||
a.stop = true;
|
||||
}
|
||||
|
||||
async function bar() {
|
||||
await Promise.resolve();
|
||||
debugger;
|
||||
}`, 'testStepIntoAtReturn.js');
|
||||
await runTestAndStepAction('stepOut');
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
async function runTestAndStepAction(action) {
|
||||
Protocol.Debugger.enable();
|
||||
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
|
||||
let finished =
|
||||
Protocol.Runtime.evaluate({expression: 'test()', awaitPromise: true})
|
||||
.then(() => false);
|
||||
while (true) {
|
||||
const r = await Promise.race([finished, waitPauseAndDumpStack()]);
|
||||
if (!r) break;
|
||||
Protocol.Debugger[action]();
|
||||
}
|
||||
await Protocol.Debugger.disable();
|
||||
}
|
||||
|
||||
async function waitPauseAndDumpStack() {
|
||||
const {params} = await Protocol.Debugger.oncePaused();
|
||||
session.logCallFrames(params.callFrames);
|
||||
session.logAsyncStackTrace(params.asyncStackTrace);
|
||||
InspectorTest.log('');
|
||||
return true;
|
||||
}
|
@ -0,0 +1,200 @@
|
||||
Tests breakable locations in variable initializations.
|
||||
|
||||
Running test: testBreakLocations
|
||||
|
||||
Running test: testStepInto
|
||||
|
||||
function testFunction() {
|
||||
var obj1 = |_|{a : 1};
|
||||
var arr1 = |_|[1];
|
||||
var promise = |_|Promise.|C|resolve(1).|C|then(x => x |_|* 2|R|).|C|then(x => x |_|/ 2|R|);
|
||||
|_|Promise.|C|resolve(1).|C|then(x => x |_|* 2|R|).|C|then(x => x |_|/ 2|R|);
|
||||
|_|promise = Promise.|C|resolve(1).|C|then(x => x |_|* 2|R|).|C|then(x => x |_|/ 2|R|);
|
||||
var a = |_|1;
|
||||
const x = |_|(a = 20);
|
||||
var y = |_|(a = 100);
|
||||
var z = |_|x + (a = 1) + (a = 2) + (a = 3) + |C|f();
|
||||
function f() {
|
||||
for (let { x, y } = |_|{ x: 0, y: 1 }; y |_|> 0; --|_|y) { let z = |_|x + y; }
|
||||
|R|}
|
||||
var b = obj1.|_|a;
|
||||
|_|(async function asyncF() {
|
||||
let r = |_|await Promise.|C|resolve(42);
|
||||
|_|return r;
|
||||
|R|})|C|();
|
||||
|_|return promise;|R|
|
||||
}
|
||||
|
||||
(anonymous) (expr.js:0:0)
|
||||
|
||||
|
||||
testFunction (test.js:2:13)
|
||||
(anonymous) (expr.js:0:0)
|
||||
function testFunction() {
|
||||
var obj1 = #{a : 1};
|
||||
var arr1 = [1];
|
||||
|
||||
testFunction (test.js:3:13)
|
||||
(anonymous) (expr.js:0:0)
|
||||
var obj1 = {a : 1};
|
||||
var arr1 = #[1];
|
||||
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
|
||||
testFunction (test.js:4:16)
|
||||
(anonymous) (expr.js:0:0)
|
||||
var arr1 = [1];
|
||||
var promise = #Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
|
||||
testFunction (test.js:5:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
#Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
|
||||
testFunction (test.js:6:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
#promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
var a = 1;
|
||||
|
||||
testFunction (test.js:7:10)
|
||||
(anonymous) (expr.js:0:0)
|
||||
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
var a = #1;
|
||||
const x = (a = 20);
|
||||
|
||||
testFunction (test.js:8:12)
|
||||
(anonymous) (expr.js:0:0)
|
||||
var a = 1;
|
||||
const x = #(a = 20);
|
||||
var y = (a = 100);
|
||||
|
||||
testFunction (test.js:9:10)
|
||||
(anonymous) (expr.js:0:0)
|
||||
const x = (a = 20);
|
||||
var y = #(a = 100);
|
||||
var z = x + (a = 1) + (a = 2) + (a = 3) + f();
|
||||
|
||||
testFunction (test.js:10:10)
|
||||
(anonymous) (expr.js:0:0)
|
||||
var y = (a = 100);
|
||||
var z = #x + (a = 1) + (a = 2) + (a = 3) + f();
|
||||
function f() {
|
||||
|
||||
f (test.js:12:24)
|
||||
testFunction (test.js:10:44)
|
||||
(anonymous) (expr.js:0:0)
|
||||
function f() {
|
||||
for (let { x, y } = #{ x: 0, y: 1 }; y > 0; --y) { let z = x + y; }
|
||||
}
|
||||
|
||||
f (test.js:12:42)
|
||||
testFunction (test.js:10:44)
|
||||
(anonymous) (expr.js:0:0)
|
||||
function f() {
|
||||
for (let { x, y } = { x: 0, y: 1 }; y #> 0; --y) { let z = x + y; }
|
||||
}
|
||||
|
||||
f (test.js:12:62)
|
||||
testFunction (test.js:10:44)
|
||||
(anonymous) (expr.js:0:0)
|
||||
function f() {
|
||||
for (let { x, y } = { x: 0, y: 1 }; y > 0; --y) { let z = #x + y; }
|
||||
}
|
||||
|
||||
f (test.js:12:49)
|
||||
testFunction (test.js:10:44)
|
||||
(anonymous) (expr.js:0:0)
|
||||
function f() {
|
||||
for (let { x, y } = { x: 0, y: 1 }; y > 0; --#y) { let z = x + y; }
|
||||
}
|
||||
|
||||
f (test.js:12:42)
|
||||
testFunction (test.js:10:44)
|
||||
(anonymous) (expr.js:0:0)
|
||||
function f() {
|
||||
for (let { x, y } = { x: 0, y: 1 }; y #> 0; --y) { let z = x + y; }
|
||||
}
|
||||
|
||||
f (test.js:13:2)
|
||||
testFunction (test.js:10:44)
|
||||
(anonymous) (expr.js:0:0)
|
||||
for (let { x, y } = { x: 0, y: 1 }; y > 0; --y) { let z = x + y; }
|
||||
#}
|
||||
var b = obj1.a;
|
||||
|
||||
testFunction (test.js:14:15)
|
||||
(anonymous) (expr.js:0:0)
|
||||
}
|
||||
var b = obj1.#a;
|
||||
(async function asyncF() {
|
||||
|
||||
testFunction (test.js:15:2)
|
||||
(anonymous) (expr.js:0:0)
|
||||
var b = obj1.a;
|
||||
#(async function asyncF() {
|
||||
let r = await Promise.resolve(42);
|
||||
|
||||
asyncF (test.js:16:12)
|
||||
testFunction (test.js:18:4)
|
||||
(anonymous) (expr.js:0:0)
|
||||
(async function asyncF() {
|
||||
let r = #await Promise.resolve(42);
|
||||
return r;
|
||||
|
||||
asyncF (test.js:17:4)
|
||||
let r = await Promise.resolve(42);
|
||||
#return r;
|
||||
})();
|
||||
|
||||
asyncF (test.js:18:2)
|
||||
return r;
|
||||
#})();
|
||||
return promise;
|
||||
|
||||
Promise.resolve.then.then.x (test.js:4:64)
|
||||
var arr1 = [1];
|
||||
var promise = Promise.resolve(1).then(x => x * 2).then(x => x #/ 2);
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
|
||||
Promise.resolve.then.then.x (test.js:4:67)
|
||||
var arr1 = [1];
|
||||
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2#);
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
|
||||
Promise.resolve.then.then.x (test.js:5:50)
|
||||
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x #/ 2);
|
||||
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
|
||||
Promise.resolve.then.then.x (test.js:5:53)
|
||||
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x / 2#);
|
||||
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
|
||||
Promise.resolve.then.then.x (test.js:6:60)
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
promise = Promise.resolve(1).then(x => x * 2).then(x => x #/ 2);
|
||||
var a = 1;
|
||||
|
||||
Promise.resolve.then.then.x (test.js:6:63)
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2#);
|
||||
var a = 1;
|
||||
|
||||
|
||||
Running test: testStepIntoAfterBreakpoint
|
||||
testFunction (test.js:10:10)
|
||||
(anonymous) (expr.js:0:0)
|
||||
var y = (a = 100);
|
||||
var z = #x + (a = 1) + (a = 2) + (a = 3) + f();
|
||||
function f() {
|
||||
|
||||
f (test.js:12:24)
|
||||
testFunction (test.js:10:44)
|
||||
(anonymous) (expr.js:0:0)
|
||||
function f() {
|
||||
for (let { x, y } = #{ x: 0, y: 1 }; y > 0; --y) { let z = x + y; }
|
||||
}
|
||||
|
@ -0,0 +1,76 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-await-optimization
|
||||
|
||||
let {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('Tests breakable locations in variable initializations.');
|
||||
|
||||
let source = `
|
||||
function testFunction() {
|
||||
var obj1 = {a : 1};
|
||||
var arr1 = [1];
|
||||
var promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
promise = Promise.resolve(1).then(x => x * 2).then(x => x / 2);
|
||||
var a = 1;
|
||||
const x = (a = 20);
|
||||
var y = (a = 100);
|
||||
var z = x + (a = 1) + (a = 2) + (a = 3) + f();
|
||||
function f() {
|
||||
for (let { x, y } = { x: 0, y: 1 }; y > 0; --y) { let z = x + y; }
|
||||
}
|
||||
var b = obj1.a;
|
||||
(async function asyncF() {
|
||||
let r = await Promise.resolve(42);
|
||||
return r;
|
||||
})();
|
||||
return promise;
|
||||
}
|
||||
//# sourceURL=test.js`;
|
||||
|
||||
contextGroup.addScript(source);
|
||||
session.setupScriptMap();
|
||||
|
||||
InspectorTest.runAsyncTestSuite([
|
||||
async function testBreakLocations() {
|
||||
Protocol.Debugger.enable();
|
||||
let {params:{scriptId}} = await Protocol.Debugger.onceScriptParsed();
|
||||
let {result:{locations}} = await Protocol.Debugger.getPossibleBreakpoints({
|
||||
start: {lineNumber: 0, columnNumber : 0, scriptId}});
|
||||
session.logBreakLocations(locations);
|
||||
},
|
||||
|
||||
async function testStepInto() {
|
||||
Protocol.Debugger.pause();
|
||||
let fin = Protocol.Runtime.evaluate({
|
||||
expression: 'testFunction()//# sourceURL=expr.js', awaitPromise: true}).then(() => false);
|
||||
let result;
|
||||
while (result = await Promise.race([fin, Protocol.Debugger.oncePaused()])) {
|
||||
let {params:{callFrames}} = result;
|
||||
session.logCallFrames(callFrames);
|
||||
session.logSourceLocation(callFrames[0].location);
|
||||
Protocol.Debugger.stepInto();
|
||||
}
|
||||
Protocol.Runtime.evaluate({expression: '42'});
|
||||
await Protocol.Debugger.oncePaused();
|
||||
await Protocol.Debugger.resume();
|
||||
},
|
||||
|
||||
async function testStepIntoAfterBreakpoint() {
|
||||
Protocol.Debugger.setBreakpointByUrl({lineNumber: 10, url: 'test.js'});
|
||||
Protocol.Runtime.evaluate({
|
||||
expression: 'testFunction()//# sourceURL=expr.js'});
|
||||
await awaitPausedAndDump();
|
||||
Protocol.Debugger.stepInto();
|
||||
await awaitPausedAndDump();
|
||||
await Protocol.Debugger.resume();
|
||||
|
||||
async function awaitPausedAndDump() {
|
||||
let {params:{callFrames}} = await Protocol.Debugger.oncePaused();
|
||||
session.logCallFrames(callFrames);
|
||||
session.logSourceLocation(callFrames[0].location);
|
||||
}
|
||||
}
|
||||
]);
|
@ -8,7 +8,7 @@ var ran = false;
|
||||
|
||||
async function test() {
|
||||
try {
|
||||
let namespace = await import('modules-skip-5.js');
|
||||
let namespace = await import('modules-skip-5.js').then(x => x);
|
||||
assertSame(namespace.static_life, namespace.dynamic_life);
|
||||
assertSame(namespace.relative_static_life, namespace.dynamic_life);
|
||||
ran = true;
|
||||
|
@ -15,13 +15,17 @@ async function f() {
|
||||
}
|
||||
f();
|
||||
|
||||
async function g() {
|
||||
async function importUndefined() {
|
||||
try {
|
||||
let namespace = await import({ get toString() { return undefined; }});
|
||||
await import({ get toString() { return undefined; }})
|
||||
} catch(e) {
|
||||
log(2);
|
||||
}
|
||||
}
|
||||
|
||||
function g() {
|
||||
let namespace = Promise.resolve().then(importUndefined);
|
||||
}
|
||||
g();
|
||||
%RunMicrotasks();
|
||||
assertEquals(list, [1,2]);
|
||||
|
Loading…
Reference in New Issue
Block a user