[async-await] Refactor await optimization and include async generators
Design doc: https://docs.google.com/document/d/1kL08cz4lR6gO5b2FATNK3QAfS8t-6K6kdk88U-n8tug/edit This CL is a follow-up after the original implementation, see CL: https://chromium-review.googlesource.com/c/v8/v8/+/1106977 It includes a fix for the missing async generators optimization, as well as cleanup of the manual patching of the builtins. It also includes mjsunit test for all usages of the new behaviour. Bug: v8:8267 Change-Id: I999f341acb746c6da5216e44b68a519656fd5403 Reviewed-on: https://chromium-review.googlesource.com/c/1261124 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Commit-Queue: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#56414}
This commit is contained in:
parent
3eceaf0349
commit
2a2c9e5f79
@ -4543,54 +4543,6 @@ void Genesis::InitializeGlobal_harmony_string_matchall() {
|
||||
}
|
||||
|
||||
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(isolate(), 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
|
||||
|
@ -21,10 +21,6 @@ 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,
|
||||
@ -126,38 +122,6 @@ 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));
|
||||
|
||||
// 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,
|
||||
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) {
|
||||
@ -173,19 +137,6 @@ 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) {
|
||||
@ -201,20 +152,6 @@ 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);
|
||||
|
@ -23,10 +23,11 @@ class ValueUnwrapContext {
|
||||
|
||||
} // namespace
|
||||
|
||||
Node* AsyncBuiltinsAssembler::Await(
|
||||
Node* context, Node* generator, Node* value, Node* outer_promise,
|
||||
Node* on_resolve_context_index, Node* on_reject_context_index,
|
||||
Node* is_predicted_as_caught) {
|
||||
Node* AsyncBuiltinsAssembler::AwaitOld(Node* context, Node* generator,
|
||||
Node* value, Node* outer_promise,
|
||||
Node* on_resolve_context_index,
|
||||
Node* on_reject_context_index,
|
||||
Node* is_predicted_as_caught) {
|
||||
Node* const native_context = LoadNativeContext(context);
|
||||
|
||||
static const int kWrappedPromiseOffset =
|
||||
@ -278,6 +279,37 @@ Node* AsyncBuiltinsAssembler::AwaitOptimized(
|
||||
on_resolve, on_reject, throwaway);
|
||||
}
|
||||
|
||||
Node* AsyncBuiltinsAssembler::Await(Node* context, Node* generator, Node* value,
|
||||
Node* outer_promise,
|
||||
Node* on_resolve_context_index,
|
||||
Node* on_reject_context_index,
|
||||
Node* is_predicted_as_caught) {
|
||||
VARIABLE(result, MachineRepresentation::kTagged);
|
||||
Label if_old(this), if_new(this), done(this);
|
||||
|
||||
TNode<Word32T> flag_value = UncheckedCast<Word32T>(Load(
|
||||
MachineType::Int32(),
|
||||
ExternalConstant(
|
||||
ExternalReference::address_of_harmony_await_optimization_flag())));
|
||||
|
||||
Branch(Word32Equal(flag_value, Int32Constant(0)), &if_old, &if_new);
|
||||
|
||||
BIND(&if_old);
|
||||
result.Bind(AwaitOld(context, generator, value, outer_promise,
|
||||
on_resolve_context_index, on_reject_context_index,
|
||||
is_predicted_as_caught));
|
||||
Goto(&done);
|
||||
|
||||
BIND(&if_new);
|
||||
result.Bind(AwaitOptimized(context, generator, value, outer_promise,
|
||||
on_resolve_context_index, on_reject_context_index,
|
||||
is_predicted_as_caught));
|
||||
Goto(&done);
|
||||
|
||||
BIND(&done);
|
||||
return result.value();
|
||||
}
|
||||
|
||||
void AsyncBuiltinsAssembler::InitializeNativeClosure(Node* context,
|
||||
Node* native_context,
|
||||
Node* function,
|
||||
|
@ -24,10 +24,6 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
|
||||
Node* Await(Node* context, Node* generator, Node* value, Node* outer_promise,
|
||||
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, 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 on_resolve_context_index, int on_reject_context_index,
|
||||
Node* is_predicted_as_caught) {
|
||||
@ -36,15 +32,6 @@ 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 on_resolve_context_index,
|
||||
int on_reject_context_index,
|
||||
Node* is_predicted_as_caught) {
|
||||
return AwaitOptimized(context, generator, value, outer_promise,
|
||||
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 on_resolve_context_index, int on_reject_context_index,
|
||||
bool is_predicted_as_caught) {
|
||||
@ -52,14 +39,6 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
|
||||
on_resolve_context_index, on_reject_context_index,
|
||||
BooleanConstant(is_predicted_as_caught));
|
||||
}
|
||||
Node* AwaitOptimized(Node* context, Node* generator, Node* value,
|
||||
Node* outer_promise, int on_resolve_context_index,
|
||||
int on_reject_context_index,
|
||||
bool is_predicted_as_caught) {
|
||||
return AwaitOptimized(context, generator, value, outer_promise,
|
||||
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
|
||||
@ -70,6 +49,14 @@ class AsyncBuiltinsAssembler : public PromiseBuiltinsAssembler {
|
||||
Node* function, Node* context_index);
|
||||
Node* AllocateAsyncIteratorValueUnwrapContext(Node* native_context,
|
||||
Node* done);
|
||||
|
||||
Node* AwaitOld(Node* context, Node* generator, Node* value,
|
||||
Node* outer_promise, 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, Node* on_resolve_context_index,
|
||||
Node* on_reject_context_index,
|
||||
Node* is_predicted_as_caught);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -333,20 +333,6 @@ 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,
|
||||
@ -374,31 +360,6 @@ 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,
|
||||
@ -416,20 +377,5 @@ 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
|
||||
|
@ -429,12 +429,8 @@ 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) \
|
||||
@ -1297,13 +1293,10 @@ 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) \
|
||||
\
|
||||
@ -1480,14 +1473,9 @@ 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) \
|
||||
|
@ -459,6 +459,11 @@ ExternalReference ExternalReference::abort_with_reason() {
|
||||
return ExternalReference(Redirect(FUNCTION_ADDR(i::abort_with_reason)));
|
||||
}
|
||||
|
||||
ExternalReference
|
||||
ExternalReference::address_of_harmony_await_optimization_flag() {
|
||||
return ExternalReference(&FLAG_harmony_await_optimization);
|
||||
}
|
||||
|
||||
ExternalReference ExternalReference::address_of_min_int() {
|
||||
return ExternalReference(reinterpret_cast<Address>(&double_min_int_constant));
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ class StatsCounter;
|
||||
V(address_of_double_neg_constant, "double_negate_constant") \
|
||||
V(address_of_float_abs_constant, "float_absolute_constant") \
|
||||
V(address_of_float_neg_constant, "float_negate_constant") \
|
||||
V(address_of_harmony_await_optimization_flag, \
|
||||
"FLAG_harmony_await_optimization") \
|
||||
V(address_of_min_int, "LDoubleConstant::min_int") \
|
||||
V(address_of_one_half, "LDoubleConstant::one_half") \
|
||||
V(address_of_runtime_stats_flag, "FLAG_runtime_stats") \
|
||||
|
124
test/mjsunit/harmony/async-await-optimization.js
Normal file
124
test/mjsunit/harmony/async-await-optimization.js
Normal file
@ -0,0 +1,124 @@
|
||||
// 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
|
||||
|
||||
// test basic interleaving
|
||||
(function () {
|
||||
const actual = [];
|
||||
const expected = [ 'await', 1, 'await', 2 ];
|
||||
const iterations = 2;
|
||||
|
||||
async function pushAwait() {
|
||||
actual.push('await');
|
||||
}
|
||||
|
||||
async function callAsync() {
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
await pushAwait();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function checkAssertions() {
|
||||
assertArrayEquals(expected, actual,
|
||||
'Async/await and promises should be interleaved.');
|
||||
}
|
||||
|
||||
assertPromiseResult((async() => {
|
||||
callAsync();
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
actual.push(1);
|
||||
resolve();
|
||||
}).then(function () {
|
||||
actual.push(2);
|
||||
}).then(checkAssertions);
|
||||
})());
|
||||
})();
|
||||
|
||||
// test async generators
|
||||
(function () {
|
||||
const actual = [];
|
||||
const expected = [ 'await', 1, 'await', 2 ];
|
||||
const iterations = 2;
|
||||
|
||||
async function pushAwait() {
|
||||
actual.push('await');
|
||||
}
|
||||
|
||||
async function* callAsync() {
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
await pushAwait();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function checkAssertions() {
|
||||
assertArrayEquals(expected, actual,
|
||||
'Async/await and promises should be interleaved when using async generators.');
|
||||
}
|
||||
|
||||
assertPromiseResult((async() => {
|
||||
callAsync().next();
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
actual.push(1);
|
||||
resolve();
|
||||
}).then(function () {
|
||||
actual.push(2);
|
||||
}).then(checkAssertions);
|
||||
})());
|
||||
})();
|
||||
|
||||
// test yielding from async generators
|
||||
(function () {
|
||||
const actual = [];
|
||||
const expected = [
|
||||
'Promise: 6',
|
||||
'Promise: 5',
|
||||
'Await: 3',
|
||||
'Promise: 4',
|
||||
'Promise: 3',
|
||||
'Await: 2',
|
||||
'Promise: 2',
|
||||
'Promise: 1',
|
||||
'Await: 1',
|
||||
'Promise: 0'
|
||||
];
|
||||
const iterations = 3;
|
||||
|
||||
async function* naturalNumbers(start) {
|
||||
let current = start;
|
||||
while (current > 0) {
|
||||
yield Promise.resolve(current--);
|
||||
}
|
||||
}
|
||||
|
||||
async function trigger() {
|
||||
for await (const num of naturalNumbers(iterations)) {
|
||||
actual.push('Await: ' + num);
|
||||
}
|
||||
}
|
||||
|
||||
async function checkAssertions() {
|
||||
assertArrayEquals(expected, actual,
|
||||
'Async/await and promises should be interleaved when yielding.');
|
||||
}
|
||||
|
||||
async function countdown(counter) {
|
||||
actual.push('Promise: ' + counter);
|
||||
if (counter > 0) {
|
||||
return Promise.resolve(counter - 1).then(countdown);
|
||||
} else {
|
||||
await checkAssertions();
|
||||
}
|
||||
}
|
||||
|
||||
assertPromiseResult((async() => {
|
||||
trigger();
|
||||
|
||||
return countdown(iterations * 2);
|
||||
})());
|
||||
})();
|
Loading…
Reference in New Issue
Block a user