[async-iteration] implement spec-change to yield
in async generators
Per https://github.com/tc39/proposal-async-iteration/pull/102/files: AsyncGeneratorResolve no longer unwraps a value component. Instead, the value is unwrapped before the builtin call via Await, allowing Promise rejections to affect the generator control flow. Thus, all `yield <expr>` implicitly become `yield await <expr>`. Additionally, `return <expr>` becomes `return await <expr>`. Finally, when the generator is resumed with `.return()`, the parameter passed to .return() is awaited before generator execution properly continues). BUG=v8:5855 R=littledan@chromium.org, neis@chromium.org, adamk@chromium.org Cq-Include-Trybots: master.tryserver.v8:v8_linux_noi18n_rel_ng Change-Id: Ife084076c3ed434b5467e6aeba14082f8b410ad5 Reviewed-on: https://chromium-review.googlesource.com/523844 Commit-Queue: Caitlin Potter <caitp@igalia.com> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#47011}
This commit is contained in:
parent
2f79e03560
commit
409f84c93b
@ -95,6 +95,7 @@ class AstNumberingVisitor final : public AstVisitor<AstNumberingVisitor> {
|
||||
int suspend_count_;
|
||||
AstProperties properties_;
|
||||
LanguageMode language_mode_;
|
||||
FunctionKind function_kind_;
|
||||
// The slot cache allows us to reuse certain feedback slots.
|
||||
FeedbackSlotCache slot_cache_;
|
||||
BailoutReason disable_fullcodegen_reason_;
|
||||
@ -218,7 +219,13 @@ void AstNumberingVisitor::VisitSuspend(Suspend* node) {
|
||||
Visit(node->expression());
|
||||
}
|
||||
|
||||
void AstNumberingVisitor::VisitYield(Yield* node) { VisitSuspend(node); }
|
||||
void AstNumberingVisitor::VisitYield(Yield* node) {
|
||||
node->set_suspend_id(suspend_count_++);
|
||||
if (IsAsyncGeneratorFunction(function_kind_)) {
|
||||
node->set_await_return_value_suspend_id(suspend_count_++);
|
||||
}
|
||||
Visit(node->expression());
|
||||
}
|
||||
|
||||
void AstNumberingVisitor::VisitYieldStar(YieldStar* node) {
|
||||
VisitSuspend(node);
|
||||
@ -581,6 +588,7 @@ bool AstNumberingVisitor::Renumber(FunctionLiteral* node) {
|
||||
DisableFullCodegen(kClassConstructorFunction);
|
||||
}
|
||||
|
||||
function_kind_ = node->kind();
|
||||
LanguageModeScope language_mode_scope(this, node->language_mode());
|
||||
|
||||
if (collect_type_profile_) {
|
||||
|
@ -2275,10 +2275,24 @@ class Suspend : public Expression {
|
||||
};
|
||||
|
||||
class Yield final : public Suspend {
|
||||
public:
|
||||
inline int await_return_value_suspend_id() const {
|
||||
DCHECK_NE(await_return_value_suspend_id_, -1);
|
||||
return await_return_value_suspend_id_;
|
||||
}
|
||||
void set_await_return_value_suspend_id(int id) {
|
||||
await_return_value_suspend_id_ = id;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
Yield(Expression* expression, int pos, OnAbruptResume on_abrupt_resume)
|
||||
: Suspend(kYield, expression, pos, on_abrupt_resume) {}
|
||||
: Suspend(kYield, expression, pos, on_abrupt_resume),
|
||||
await_return_value_suspend_id_(-1) {}
|
||||
|
||||
// TODO(caitp): remove from class once `await` handled by AsyncGeneratorReturn
|
||||
// stub.
|
||||
int await_return_value_suspend_id_;
|
||||
};
|
||||
|
||||
class YieldStar final : public Suspend {
|
||||
|
@ -1483,6 +1483,20 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
info->set_internal_formal_parameter_count(1);
|
||||
info->set_length(1);
|
||||
native_context()->set_async_generator_await_reject_shared_fun(*info);
|
||||
|
||||
info = factory->NewSharedFunctionInfo(
|
||||
factory->empty_string(),
|
||||
isolate->builtins()->AsyncGeneratorReturnResolveClosure(), false);
|
||||
info->set_internal_formal_parameter_count(1);
|
||||
info->set_length(1);
|
||||
native_context()->set_async_generator_return_resolve_shared_fun(*info);
|
||||
|
||||
info = factory->NewSharedFunctionInfo(
|
||||
factory->empty_string(),
|
||||
isolate->builtins()->AsyncGeneratorReturnRejectClosure(), false);
|
||||
info->set_internal_formal_parameter_count(1);
|
||||
info->set_length(1);
|
||||
native_context()->set_async_generator_return_reject_shared_fun(*info);
|
||||
}
|
||||
|
||||
{ // --- A r r a y ---
|
||||
|
@ -418,8 +418,8 @@ TF_BUILTIN(AsyncGeneratorResumeNext, AsyncGeneratorBuiltinsAssembler) {
|
||||
LoadGeneratorState(generator));
|
||||
VARIABLE(var_next, MachineRepresentation::kTagged,
|
||||
LoadFirstAsyncGeneratorRequestFromQueue(generator));
|
||||
Variable* labels[] = {&var_state, &var_next};
|
||||
Label start(this, 2, labels);
|
||||
Variable* loop_variables[] = {&var_state, &var_next};
|
||||
Label start(this, 2, loop_variables);
|
||||
Goto(&start);
|
||||
BIND(&start);
|
||||
|
||||
@ -453,10 +453,10 @@ TF_BUILTIN(AsyncGeneratorResumeNext, AsyncGeneratorBuiltinsAssembler) {
|
||||
&fulfill_promise, &reject_promise);
|
||||
|
||||
BIND(&fulfill_promise);
|
||||
CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator,
|
||||
LoadValueFromAsyncGeneratorRequest(next), TrueConstant());
|
||||
var_next.Bind(LoadFirstAsyncGeneratorRequestFromQueue(generator));
|
||||
Goto(&start);
|
||||
// Unwrap Promise values for "return" in state "suspendedStart".
|
||||
// This simulates `return await request.[[Completion]].[[Value]]`
|
||||
TailCallBuiltin(Builtins::kAsyncGeneratorReturnProcessor, context,
|
||||
generator);
|
||||
|
||||
BIND(&reject_promise);
|
||||
CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator,
|
||||
@ -495,19 +495,35 @@ TF_BUILTIN(AsyncGeneratorResolve, AsyncGeneratorBuiltinsAssembler) {
|
||||
HasInstanceType(generator, JS_ASYNC_GENERATOR_OBJECT_TYPE));
|
||||
CSA_ASSERT(this, IsGeneratorNotSuspendedForAwait(generator));
|
||||
|
||||
// If this assertion fails, the `value` component was not Awaited as it should
|
||||
// have been, per https://github.com/tc39/proposal-async-iteration/pull/102/.
|
||||
CSA_SLOW_ASSERT(this, DoesntHaveInstanceType(value, JS_PROMISE_TYPE));
|
||||
|
||||
Node* const next = TakeFirstAsyncGeneratorRequestFromQueue(generator);
|
||||
Node* const promise = LoadPromiseFromAsyncGeneratorRequest(next);
|
||||
|
||||
Node* const wrapper = AllocateAndInitJSPromise(context);
|
||||
CallBuiltin(Builtins::kResolveNativePromise, context, wrapper, value);
|
||||
// Let iteratorResult be CreateIterResultObject(value, done).
|
||||
Node* const iter_result = Allocate(JSIteratorResult::kSize);
|
||||
{
|
||||
Node* map = LoadContextElement(LoadNativeContext(context),
|
||||
Context::ITERATOR_RESULT_MAP_INDEX);
|
||||
StoreMapNoWriteBarrier(iter_result, map);
|
||||
StoreObjectFieldRoot(iter_result, JSIteratorResult::kPropertiesOrHashOffset,
|
||||
Heap::kEmptyFixedArrayRootIndex);
|
||||
StoreObjectFieldRoot(iter_result, JSIteratorResult::kElementsOffset,
|
||||
Heap::kEmptyFixedArrayRootIndex);
|
||||
StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kValueOffset,
|
||||
value);
|
||||
StoreObjectFieldNoWriteBarrier(iter_result, JSIteratorResult::kDoneOffset,
|
||||
done);
|
||||
}
|
||||
|
||||
Node* const on_fulfilled =
|
||||
CreateUnwrapClosure(LoadNativeContext(context), done);
|
||||
// Perform Call(promiseCapability.[[Resolve]], undefined, «iteratorResult»).
|
||||
CallBuiltin(Builtins::kResolveNativePromise, context, promise, iter_result);
|
||||
|
||||
// Per spec, AsyncGeneratorResolve() returns undefined. However, for the
|
||||
// benefit of %TraceExit(), return the Promise.
|
||||
Return(CallBuiltin(Builtins::kPerformNativePromiseThen, context, wrapper,
|
||||
on_fulfilled, UndefinedConstant(), promise));
|
||||
Return(promise);
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) {
|
||||
@ -523,5 +539,65 @@ TF_BUILTIN(AsyncGeneratorReject, AsyncGeneratorBuiltinsAssembler) {
|
||||
TrueConstant()));
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncGeneratorReturnProcessor, AsyncGeneratorBuiltinsAssembler) {
|
||||
Node* const generator = Parameter(Descriptor::kGenerator);
|
||||
Node* const req = LoadFirstAsyncGeneratorRequestFromQueue(generator);
|
||||
|
||||
CSA_SLOW_ASSERT(this, SmiEqual(LoadResumeTypeFromAsyncGeneratorRequest(req),
|
||||
SmiConstant(JSGeneratorObject::kReturn)));
|
||||
|
||||
ContextInitializer init_closure_context = [&](Node* context) {
|
||||
StoreContextElementNoWriteBarrier(context, AwaitContext::kGeneratorSlot,
|
||||
generator);
|
||||
};
|
||||
|
||||
const bool kIsPredictedAsCaught = false;
|
||||
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
Node* const outer_promise = LoadPromiseFromAsyncGeneratorRequest(req);
|
||||
Node* const value = LoadValueFromAsyncGeneratorRequest(req);
|
||||
Node* const promise = Await(
|
||||
context, generator, value, outer_promise, AwaitContext::kLength,
|
||||
init_closure_context, Context::ASYNC_GENERATOR_RETURN_RESOLVE_SHARED_FUN,
|
||||
Context::ASYNC_GENERATOR_RETURN_REJECT_SHARED_FUN, kIsPredictedAsCaught);
|
||||
|
||||
CSA_SLOW_ASSERT(this, IsGeneratorNotSuspendedForAwait(generator));
|
||||
StoreObjectField(generator, JSAsyncGeneratorObject::kAwaitedPromiseOffset,
|
||||
promise);
|
||||
Return(UndefinedConstant());
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncGeneratorReturnResolveClosure,
|
||||
AsyncGeneratorBuiltinsAssembler) {
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
Node* const value = Parameter(Descriptor::kValue);
|
||||
Node* const generator =
|
||||
LoadContextElement(context, AwaitContext::kGeneratorSlot);
|
||||
|
||||
CSA_SLOW_ASSERT(this, IsGeneratorSuspendedForAwait(generator));
|
||||
ClearAwaitedPromise(generator);
|
||||
|
||||
// Return ! AsyncGeneratorResolve(_F_.[[Generator]], _value_, *true*).
|
||||
CallBuiltin(Builtins::kAsyncGeneratorResolve, context, generator, value,
|
||||
TrueConstant());
|
||||
|
||||
TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
|
||||
}
|
||||
|
||||
TF_BUILTIN(AsyncGeneratorReturnRejectClosure, AsyncGeneratorBuiltinsAssembler) {
|
||||
Node* const context = Parameter(Descriptor::kContext);
|
||||
Node* const value = Parameter(Descriptor::kValue);
|
||||
Node* const generator =
|
||||
LoadContextElement(context, AwaitContext::kGeneratorSlot);
|
||||
|
||||
CSA_SLOW_ASSERT(this, IsGeneratorSuspendedForAwait(generator));
|
||||
ClearAwaitedPromise(generator);
|
||||
|
||||
// Return ! AsyncGeneratorReject(_F_.[[Generator]], _reason_).
|
||||
CallBuiltin(Builtins::kAsyncGeneratorReject, context, generator, value);
|
||||
|
||||
TailCallBuiltin(Builtins::kAsyncGeneratorResumeNext, context, generator);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -1050,6 +1050,7 @@ namespace internal {
|
||||
\
|
||||
TFS(AsyncGeneratorResolve, kGenerator, kValue, kDone) \
|
||||
TFS(AsyncGeneratorReject, kGenerator, kValue) \
|
||||
TFS(AsyncGeneratorReturnProcessor, kGenerator) \
|
||||
TFS(AsyncGeneratorResumeNext, kGenerator) \
|
||||
\
|
||||
/* AsyncGeneratorFunction( p1, p2, ... pn, body ) */ \
|
||||
@ -1074,6 +1075,8 @@ namespace internal {
|
||||
TFJ(AsyncGeneratorAwaitUncaught, 1, kAwaited) \
|
||||
TFJ(AsyncGeneratorAwaitResolveClosure, 1, kValue) \
|
||||
TFJ(AsyncGeneratorAwaitRejectClosure, 1, kValue) \
|
||||
TFJ(AsyncGeneratorReturnResolveClosure, 1, kValue) \
|
||||
TFJ(AsyncGeneratorReturnRejectClosure, 1, kValue) \
|
||||
\
|
||||
/* Async-from-Sync Iterator */ \
|
||||
\
|
||||
|
@ -217,6 +217,10 @@ enum ContextLookupFlags {
|
||||
async_generator_await_reject_shared_fun) \
|
||||
V(ASYNC_GENERATOR_AWAIT_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
|
||||
async_generator_await_resolve_shared_fun) \
|
||||
V(ASYNC_GENERATOR_RETURN_RESOLVE_SHARED_FUN, SharedFunctionInfo, \
|
||||
async_generator_return_resolve_shared_fun) \
|
||||
V(ASYNC_GENERATOR_RETURN_REJECT_SHARED_FUN, SharedFunctionInfo, \
|
||||
async_generator_return_reject_shared_fun) \
|
||||
V(ATOMICS_OBJECT, JSObject, atomics_object) \
|
||||
V(BOOLEAN_FUNCTION_INDEX, JSFunction, boolean_function) \
|
||||
V(BOUND_FUNCTION_WITH_CONSTRUCTOR_MAP_INDEX, Map, \
|
||||
|
@ -1578,7 +1578,7 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
||||
|
||||
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
||||
// We can't know whether the finally block will override ("catch") an
|
||||
// exception thrown in the try bblock, so we just adopt the outer prediction.
|
||||
// exception thrown in the try block, so we just adopt the outer prediction.
|
||||
TryFinallyBuilder try_control_builder(builder(), catch_prediction());
|
||||
|
||||
// We keep a record of all paths that enter the finally-block to be able to
|
||||
@ -2661,6 +2661,7 @@ void BytecodeGenerator::VisitYield(Yield* expr) {
|
||||
builder()->LoadAccumulatorWithRegister(input);
|
||||
if (IsAsyncGeneratorFunction(function_kind())) {
|
||||
// Async generator methods will produce the iter result object.
|
||||
BuildAwait(expr->await_return_value_suspend_id());
|
||||
execution_control()->AsyncReturnAccumulator();
|
||||
} else {
|
||||
execution_control()->ReturnAccumulator();
|
||||
@ -2911,7 +2912,7 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
|
||||
builder()->LoadAccumulatorWithRegister(output_value);
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitAwait(Await* expr) {
|
||||
void BytecodeGenerator::BuildAwait(int suspend_id) {
|
||||
// Rather than HandlerTable::UNCAUGHT, async functions use
|
||||
// HandlerTable::ASYNC_AWAIT to communicate that top-level exceptions are
|
||||
// transformed into promise rejections. This is necessary to prevent emitting
|
||||
@ -2920,8 +2921,6 @@ void BytecodeGenerator::VisitAwait(Await* expr) {
|
||||
// HandlerTable::UNCAUGHT.
|
||||
DCHECK(catch_prediction() != HandlerTable::UNCAUGHT);
|
||||
|
||||
builder()->SetExpressionPosition(expr);
|
||||
VisitForAccumulatorValue(expr->expression());
|
||||
{
|
||||
// Await(operand) and suspend.
|
||||
RegisterAllocationScope register_scope(this);
|
||||
@ -2958,7 +2957,7 @@ void BytecodeGenerator::VisitAwait(Await* expr) {
|
||||
builder()->CallJSRuntime(await_builtin_context_index, args);
|
||||
}
|
||||
|
||||
BuildSuspendPoint(expr->suspend_id());
|
||||
BuildSuspendPoint(suspend_id);
|
||||
|
||||
Register input = register_allocator()->NewRegister();
|
||||
Register resume_mode = register_allocator()->NewRegister();
|
||||
@ -2976,14 +2975,19 @@ void BytecodeGenerator::VisitAwait(Await* expr) {
|
||||
// Resume with "throw" completion (rethrow the received value).
|
||||
// TODO(leszeks): Add a debug-only check that the accumulator is
|
||||
// JSGeneratorObject::kThrow.
|
||||
builder()->SetExpressionPosition(expr);
|
||||
builder()->LoadAccumulatorWithRegister(input).ReThrow();
|
||||
|
||||
// Resume with next.
|
||||
builder()->Bind(&resume_next);
|
||||
builder()->LoadAccumulatorWithRegister(input);
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitAwait(Await* expr) {
|
||||
builder()->SetExpressionPosition(expr);
|
||||
VisitForAccumulatorValue(expr->expression());
|
||||
BuildAwait(expr->suspend_id());
|
||||
BuildIncrementBlockCoverageCounterIfEnabled(expr,
|
||||
SourceRangeKind::kContinuation);
|
||||
builder()->LoadAccumulatorWithRegister(input);
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitThrow(Throw* expr) {
|
||||
|
@ -140,7 +140,8 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
void BuildGeneratorPrologue();
|
||||
void BuildSuspendPoint(int suspend_id);
|
||||
|
||||
void BuildAbruptResume(Suspend* expr);
|
||||
void BuildAwait(int suspend_id);
|
||||
|
||||
void BuildGetIterator(Expression* iterable, IteratorType hint,
|
||||
FeedbackSlot load_slot, FeedbackSlot call_slot,
|
||||
FeedbackSlot async_load_slot,
|
||||
|
@ -1341,9 +1341,17 @@ class ParserBase {
|
||||
// depending on the current function type.
|
||||
inline StatementT BuildReturnStatement(ExpressionT expr, int pos,
|
||||
int end_pos = kNoSourcePosition) {
|
||||
return is_async_function()
|
||||
? factory()->NewAsyncReturnStatement(expr, pos, end_pos)
|
||||
: factory()->NewReturnStatement(expr, pos, end_pos);
|
||||
if (impl()->IsEmptyExpression(expr)) {
|
||||
expr = impl()->GetLiteralUndefined(kNoSourcePosition);
|
||||
} else if (is_async_generator()) {
|
||||
// In async generators, if there is an explicit operand to the return
|
||||
// statement, await the operand.
|
||||
expr = factory()->NewAwait(expr, kNoSourcePosition);
|
||||
}
|
||||
if (is_async_function()) {
|
||||
return factory()->NewAsyncReturnStatement(expr, pos, end_pos);
|
||||
}
|
||||
return factory()->NewReturnStatement(expr, pos, end_pos);
|
||||
}
|
||||
|
||||
// Validation per ES6 object literals.
|
||||
@ -2943,6 +2951,12 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseYieldExpression(
|
||||
return impl()->RewriteYieldStar(expression, pos);
|
||||
}
|
||||
|
||||
if (is_async_generator()) {
|
||||
// Per https://github.com/tc39/proposal-async-iteration/pull/102, the yield
|
||||
// operand must be Await-ed in async generators.
|
||||
expression = factory()->NewAwait(expression, pos);
|
||||
}
|
||||
|
||||
// Hackily disambiguate o from o.next and o [Symbol.iterator]().
|
||||
// TODO(verwaest): Come up with a better solution.
|
||||
ExpressionT yield =
|
||||
@ -5221,8 +5235,6 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseReturnStatement(
|
||||
tok == Token::RBRACE || tok == Token::EOS) {
|
||||
if (IsDerivedConstructor(function_state_->kind())) {
|
||||
return_value = impl()->ThisExpression(loc.beg_pos);
|
||||
} else {
|
||||
return_value = impl()->GetLiteralUndefined(position());
|
||||
}
|
||||
} else {
|
||||
return_value = ParseExpression(true, CHECK_OK);
|
||||
|
@ -4526,11 +4526,14 @@ Expression* Parser::RewriteYieldStar(Expression* iterable, int pos) {
|
||||
VariableProxy* output_proxy = factory()->NewVariableProxy(var_output);
|
||||
Expression* literal = factory()->NewStringLiteral(
|
||||
ast_value_factory()->value_string(), nopos);
|
||||
Assignment* assign = factory()->NewAssignment(
|
||||
Token::ASSIGN, output_proxy,
|
||||
|
||||
Expression* value = factory()->NewAwait(
|
||||
factory()->NewProperty(factory()->NewVariableProxy(var_output),
|
||||
literal, nopos),
|
||||
nopos);
|
||||
|
||||
Assignment* assign =
|
||||
factory()->NewAssignment(Token::ASSIGN, output_proxy, value, nopos);
|
||||
loop_body->statements()->Add(
|
||||
factory()->NewExpressionStatement(assign, nopos), zone());
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -92,7 +92,7 @@ bytecodes: [
|
||||
B(TestEqualStrictNoFeedback), R(22),
|
||||
B(JumpIfTrue), U8(5),
|
||||
B(Ldar), R(21),
|
||||
/* 40 E> */ B(ReThrow),
|
||||
B(ReThrow),
|
||||
B(Mov), R(21), R(5),
|
||||
/* 40 E> */ B(InvokeIntrinsic), U8(Runtime::k_IsJSReceiver), R(21), U8(1),
|
||||
B(ToBooleanLogicalNot),
|
||||
@ -395,7 +395,7 @@ bytecodes: [
|
||||
B(TestEqualStrictNoFeedback), R(22),
|
||||
B(JumpIfTrue), U8(5),
|
||||
B(Ldar), R(21),
|
||||
/* 40 E> */ B(ReThrow),
|
||||
B(ReThrow),
|
||||
B(Mov), R(21), R(5),
|
||||
/* 40 E> */ B(InvokeIntrinsic), U8(Runtime::k_IsJSReceiver), R(21), U8(1),
|
||||
B(ToBooleanLogicalNot),
|
||||
@ -716,7 +716,7 @@ bytecodes: [
|
||||
B(TestEqualStrictNoFeedback), R(22),
|
||||
B(JumpIfTrue), U8(5),
|
||||
B(Ldar), R(21),
|
||||
/* 40 E> */ B(ReThrow),
|
||||
B(ReThrow),
|
||||
B(Mov), R(21), R(5),
|
||||
/* 40 E> */ B(InvokeIntrinsic), U8(Runtime::k_IsJSReceiver), R(21), U8(1),
|
||||
B(ToBooleanLogicalNot),
|
||||
|
@ -1346,7 +1346,7 @@ bytecodes: [
|
||||
B(TestEqualStrictNoFeedback), R(23),
|
||||
B(JumpIfTrue), U8(5),
|
||||
B(Ldar), R(22),
|
||||
/* 45 E> */ B(ReThrow),
|
||||
B(ReThrow),
|
||||
B(LdaZero),
|
||||
B(Star), R(6),
|
||||
B(JumpLoop), U8(118), I8(0),
|
||||
|
@ -585,7 +585,7 @@ bytecodes: [
|
||||
B(TestEqualStrictNoFeedback), R(10),
|
||||
B(JumpIfTrue), U8(5),
|
||||
B(Ldar), R(9),
|
||||
/* 52 E> */ B(ReThrow),
|
||||
B(ReThrow),
|
||||
/* 49 S> */ B(Ldar), R(0),
|
||||
B(Inc), U8(1),
|
||||
B(Star), R(0),
|
||||
|
150
test/mjsunit/harmony/async-generators-resume-return.js
Normal file
150
test/mjsunit/harmony/async-generators-resume-return.js
Normal file
@ -0,0 +1,150 @@
|
||||
// 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-async-iteration --allow-natives-syntax
|
||||
|
||||
// .return() from state suspendedStart with undefined
|
||||
testAsync(test => {
|
||||
test.plan(3);
|
||||
|
||||
async function* gen() {
|
||||
test.unreachable();
|
||||
}
|
||||
|
||||
let didResolvePromise = false;
|
||||
let g = gen();
|
||||
g.return(undefined).then(
|
||||
(iterResult) => {
|
||||
test.equals(true, didResolvePromise);
|
||||
test.equals({ value: undefined, done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
g.next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: undefined, done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
// Race: generator request's promise should resolve after this Promise.
|
||||
Promise.resolve("already-resolved").then(
|
||||
_ => { didResolvePromise = true },
|
||||
test.unexpectedRejection());
|
||||
|
||||
}, "AsyncGenerator.return(undefined) / suspendStart");
|
||||
|
||||
// .return() from state suspendedStart with thenable
|
||||
testAsync(test => {
|
||||
test.plan(3);
|
||||
|
||||
async function* gen() {
|
||||
test.unreachable();
|
||||
}
|
||||
|
||||
let didResolvePromise = false;
|
||||
let g = gen();
|
||||
|
||||
let resolve;
|
||||
let awaitedThenable = { then(resolveFn) { resolve = resolveFn; } };
|
||||
|
||||
g.return(awaitedThenable).then(
|
||||
(iterResult) => {
|
||||
test.equals(true, didResolvePromise);
|
||||
test.equals({ value: "resolvedPromise", done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
g.next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: undefined, done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
test.drainMicrotasks();
|
||||
resolve("resolvedPromise");
|
||||
|
||||
// Race: generator request's promise should resolve after this Promise.
|
||||
Promise.resolve("already-resolved").then(
|
||||
_ => { didResolvePromise = true },
|
||||
test.unexpectedRejection());
|
||||
|
||||
}, "AsyncGenerator.return(thenable) / suspendStart");
|
||||
|
||||
// .return() from state suspendedYield with undefined
|
||||
testAsync(test => {
|
||||
test.plan(4);
|
||||
|
||||
async function* gen() {
|
||||
yield;
|
||||
test.unreachable();
|
||||
}
|
||||
|
||||
let didResolvePromise = false;
|
||||
let g = gen();
|
||||
g.next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: undefined, done: false }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
g.return(undefined).then(
|
||||
(iterResult) => {
|
||||
test.equals(true, didResolvePromise);
|
||||
test.equals({ value: undefined, done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
g.next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: undefined, done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
// Race: generator request's promise should resolve after this Promise.
|
||||
Promise.resolve("already-resolved").then(
|
||||
_ => { didResolvePromise = true },
|
||||
test.unexpectedRejection());
|
||||
|
||||
}, "AsyncGenerator.return(undefined) / suspendedYield");
|
||||
|
||||
// .return() from state suspendedYield with thenable
|
||||
testAsync(test => {
|
||||
test.plan(4);
|
||||
|
||||
async function* gen() {
|
||||
yield;
|
||||
test.unreachable();
|
||||
}
|
||||
|
||||
let didResolvePromise = false;
|
||||
let g = gen();
|
||||
|
||||
let resolve;
|
||||
let awaitedThenable = { then(resolveFn) { resolve = resolveFn; } };
|
||||
|
||||
g.next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: undefined, done: false }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
g.return(awaitedThenable).then(
|
||||
(iterResult) => {
|
||||
test.equals(true, didResolvePromise);
|
||||
test.equals({ value: "resolvedPromise", done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
g.next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: undefined, done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
test.drainMicrotasks();
|
||||
resolve("resolvedPromise");
|
||||
|
||||
// Race: generator request's promise should resolve after this Promise.
|
||||
Promise.resolve("already-resolved").then(
|
||||
_ => { didResolvePromise = true },
|
||||
test.unexpectedRejection());
|
||||
|
||||
}, "AsyncGenerator.return(thenable) / suspendYield");
|
119
test/mjsunit/harmony/async-generators-return.js
Normal file
119
test/mjsunit/harmony/async-generators-return.js
Normal file
@ -0,0 +1,119 @@
|
||||
// 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-async-iteration --allow-natives-syntax
|
||||
|
||||
testAsync(test => {
|
||||
test.plan(2);
|
||||
|
||||
async function* gen() {
|
||||
return;
|
||||
test.unreachable();
|
||||
}
|
||||
|
||||
let didResolvePromise = false;
|
||||
gen().next().then(
|
||||
(iterResult) => {
|
||||
test.equals(false, didResolvePromise);
|
||||
test.equals({ value: undefined, done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
// Race: generator request's promise should resolve before this Promise.
|
||||
Promise.resolve("already-resolved").then(
|
||||
_ => { didResolvePromise = true },
|
||||
test.unexpectedRejection());
|
||||
|
||||
}, "return-race-no-operand");
|
||||
|
||||
testAsync(test => {
|
||||
test.plan(2);
|
||||
|
||||
async function* gen() {
|
||||
return undefined;
|
||||
test.unreachable();
|
||||
}
|
||||
|
||||
let didResolvePromise = false;
|
||||
gen().next().then(
|
||||
(iterResult) => {
|
||||
test.equals(true, didResolvePromise);
|
||||
test.equals({ value: undefined, done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
// Race: generator request's promise should resolve after this Promise.
|
||||
Promise.resolve("already-resolved").then(
|
||||
_ => { didResolvePromise = true },
|
||||
test.unexpectedRejection());
|
||||
|
||||
}, "return-race-with-operand");
|
||||
|
||||
// Return a thenable which is never settled
|
||||
testAsync(test => {
|
||||
test.plan(0);
|
||||
|
||||
let promise = { then() { } };
|
||||
|
||||
async function* gen() {
|
||||
return promise;
|
||||
test.unreachable();
|
||||
}
|
||||
|
||||
gen().next().then(
|
||||
(iterResult) => test.unreachable(),
|
||||
test.unexpectedRejection());
|
||||
}, "return-await-thenable-pending");
|
||||
|
||||
// Return a thenable which is fulfilled later
|
||||
testAsync(test => {
|
||||
test.plan(2);
|
||||
|
||||
let resolve;
|
||||
let awaitedThenable = { then(resolveFn) { resolve = resolveFn; } };
|
||||
let finallyEvaluated = false;
|
||||
|
||||
async function* gen() {
|
||||
try {
|
||||
return awaitedThenable;
|
||||
} finally {
|
||||
finallyEvaluated = true;
|
||||
}
|
||||
}
|
||||
|
||||
gen().next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: "resolvedPromise", done: true }, iterResult);
|
||||
test.equals(true, finallyEvaluated);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
test.drainMicrotasks();
|
||||
resolve("resolvedPromise");
|
||||
}, "yield-await-thenable-resolved");
|
||||
|
||||
// Return a thenable which is rejected later
|
||||
testAsync(test => {
|
||||
test.plan(2);
|
||||
|
||||
let reject;
|
||||
let awaitedThenable = { then(resolveFn, rejectFn) { reject = rejectFn; } };
|
||||
async function* gen() {
|
||||
try {
|
||||
yield awaitedThenable;
|
||||
} catch (e) {
|
||||
test.equals("rejection", e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
gen().next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: "rejection", done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
test.drainMicrotasks();
|
||||
reject("rejection");
|
||||
}, "yield-await-thenable-rejected");
|
68
test/mjsunit/harmony/async-generators-yield.js
Normal file
68
test/mjsunit/harmony/async-generators-yield.js
Normal file
@ -0,0 +1,68 @@
|
||||
// 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-async-iteration --allow-natives-syntax
|
||||
|
||||
// Yield a thenable which is never settled
|
||||
testAsync(test => {
|
||||
test.plan(0);
|
||||
|
||||
let awaitedThenable = { then() { } };
|
||||
|
||||
async function* gen() {
|
||||
yield awaitedThenable;
|
||||
test.unreachable();
|
||||
}
|
||||
|
||||
gen().next().then(
|
||||
(iterResult) => test.unreachable(),
|
||||
test.unexpectedRejection());
|
||||
}, "yield-await-thenable-pending");
|
||||
|
||||
// Yield a thenable which is fulfilled later
|
||||
testAsync(test => {
|
||||
test.plan(1);
|
||||
|
||||
let resolve;
|
||||
let awaitedThenable = { then(resolveFn) { resolve = resolveFn; } };
|
||||
|
||||
async function* gen() {
|
||||
let input = yield awaitedThenable;
|
||||
test.equals("resolvedPromise", input);
|
||||
}
|
||||
|
||||
gen().next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: "resolvedPromise", done: false }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
test.drainMicrotasks();
|
||||
resolve("resolvedPromise");
|
||||
}, "yield-await-thenable-resolved");
|
||||
|
||||
// Yield a thenable which is rejected later
|
||||
testAsync(test => {
|
||||
test.plan(2);
|
||||
|
||||
let reject;
|
||||
let awaitedThenable = { then(resolveFn, rejectFn) { reject = rejectFn; } };
|
||||
async function* gen() {
|
||||
try {
|
||||
yield awaitedThenable;
|
||||
} catch (e) {
|
||||
test.equals("rejection", e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
gen().next().then(
|
||||
(iterResult) => {
|
||||
test.equals({ value: "rejection", done: true }, iterResult);
|
||||
},
|
||||
test.unexpectedRejection());
|
||||
|
||||
test.drainMicrotasks();
|
||||
reject("rejection");
|
||||
}, "yield-await-thenable-rejected");
|
@ -754,7 +754,7 @@ var failWithMessage;
|
||||
}
|
||||
|
||||
fail(expectedText, found) {
|
||||
message = formatFailureText(expectedText, found);
|
||||
let message = formatFailureText(expectedText, found);
|
||||
message += "\nin test:" + this.name_
|
||||
message += "\n" + Function.prototype.toString.apply(this.test_);
|
||||
eval("%AbortJS(message)");
|
||||
@ -773,6 +773,25 @@ var failWithMessage;
|
||||
eval("%AbortJS(message)");
|
||||
}
|
||||
|
||||
unexpectedRejection(details) {
|
||||
return (error) => {
|
||||
let message =
|
||||
"Failure: unexpected Promise rejection in test: " + this.name_;
|
||||
if (details) message += "\n @" + details;
|
||||
if (error instanceof Error) {
|
||||
message += "\n" + String(error.stack);
|
||||
} else {
|
||||
message += "\n" + String(error);
|
||||
}
|
||||
message += "\n\n" + Function.prototype.toString.apply(this.test_);
|
||||
eval("%AbortJS(message)");
|
||||
};
|
||||
}
|
||||
|
||||
drainMicrotasks() {
|
||||
eval("%RunMicrotasks()");
|
||||
}
|
||||
|
||||
done_() {
|
||||
if (this.expectedAsserts_ === -1) {
|
||||
let message = "Please call t.plan(count) to initialize test harness " +
|
||||
|
@ -504,36 +504,6 @@
|
||||
'built-ins/Array/prototype/splice/create-species-length-exceeding-integer-limit': [FAIL],
|
||||
'built-ins/Array/prototype/splice/throws-if-integer-limit-exceeded': [SKIP],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=5855
|
||||
'language/expressions/async-generator/named-yield-promise-reject-next': [FAIL],
|
||||
'language/expressions/async-generator/named-yield-promise-reject-next-catch': [FAIL],
|
||||
'language/expressions/async-generator/yield-promise-reject-next': [FAIL],
|
||||
'language/expressions/async-generator/yield-promise-reject-next-catch': [FAIL],
|
||||
'language/expressions/class/async-gen-method-static-yield-promise-reject-next': [FAIL],
|
||||
'language/expressions/class/async-gen-method-static-yield-promise-reject-next-catch': [FAIL],
|
||||
'language/expressions/class/async-gen-method-yield-promise-reject-next': [FAIL],
|
||||
'language/expressions/class/async-gen-method-yield-promise-reject-next-catch': [FAIL],
|
||||
'language/expressions/object/method-definition/async-gen-yield-promise-reject-next': [FAIL],
|
||||
'language/expressions/object/method-definition/async-gen-yield-promise-reject-next-catch': [FAIL],
|
||||
'language/statements/async-generator/yield-promise-reject-next': [FAIL],
|
||||
'language/statements/async-generator/yield-promise-reject-next-catch': [FAIL],
|
||||
'language/statements/class/async-gen-method-static-yield-promise-reject-next': [FAIL],
|
||||
'language/statements/class/async-gen-method-static-yield-promise-reject-next-catch': [FAIL],
|
||||
'language/statements/class/async-gen-method-yield-promise-reject-next': [FAIL],
|
||||
'language/statements/class/async-gen-method-yield-promise-reject-next-catch': [FAIL],
|
||||
'language/statements/for-await-of/async-func-decl-dstr-array-elem-init-let': [FAIL],
|
||||
'language/statements/for-await-of/async-func-decl-dstr-array-elem-put-let': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-init-let': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-put-let': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-elision-iter-abpt': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-get-err': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-nrml-close-null': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-rtrn-close': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-rtrn-close-err': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-rtrn-close-null': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-rest-rtrn-close-err': [PASS, FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-rest-rtrn-close-null': [FAIL],
|
||||
|
||||
# https://bugs.chromium.org/p/v8/issues/detail?id=6541
|
||||
'language/export/escaped-as-export-specifier': [FAIL],
|
||||
'language/export/escaped-from': [FAIL],
|
||||
@ -633,6 +603,20 @@
|
||||
'language/statements/for-await-of/async-gen-dstr-const-obj-ptrn-rest-nested-obj': [FAIL],
|
||||
'language/statements/for-await-of/async-func-dstr-const-obj-ptrn-rest-obj-own-property': [FAIL],
|
||||
|
||||
# Async Iteration https://github.com/tc39/test262/issues/1154
|
||||
'language/statements/for-await-of/async-func-decl-dstr-array-elem-init-let': [FAIL],
|
||||
'language/statements/for-await-of/async-func-decl-dstr-array-elem-put-let': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-init-let': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-put-let': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-elision-iter-abpt': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-get-err': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-nrml-close-null': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-rtrn-close-err': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-rtrn-close-null': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-rest-rtrn-close-null': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-list-rtrn-close': [FAIL],
|
||||
'language/statements/for-await-of/async-gen-decl-dstr-array-elem-trlg-iter-rest-rtrn-close-err': [PASS, FAIL],
|
||||
|
||||
############################ SKIPPED TESTS #############################
|
||||
|
||||
# These tests take a looong time to run.
|
||||
|
Loading…
Reference in New Issue
Block a user