Commit 76ab55e3 authored by caitp's avatar caitp Committed by Commit bot

[async-iteration] add support for for-await-of loops in Async Functions

When --harmony-async-iteration is enabled, it is now possible to
use the for-await-of loop, which uses the Async Iteration protocol
rather than the ordinary ES6 Iteration protocol.

the Async-from-Sync Iterator object is not implemented in this CL,
and so for-await-of loops will abort execution if the iterated object
does not have a Symbol.asyncIterator() method. Async-from-Sync
Iterators are implemented seperately in https://codereview.chromium.org/2645313003/

BUG=v8:5855, v8:4483
R=neis@chromium.org, littledan@chromium.org, adamk@chromium.org

Review-Url: https://codereview.chromium.org/2637403008
Cr-Commit-Position: refs/heads/master@{#43224}
parent 70105d5d
......@@ -2961,8 +2961,11 @@ class EmptyParentheses final : public Expression {
// (defined at https://tc39.github.io/ecma262/#sec-getiterator). Ignition
// desugars this into a LoadIC / JSLoadNamed, CallIC, and a type-check to
// validate return value of the Symbol.iterator() call.
enum class IteratorType { kNormal, kAsync };
class GetIterator final : public Expression {
public:
IteratorType hint() const { return hint_; }
Expression* iterable() const { return iterable_; }
void set_iterable(Expression* iterable) { iterable_ = iterable; }
......@@ -2972,6 +2975,10 @@ class GetIterator final : public Expression {
FeedbackSlotCache* cache) {
iterator_property_feedback_slot_ = spec->AddLoadICSlot();
iterator_call_feedback_slot_ = spec->AddCallICSlot();
if (hint() == IteratorType::kAsync) {
async_iterator_property_feedback_slot_ = spec->AddLoadICSlot();
async_iterator_call_feedback_slot_ = spec->AddCallICSlot();
}
}
FeedbackSlot IteratorPropertyFeedbackSlot() const {
......@@ -2982,15 +2989,26 @@ class GetIterator final : public Expression {
return iterator_call_feedback_slot_;
}
FeedbackSlot AsyncIteratorPropertyFeedbackSlot() const {
return async_iterator_property_feedback_slot_;
}
FeedbackSlot AsyncIteratorCallFeedbackSlot() const {
return async_iterator_call_feedback_slot_;
}
private:
friend class AstNodeFactory;
explicit GetIterator(Expression* iterable, int pos)
: Expression(pos, kGetIterator), iterable_(iterable) {}
explicit GetIterator(Expression* iterable, IteratorType hint, int pos)
: Expression(pos, kGetIterator), hint_(hint), iterable_(iterable) {}
IteratorType hint_;
Expression* iterable_;
FeedbackSlot iterator_property_feedback_slot_;
FeedbackSlot iterator_call_feedback_slot_;
FeedbackSlot async_iterator_property_feedback_slot_;
FeedbackSlot async_iterator_call_feedback_slot_;
};
// ----------------------------------------------------------------------------
......@@ -3206,6 +3224,11 @@ class AstNodeFactory final BASE_EMBEDDED {
return NULL;
}
ForOfStatement* NewForOfStatement(ZoneList<const AstRawString*>* labels,
int pos) {
return new (zone_) ForOfStatement(labels, pos);
}
ExpressionStatement* NewExpressionStatement(Expression* expression, int pos) {
return new (zone_) ExpressionStatement(expression, pos);
}
......@@ -3572,8 +3595,9 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) EmptyParentheses(pos);
}
GetIterator* NewGetIterator(Expression* iterable, int pos) {
return new (zone_) GetIterator(iterable, pos);
GetIterator* NewGetIterator(Expression* iterable, IteratorType hint,
int pos) {
return new (zone_) GetIterator(iterable, hint, pos);
}
Zone* zone() const { return zone_; }
......
......@@ -620,6 +620,13 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::LoadIteratorProperty(
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadAsyncIteratorProperty(
Register object, int feedback_slot) {
size_t name_index = AsyncIteratorSymbolConstantPoolEntry();
OutputLdaNamedProperty(object, name_index, feedback_slot);
return *this;
}
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreDataPropertyInLiteral(
Register object, Register name, DataPropertyInLiteralFlags flags,
int feedback_slot) {
......
......@@ -127,6 +127,9 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final
// Named load property of the @@iterator symbol.
BytecodeArrayBuilder& LoadIteratorProperty(Register object,
int feedback_slot);
// Named load property of the @@asyncIterator symbol.
BytecodeArrayBuilder& LoadAsyncIteratorProperty(Register object,
int feedback_slot);
// Store properties. Flag for NeedsSetFunctionName() should
// be in the accumulator.
......
......@@ -2973,21 +2973,64 @@ void BytecodeGenerator::VisitGetIterator(GetIterator* expr) {
VisitForAccumulatorValue(expr->iterable());
// Let method be GetMethod(obj, @@iterator).
builder()
->StoreAccumulatorInRegister(obj)
.LoadIteratorProperty(obj, feedback_index(load_slot))
.StoreAccumulatorInRegister(method);
// Let iterator be Call(method, obj).
builder()->Call(method, args, feedback_index(call_slot),
Call::NAMED_PROPERTY_CALL);
// If Type(iterator) is not Object, throw a TypeError exception.
BytecodeLabel no_type_error;
builder()->JumpIfJSReceiver(&no_type_error);
builder()->CallRuntime(Runtime::kThrowSymbolIteratorInvalid);
builder()->Bind(&no_type_error);
if (expr->hint() == IteratorType::kAsync) {
FeedbackSlot async_load_slot = expr->AsyncIteratorPropertyFeedbackSlot();
FeedbackSlot async_call_slot = expr->AsyncIteratorCallFeedbackSlot();
// Set method to GetMethod(obj, @@asyncIterator)
builder()->StoreAccumulatorInRegister(obj).LoadAsyncIteratorProperty(
obj, feedback_index(async_load_slot));
BytecodeLabel async_iterator_undefined, async_iterator_null, done;
// TODO(ignition): Add a single opcode for JumpIfNullOrUndefined
builder()->JumpIfUndefined(&async_iterator_undefined);
builder()->JumpIfNull(&async_iterator_null);
// Let iterator be Call(method, obj)
builder()->StoreAccumulatorInRegister(method).Call(
method, args, feedback_index(async_call_slot),
Call::NAMED_PROPERTY_CALL);
// If Type(iterator) is not Object, throw a TypeError exception.
builder()->JumpIfJSReceiver(&done);
builder()->CallRuntime(Runtime::kThrowSymbolAsyncIteratorInvalid);
builder()->Bind(&async_iterator_undefined);
builder()->Bind(&async_iterator_null);
// If method is undefined,
// Let syncMethod be GetMethod(obj, @@iterator)
builder()
->LoadIteratorProperty(obj, feedback_index(load_slot))
.StoreAccumulatorInRegister(method);
// Let syncIterator be Call(syncMethod, obj)
builder()->Call(method, args, feedback_index(call_slot),
Call::NAMED_PROPERTY_CALL);
// Return CreateAsyncFromSyncIterator(syncIterator)
// alias `method` register as it's no longer used
Register sync_iter = method;
builder()->StoreAccumulatorInRegister(sync_iter).CallRuntime(
Runtime::kInlineCreateAsyncFromSyncIterator, sync_iter);
builder()->Bind(&done);
} else {
// Let method be GetMethod(obj, @@iterator).
builder()
->StoreAccumulatorInRegister(obj)
.LoadIteratorProperty(obj, feedback_index(load_slot))
.StoreAccumulatorInRegister(method);
// Let iterator be Call(method, obj).
builder()->Call(method, args, feedback_index(call_slot),
Call::NAMED_PROPERTY_CALL);
// If Type(iterator) is not Object, throw a TypeError exception.
BytecodeLabel no_type_error;
builder()->JumpIfJSReceiver(&no_type_error);
builder()->CallRuntime(Runtime::kThrowSymbolIteratorInvalid);
builder()->Bind(&no_type_error);
}
}
void BytecodeGenerator::VisitThisFunction(ThisFunction* expr) {
......
......@@ -319,12 +319,11 @@ Handle<Object> ConstantArrayBuilder::Entry::ToHandle(Isolate* isolate) const {
return heap_number_->value();
case Tag::kScope:
return scope_->scope_info();
case Tag::kIteratorSymbol:
return isolate->factory()->iterator_symbol();
case Tag::kHomeObjectSymbol:
return isolate->factory()->home_object_symbol();
case Tag::kEmptyFixedArray:
return isolate->factory()->empty_fixed_array();
#define ENTRY_LOOKUP(Name, name) \
case Tag::k##Name: \
return isolate->factory()->name();
SINGLETON_CONSTANT_ENTRY_TYPES(ENTRY_LOOKUP);
#undef ENTRY_LOOKUP
}
UNREACHABLE();
return Handle<Object>::null();
......
......@@ -20,9 +20,10 @@ class AstValue;
namespace interpreter {
// Constant array entries that represent singletons.
#define SINGLETON_CONSTANT_ENTRY_TYPES(V) \
V(IteratorSymbol, iterator_symbol) \
V(HomeObjectSymbol, home_object_symbol) \
#define SINGLETON_CONSTANT_ENTRY_TYPES(V) \
V(IteratorSymbol, iterator_symbol) \
V(AsyncIteratorSymbol, async_iterator_symbol) \
V(HomeObjectSymbol, home_object_symbol) \
V(EmptyFixedArray, empty_fixed_array)
// A helper class for constructing constant arrays for the
......
......@@ -475,6 +475,8 @@ class ErrorUtils : public AllStatic {
T(StrictCannotCreateProperty, "Cannot create property '%' on % '%'") \
T(SymbolIteratorInvalid, \
"Result of the Symbol.iterator method is not an object") \
T(SymbolAsyncIteratorInvalid, \
"Result of the Symbol.asyncIterator method is not an object") \
T(SymbolKeyFor, "% is not a symbol") \
T(SymbolToNumber, "Cannot convert a Symbol value to a number") \
T(SymbolToString, "Cannot convert a Symbol value to a string") \
......
......@@ -225,7 +225,8 @@ class ParserBase {
allow_harmony_trailing_commas_(false),
allow_harmony_class_fields_(false),
allow_harmony_object_rest_spread_(false),
allow_harmony_dynamic_import_(false) {}
allow_harmony_dynamic_import_(false),
allow_harmony_async_iteration_(false) {}
#define ALLOW_ACCESSORS(name) \
bool allow_##name() const { return allow_##name##_; } \
......@@ -240,6 +241,7 @@ class ParserBase {
ALLOW_ACCESSORS(harmony_class_fields);
ALLOW_ACCESSORS(harmony_object_rest_spread);
ALLOW_ACCESSORS(harmony_dynamic_import);
ALLOW_ACCESSORS(harmony_async_iteration);
#undef ALLOW_ACCESSORS
......@@ -1292,6 +1294,8 @@ class ParserBase {
ForInfo* for_info, BlockState* for_state,
ZoneList<const AstRawString*>* labels,
bool* ok);
StatementT ParseForAwaitStatement(ZoneList<const AstRawString*>* labels,
bool* ok);
bool IsNextLetKeyword();
bool IsTrivialExpression();
......@@ -1488,6 +1492,7 @@ class ParserBase {
bool allow_harmony_class_fields_;
bool allow_harmony_object_rest_spread_;
bool allow_harmony_dynamic_import_;
bool allow_harmony_async_iteration_;
friend class DiscardableZoneScope;
};
......@@ -4795,6 +4800,10 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement(
case Token::WHILE:
return ParseWhileStatement(labels, ok);
case Token::FOR:
if (V8_UNLIKELY(allow_harmony_async_iteration() && is_async_function() &&
PeekAhead() == Token::AWAIT)) {
return ParseForAwaitStatement(labels, ok);
}
return ParseForStatement(labels, ok);
case Token::CONTINUE:
case Token::BREAK:
......@@ -5700,6 +5709,151 @@ void ParserBase<Impl>::MarkLoopVariableAsAssigned(Scope* scope, Variable* var) {
}
}
template <typename Impl>
typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
ZoneList<const AstRawString*>* labels, bool* ok) {
// for await '(' ForDeclaration of AssignmentExpression ')'
DCHECK(is_async_function());
DCHECK(allow_harmony_async_iteration());
int stmt_pos = peek_position();
ForInfo for_info(this);
for_info.mode = ForEachStatement::ITERATE;
// Create an in-between scope for let-bound iteration variables.
BlockState for_state(zone(), &scope_state_);
Expect(Token::FOR, CHECK_OK);
Expect(Token::AWAIT, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
for_state.set_start_position(scanner()->location().beg_pos);
for_state.set_is_hidden();
auto loop = factory()->NewForOfStatement(labels, stmt_pos);
typename Types::Target target(this, loop);
ExpressionT each_variable = impl()->EmptyExpression();
bool has_declarations = false;
if (peek() == Token::VAR || peek() == Token::CONST ||
(peek() == Token::LET && IsNextLetKeyword())) {
// The initializer contains declarations
// 'for' 'await' '(' ForDeclaration 'of' AssignmentExpression ')'
// Statement
// 'for' 'await' '(' 'var' ForBinding 'of' AssignmentExpression ')'
// Statement
has_declarations = true;
ParseVariableDeclarations(kForStatement, &for_info.parsing_result, nullptr,
CHECK_OK);
for_info.position = scanner()->location().beg_pos;
// Only a single declaration is allowed in for-await-of loops
if (for_info.parsing_result.declarations.length() != 1) {
impl()->ReportMessageAt(for_info.parsing_result.bindings_loc,
MessageTemplate::kForInOfLoopMultiBindings,
"for-await-of");
*ok = false;
return impl()->NullStatement();
}
// for-await-of's declarations do not permit initializers.
if (for_info.parsing_result.first_initializer_loc.IsValid()) {
impl()->ReportMessageAt(for_info.parsing_result.first_initializer_loc,
MessageTemplate::kForInOfLoopInitializer,
"for-await-of");
*ok = false;
return impl()->NullStatement();
}
} else {
// The initializer does not contain declarations.
// 'for' 'await' '(' LeftHandSideExpression 'of' AssignmentExpression ')'
// Statement
int lhs_beg_pos = peek_position();
ExpressionClassifier classifier(this);
ExpressionT lhs = each_variable = ParseLeftHandSideExpression(CHECK_OK);
int lhs_end_pos = scanner()->location().end_pos;
if (lhs->IsArrayLiteral() || lhs->IsObjectLiteral()) {
ValidateAssignmentPattern(CHECK_OK);
} else {
impl()->RewriteNonPattern(CHECK_OK);
each_variable = impl()->CheckAndRewriteReferenceExpression(
lhs, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor,
kSyntaxError, CHECK_OK);
}
}
ExpectContextualKeyword(CStrVector("of"), CHECK_OK);
int each_keyword_pos = scanner()->location().beg_pos;
const bool kAllowIn = true;
ExpressionT iterable = impl()->EmptyExpression();
{
ExpressionClassifier classifier(this);
iterable = ParseAssignmentExpression(kAllowIn, CHECK_OK);
impl()->RewriteNonPattern(CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
StatementT final_loop = impl()->NullStatement();
{
ReturnExprScope no_tail_calls(function_state_,
ReturnExprContext::kInsideForInOfBody);
BlockState block_state(zone(), &scope_state_);
block_state.set_start_position(scanner()->location().beg_pos);
const bool kDisallowLabelledFunctionStatement = true;
StatementT body = ParseScopedStatement(
nullptr, kDisallowLabelledFunctionStatement, CHECK_OK);
block_state.set_end_position(scanner()->location().end_pos);
if (has_declarations) {
BlockT body_block = impl()->NullBlock();
impl()->DesugarBindingInForEachStatement(&for_info, &body_block,
&each_variable, CHECK_OK);
body_block->statements()->Add(body, zone());
body_block->set_scope(block_state.FinalizedBlockScope());
for_state.set_end_position(scanner()->location().end_pos);
const bool finalize = true;
final_loop = impl()->InitializeForOfStatement(
loop, each_variable, iterable, body_block, finalize,
IteratorType::kAsync, each_keyword_pos);
} else {
const bool finalize = true;
final_loop = impl()->InitializeForOfStatement(
loop, each_variable, iterable, body, finalize, IteratorType::kAsync,
each_keyword_pos);
Scope* for_scope = for_state.FinalizedBlockScope();
DCHECK_NULL(for_scope);
USE(for_scope);
Scope* block_scope = block_state.FinalizedBlockScope();
DCHECK_NULL(block_scope);
USE(block_scope);
return final_loop;
}
}
DCHECK(has_declarations);
BlockT init_block =
impl()->CreateForEachStatementTDZ(impl()->NullBlock(), for_info, ok);
for_state.set_end_position(scanner()->location().end_pos);
Scope* for_scope = for_state.FinalizedBlockScope();
// Parsed for-in loop w/ variable declarations.
if (!impl()->IsNullStatement(init_block)) {
init_block->statements()->Add(final_loop, zone());
init_block->set_scope(for_scope);
return init_block;
}
DCHECK_NULL(for_scope);
return final_loop;
}
template <typename Impl>
void ParserBase<Impl>::ObjectLiteralChecker::CheckDuplicateProto(
Token::Value property) {
......
......@@ -554,6 +554,7 @@ Parser::Parser(ParseInfo* info)
set_allow_harmony_class_fields(FLAG_harmony_class_fields);
set_allow_harmony_object_rest_spread(FLAG_harmony_object_rest_spread);
set_allow_harmony_dynamic_import(FLAG_harmony_dynamic_import);
set_allow_harmony_async_iteration(FLAG_harmony_async_iteration);
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
++feature) {
use_counts_[feature] = 0;
......@@ -1822,10 +1823,16 @@ void Parser::CreateFunctionNameAssignment(
}
}
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
// [if (IteratorType == kNormal)]
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
// [else if (IteratorType == kAsync)]
// !%_IsJSReceiver(result = Await(iterator.next())) &&
// %ThrowIteratorResultNotAnObject(result)
// [endif]
Expression* Parser::BuildIteratorNextResult(Expression* iterator,
Variable* result, int pos) {
Variable* result, IteratorType type,
int pos) {
Expression* next_literal = factory()->NewStringLiteral(
ast_value_factory()->next_string(), kNoSourcePosition);
Expression* next_property =
......@@ -1834,6 +1841,9 @@ Expression* Parser::BuildIteratorNextResult(Expression* iterator,
new (zone()) ZoneList<Expression*>(0, zone());
Expression* next_call =
factory()->NewCall(next_property, next_arguments, pos);
if (type == IteratorType::kAsync) {
next_call = RewriteAwaitExpression(next_call, pos);
}
Expression* result_proxy = factory()->NewVariableProxy(result);
Expression* left =
factory()->NewAssignment(Token::ASSIGN, result_proxy, next_call, pos);
......@@ -1868,7 +1878,7 @@ Statement* Parser::InitializeForEachStatement(ForEachStatement* stmt,
if (for_of != NULL) {
const bool finalize = true;
return InitializeForOfStatement(for_of, each, subject, body, finalize,
each_keyword_pos);
IteratorType::kNormal, each_keyword_pos);
} else {
if (each->IsArrayLiteral() || each->IsObjectLiteral()) {
Variable* temp = NewTemporary(ast_value_factory()->empty_string());
......@@ -2021,17 +2031,14 @@ Block* Parser::CreateForEachStatementTDZ(Block* init_block,
return init_block;
}
Statement* Parser::InitializeForOfStatement(ForOfStatement* for_of,
Expression* each,
Expression* iterable,
Statement* body, bool finalize,
int next_result_pos) {
Statement* Parser::InitializeForOfStatement(
ForOfStatement* for_of, Expression* each, Expression* iterable,
Statement* body, bool finalize, IteratorType type, int next_result_pos) {
// Create the auxiliary expressions needed for iterating over the iterable,
// and initialize the given ForOfStatement with them.
// If finalize is true, also instrument the loop with code that performs the
// proper ES6 iterator finalization. In that case, the result is not
// immediately a ForOfStatement.
const int nopos = kNoSourcePosition;
auto avfactory = ast_value_factory();
......@@ -2039,22 +2046,27 @@ Statement* Parser::InitializeForOfStatement(ForOfStatement* for_of,
Variable* result = NewTemporary(avfactory->dot_result_string());
Variable* completion = NewTemporary(avfactory->empty_string());
// iterator = GetIterator(iterable)
// iterator = GetIterator(iterable, type)
Expression* assign_iterator;
{
assign_iterator = factory()->NewAssignment(
Token::ASSIGN, factory()->NewVariableProxy(iterator),
factory()->NewGetIterator(iterable, iterable->position()),
factory()->NewGetIterator(iterable, type, iterable->position()),
iterable->position());
}
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
// [if (IteratorType == kNormal)]
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
// [else if (IteratorType == kAsync)]
// !%_IsJSReceiver(result = Await(iterator.next())) &&
// %ThrowIteratorResultNotAnObject(result)
// [endif]
Expression* next_result;
{
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
next_result =
BuildIteratorNextResult(iterator_proxy, result, next_result_pos);
BuildIteratorNextResult(iterator_proxy, result, type, next_result_pos);
}
// result.done
......@@ -2142,7 +2154,8 @@ Statement* Parser::InitializeForOfStatement(ForOfStatement* for_of,
for_of->Initialize(body, iterator, assign_iterator, next_result, result_done,
assign_each);
return finalize ? FinalizeForOfStatement(for_of, completion, nopos) : for_of;
return finalize ? FinalizeForOfStatement(for_of, completion, type, nopos)
: for_of;
}
Statement* Parser::DesugarLexicalBindingsInForStatement(
......@@ -2794,6 +2807,7 @@ Parser::LazyParsingResult Parser::SkipFunction(
SET_ALLOW(harmony_class_fields);
SET_ALLOW(harmony_object_rest_spread);
SET_ALLOW(harmony_dynamic_import);
SET_ALLOW(harmony_async_iteration);
#undef SET_ALLOW
}
// Aborting inner function preparsing would leave scopes in an inconsistent
......@@ -4050,12 +4064,11 @@ Expression* Parser::RewriteSpreads(ArrayLiteral* lit) {
kNoSourcePosition);
}
// for (each of spread) %AppendElement($R, each)
ForEachStatement* loop = factory()->NewForEachStatement(
ForEachStatement::ITERATE, nullptr, kNoSourcePosition);
ForOfStatement* loop =
factory()->NewForOfStatement(nullptr, kNoSourcePosition);
const bool finalize = false;
InitializeForOfStatement(loop->AsForOfStatement(),
factory()->NewVariableProxy(each), subject,
append_body, finalize);
InitializeForOfStatement(loop, factory()->NewVariableProxy(each), subject,
append_body, finalize, IteratorType::kNormal);
do_block->statements()->Add(loop, zone());
}
}
......@@ -4247,7 +4260,8 @@ Expression* Parser::RewriteYieldStar(Expression* generator,
Variable* var_iterator = NewTemporary(ast_value_factory()->empty_string());
Statement* get_iterator;
{
Expression* iterator = factory()->NewGetIterator(iterable, nopos);
Expression* iterator =
factory()->NewGetIterator(iterable, IteratorType::kNormal, nopos);
Expression* iterator_proxy = factory()->NewVariableProxy(var_iterator);
Expression* assignment = factory()->NewAssignment(
Token::ASSIGN, iterator_proxy, iterator, nopos);
......@@ -4329,7 +4343,8 @@ Expression* Parser::RewriteYieldStar(Expression* generator,
Block* then = factory()->NewBlock(nullptr, 4 + 1, false, nopos);
BuildIteratorCloseForCompletion(
scope(), then->statements(), var_iterator,
factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos));
factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos),
IteratorType::kNormal);
then->statements()->Add(throw_call, zone());
check_throw = factory()->NewIfStatement(
condition, then, factory()->NewEmptyStatement(nopos), nopos);
......@@ -4696,7 +4711,8 @@ void Parser::BuildIteratorClose(ZoneList<Statement*>* statements,
void Parser::FinalizeIteratorUse(Scope* use_scope, Variable* completion,
Expression* condition, Variable* iter,
Block* iterator_use, Block* target) {
Block* iterator_use, Block* target,
IteratorType type) {
//
// This function adds two statements to [target], corresponding to the
// following code:
......@@ -4752,8 +4768,8 @@ void Parser::FinalizeIteratorUse(Scope* use_scope, Variable* completion,
{
Block* block = factory()->NewBlock(nullptr, 2, true, nopos);
Expression* proxy = factory()->NewVariableProxy(completion);
BuildIteratorCloseForCompletion(use_scope, block->statements(), iter,
proxy);
BuildIteratorCloseForCompletion(use_scope, block->statements(), iter, proxy,
type);
DCHECK(block->statements()->length() == 2);
maybe_close = factory()->NewBlock(nullptr, 1, true, nopos);
......@@ -4812,7 +4828,8 @@ void Parser::FinalizeIteratorUse(Scope* use_scope, Variable* completion,
void Parser::BuildIteratorCloseForCompletion(Scope* scope,
ZoneList<Statement*>* statements,
Variable* iterator,
Expression* completion) {
Expression* completion,
IteratorType type) {
//
// This function adds two statements to [statements], corresponding to the
// following code:
......@@ -4823,9 +4840,17 @@ void Parser::BuildIteratorCloseForCompletion(Scope* scope,
// if (!IS_CALLABLE(iteratorReturn)) {
// throw MakeTypeError(kReturnMethodNotCallable);
// }
// try { %_Call(iteratorReturn, iterator) } catch (_) { }
// [if (IteratorType == kAsync)]
// try { Await(%_Call(iteratorReturn, iterator) } catch (_) { }
// [else]
// try { %_Call(iteratorReturn, iterator) } catch (_) { }
// [endif]
// } else {
// let output = %_Call(iteratorReturn, iterator);
// [if (IteratorType == kAsync)]
// let output = Await(%_Call(iteratorReturn, iterator));
// [else]
// let output = %_Call(iteratorReturn, iterator);
// [endif]
// if (!IS_RECEIVER(output)) {
// %ThrowIterResultNotAnObject(output);
// }
......@@ -4870,6 +4895,10 @@ void Parser::BuildIteratorCloseForCompletion(Scope* scope,
Expression* call =
factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos);
if (type == IteratorType::kAsync) {
call = RewriteAwaitExpression(call, nopos);
}
Block* try_block = factory()->NewBlock(nullptr, 1, false, nopos);
try_block->statements()->Add(factory()->NewExpressionStatement(call, nopos),
zone());
......@@ -4899,6 +4928,9 @@ void Parser::BuildIteratorCloseForCompletion(Scope* scope,
args->Add(factory()->NewVariableProxy(iterator), zone());
Expression* call =
factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos);
if (type == IteratorType::kAsync) {
call = RewriteAwaitExpression(call, nopos);
}
Expression* output_proxy = factory()->NewVariableProxy(var_output);
Expression* assignment =
......@@ -4969,7 +5001,8 @@ void Parser::BuildIteratorCloseForCompletion(Scope* scope,
}
Statement* Parser::FinalizeForOfStatement(ForOfStatement* loop,
Variable* var_completion, int pos) {
Variable* var_completion,
IteratorType type, int pos) {
//
// This function replaces the loop with the following wrapping:
//
......@@ -5013,7 +5046,7 @@ Statement* Parser::FinalizeForOfStatement(ForOfStatement* loop,
DCHECK_EQ(scope()->scope_type(), BLOCK_SCOPE);
FinalizeIteratorUse(loop_scope, var_completion, closing_condition,
loop->iterator(), try_block, final_loop);
loop->iterator(), try_block, final_loop, type);
}
return final_loop;
......
......@@ -462,10 +462,15 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
DEFINE_AST_VISITOR_MEMBERS_WITHOUT_STACKOVERFLOW()
};
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
// [if (IteratorType == kAsync)]
// !%_IsJSReceiver(result = Await(iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
// [else]
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
// [endif]
Expression* BuildIteratorNextResult(Expression* iterator, Variable* result,
int pos);
IteratorType type, int pos);
// Initialize the components of a for-in / for-of statement.
Statement* InitializeForEachStatement(ForEachStatement* stmt,
......@@ -473,8 +478,9 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Statement* body, int each_keyword_pos);
Statement* InitializeForOfStatement(ForOfStatement* stmt, Expression* each,
Expression* iterable, Statement* body,
bool finalize,
bool finalize, IteratorType type,
int next_result_pos = kNoSourcePosition);
Block* RewriteForVarInLegacy(const ForInfo& for_info);
void DesugarBindingInForEachStatement(ForInfo* for_info, Block** body_block,
Expression** each_variable, bool* ok);
......@@ -646,16 +652,18 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
void FinalizeIteratorUse(Scope* use_scope, Variable* completion,
Expression* condition, Variable* iter,
Block* iterator_use, Block* result);
Block* iterator_use, Block* result,
IteratorType type);
Statement* FinalizeForOfStatement(ForOfStatement* loop, Variable* completion,
int pos);
IteratorType type, int pos);
void BuildIteratorClose(ZoneList<Statement*>* statements, Variable* iterator,
Variable* input, Variable* output);
void BuildIteratorCloseForCompletion(Scope* scope,
ZoneList<Statement*>* statements,
Variable* iterator,
Expression* completion);
Expression* completion,
IteratorType type);
Statement* CheckCallable(Variable* var, Expression* error, int pos);
V8_INLINE Expression* RewriteAwaitExpression(Expression* value, int pos);
......
......@@ -430,8 +430,9 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
DCHECK(block_->ignore_completion_value());
auto temp = *temp_var = CreateTempVar(current_value_);
auto iterator = CreateTempVar(factory()->NewGetIterator(
factory()->NewVariableProxy(temp), kNoSourcePosition));
auto iterator = CreateTempVar(
factory()->NewGetIterator(factory()->NewVariableProxy(temp),
IteratorType::kNormal, kNoSourcePosition));
auto done =
CreateTempVar(factory()->NewBooleanLiteral(false, kNoSourcePosition));
auto result = CreateTempVar();
......@@ -517,7 +518,7 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult(
factory()->NewVariableProxy(iterator), result,
kNoSourcePosition),
IteratorType::kNormal, kNoSourcePosition),
kNoSourcePosition),
zone());
next_block->statements()->Add(inner_if, zone());
......@@ -588,7 +589,7 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
// result = IteratorNext(iterator);
Statement* get_next = factory()->NewExpressionStatement(
parser_->BuildIteratorNextResult(factory()->NewVariableProxy(iterator),
result, nopos),
result, IteratorType::kNormal, nopos),
nopos);
// %AppendElement(array, result.value);
......@@ -657,7 +658,7 @@ void Parser::PatternRewriter::VisitArrayLiteral(ArrayLiteral* node,
Token::NOT, factory()->NewVariableProxy(done), nopos);
parser_->FinalizeIteratorUse(scope(), completion, closing_condition, iterator,
block_, target);
block_, target, IteratorType::kNormal);
block_ = target;
}
......
......@@ -765,6 +765,11 @@ class PreParserFactory {
return PreParserStatement::Default();
}
PreParserStatement NewForOfStatement(ZoneList<const AstRawString*>* labels,
int pos) {
return PreParserStatement::Default();
}
PreParserExpression NewCallRuntime(Runtime::FunctionId id,
ZoneList<PreParserExpression>* arguments,
int pos) {
......@@ -1317,6 +1322,14 @@ class PreParser : public ParserBase<PreParser> {
return stmt;
}
V8_INLINE PreParserStatement InitializeForOfStatement(
PreParserStatement stmt, PreParserExpression each,
PreParserExpression iterable, PreParserStatement body, bool finalize,
IteratorType type, int next_result_pos = kNoSourcePosition) {
MarkExpressionAsAssigned(each);
return stmt;
}
V8_INLINE PreParserStatement RewriteForVarInLegacy(const ForInfo& for_info) {
return PreParserStatement::Null();
}
......
......@@ -88,6 +88,13 @@ RUNTIME_FUNCTION(Runtime_ThrowStackOverflow) {
return isolate->StackOverflow();
}
RUNTIME_FUNCTION(Runtime_ThrowSymbolAsyncIteratorInvalid) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewTypeError(MessageTemplate::kSymbolAsyncIteratorInvalid));
}
RUNTIME_FUNCTION(Runtime_ThrowTypeError) {
HandleScope scope(isolate);
DCHECK_LE(1, args.length());
......@@ -503,5 +510,12 @@ RUNTIME_FUNCTION(Runtime_AllowDynamicFunction) {
Builtins::AllowDynamicFunction(isolate, target, global_proxy));
}
RUNTIME_FUNCTION(Runtime_CreateAsyncFromSyncIterator) {
// TODO(caitp): split AsyncFromSyncIterator functionality out of
// https://codereview.chromium.org/2622833002
UNREACHABLE();
return isolate->heap()->undefined_value();
}
} // namespace internal
} // namespace v8
......@@ -289,6 +289,7 @@ namespace internal {
F(AllocateSeqOneByteString, 1, 1) \
F(AllocateSeqTwoByteString, 1, 1) \
F(CheckIsBootstrapping, 0, 1) \
F(CreateAsyncFromSyncIterator, 1, 1) \
F(CreateListFromArrayLike, 1, 1) \
F(GetAndResetRuntimeCallStats, -1 /* <= 2 */, 1) \
F(ExportFromRuntime, 1, 1) \
......@@ -324,6 +325,7 @@ namespace internal {
F(ThrowNotGeneric, 1, 1) \
F(ThrowReferenceError, 1, 1) \
F(ThrowStackOverflow, 0, 1) \
F(ThrowSymbolAsyncIteratorInvalid, 0, 1) \
F(ThrowTypeError, -1 /* >= 1 */, 1) \
F(ThrowUndefinedOrNullToObject, 1, 1) \
F(Typeof, 1, 1) \
......
......@@ -1262,6 +1262,7 @@ enum ParserFlag {
kAllowHarmonyClassFields,
kAllowHarmonyObjectRestSpread,
kAllowHarmonyDynamicImport,
kAllowHarmonyAsyncIteration,
};
enum ParserSyncTestResult {
......@@ -1280,6 +1281,7 @@ void SetGlobalFlags(i::EnumSet<ParserFlag> flags) {
i::FLAG_harmony_object_rest_spread =
flags.Contains(kAllowHarmonyObjectRestSpread);
i::FLAG_harmony_dynamic_import = flags.Contains(kAllowHarmonyDynamicImport);
i::FLAG_harmony_async_iteration = flags.Contains(kAllowHarmonyAsyncIteration);
}
void SetParserFlags(i::PreParser* parser, i::EnumSet<ParserFlag> flags) {
......@@ -1296,6 +1298,8 @@ void SetParserFlags(i::PreParser* parser, i::EnumSet<ParserFlag> flags) {
flags.Contains(kAllowHarmonyObjectRestSpread));
parser->set_allow_harmony_dynamic_import(
flags.Contains(kAllowHarmonyDynamicImport));
parser->set_allow_harmony_async_iteration(
flags.Contains(kAllowHarmonyAsyncIteration));
}
void TestParserSyncWithFlags(i::Handle<i::String> source,
......@@ -9466,3 +9470,323 @@ TEST(EscapedStrictReservedWord) {
RunParserSyncTest(context_data, statement_data, kSuccess);
}
TEST(ForAwaitOf) {
// clang-format off
const char* context_data[][2] = {
{ "async function f() { for await ", " ; }" },
{ "async function f() { for await ", " { } }" },
{ "async function f() { 'use strict'; for await ", " ; }" },
{ "async function f() { 'use strict'; for await ", " { } }" },
{ "async function f() { for\nawait ", " ; }" },
{ "async function f() { for\nawait ", " { } }" },
{ "async function f() { 'use strict'; for\nawait ", " ; }" },
{ "async function f() { 'use strict'; for\nawait ", " { } }" },
{ "async function f() { 'use strict'; for\nawait ", " { } }" },
{ "async function f() { for await\n", " ; }" },
{ "async function f() { for await\n", " { } }" },
{ "async function f() { 'use strict'; for await\n", " ; }" },
{ "async function f() { 'use strict'; for await\n", " { } }" },
{ NULL, NULL }
};
const char* context_data2[][2] = {
{ "async function f() { let a; for await ", " ; }" },
{ "async function f() { let a; for await ", " { } }" },
{ "async function f() { 'use strict'; let a; for await ", " ; }" },
{ "async function f() { 'use strict'; let a; for await ", " { } }" },
{ "async function f() { let a; for\nawait ", " ; }" },
{ "async function f() { let a; for\nawait ", " { } }" },
{ "async function f() { 'use strict'; let a; for\nawait ", " ; }" },
{ "async function f() { 'use strict'; let a; for\nawait ", " { } }" },
{ "async function f() { 'use strict'; let a; for\nawait ", " { } }" },
{ "async function f() { let a; for await\n", " ; }" },
{ "async function f() { let a; for await\n", " { } }" },
{ "async function f() { 'use strict'; let a; for await\n", " ; }" },
{ "async function f() { 'use strict'; let a; for await\n", " { } }" },
{ NULL, NULL }
};
const char* expr_data[] = {
// Primary Expressions
"(a of [])",
"(a.b of [])",
"([a] of [])",
"([a = 1] of [])",
"([a = 1, ...b] of [])",
"({a} of [])",
"({a: a} of [])",
"({'a': a} of [])",
"({\"a\": a} of [])",
"({[Symbol.iterator]: a} of [])",
"({0: a} of [])",
"({a = 1} of [])",
"({a: a = 1} of [])",
"({'a': a = 1} of [])",
"({\"a\": a = 1} of [])",
"({[Symbol.iterator]: a = 1} of [])",
"({0: a = 1} of [])",
NULL
};
const char* var_data[] = {
// VarDeclarations
"(var a of [])",
"(var [a] of [])",
"(var [a = 1] of [])",
"(var [a = 1, ...b] of [])",
"(var {a} of [])",
"(var {a: a} of [])",
"(var {'a': a} of [])",
"(var {\"a\": a} of [])",
"(var {[Symbol.iterator]: a} of [])",
"(var {0: a} of [])",
"(var {a = 1} of [])",
"(var {a: a = 1} of [])",
"(var {'a': a = 1} of [])",
"(var {\"a\": a = 1} of [])",
"(var {[Symbol.iterator]: a = 1} of [])",
"(var {0: a = 1} of [])",
NULL
};
const char* lexical_data[] = {
// LexicalDeclartions
"(let a of [])",
"(let [a] of [])",
"(let [a = 1] of [])",
"(let [a = 1, ...b] of [])",
"(let {a} of [])",
"(let {a: a} of [])",
"(let {'a': a} of [])",
"(let {\"a\": a} of [])",
"(let {[Symbol.iterator]: a} of [])",
"(let {0: a} of [])",
"(let {a = 1} of [])",
"(let {a: a = 1} of [])",
"(let {'a': a = 1} of [])",
"(let {\"a\": a = 1} of [])",
"(let {[Symbol.iterator]: a = 1} of [])",
"(let {0: a = 1} of [])",
"(const a of [])",
"(const [a] of [])",
"(const [a = 1] of [])",
"(const [a = 1, ...b] of [])",
"(const {a} of [])",
"(const {a: a} of [])",
"(const {'a': a} of [])",
"(const {\"a\": a} of [])",
"(const {[Symbol.iterator]: a} of [])",
"(const {0: a} of [])",
"(const {a = 1} of [])",
"(const {a: a = 1} of [])",
"(const {'a': a = 1} of [])",
"(const {\"a\": a = 1} of [])",
"(const {[Symbol.iterator]: a = 1} of [])",
"(const {0: a = 1} of [])",
NULL
};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonyAsyncIteration};
RunParserSyncTest(context_data, expr_data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(context_data2, expr_data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(context_data, var_data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
// TODO(marja): PreParser doesn't report early errors.
// (https://bugs.chromium.org/p/v8/issues/detail?id=2728)
// RunParserSyncTest(context_data2, var_data, kError, NULL, 0, always_flags,
// arraysize(always_flags));
RunParserSyncTest(context_data, lexical_data, kSuccess, NULL, 0, always_flags,
arraysize(always_flags));
RunParserSyncTest(context_data2, lexical_data, kSuccess, NULL, 0,
always_flags, arraysize(always_flags));
}
TEST(ForAwaitOfErrors) {
// clang-format off
const char* context_data[][2] = {
{ "async function f() { for await ", " ; }" },
{ "async function f() { for await ", " { } }" },
{ "async function f() { 'use strict'; for await ", " ; }" },
{ "async function f() { 'use strict'; for await ", " { } }" },
{ NULL, NULL }
};
const char* data[] = {
// Primary Expressions
"(a = 1 of [])",
"(a = 1) of [])",
"(a.b = 1 of [])",
"((a.b = 1) of [])",
"([a] = 1 of [])",
"(([a] = 1) of [])",
"([a = 1] = 1 of [])",
"(([a = 1] = 1) of [])",
"([a = 1 = 1, ...b] = 1 of [])",
"(([a = 1 = 1, ...b] = 1) of [])",
"({a} = 1 of [])",
"(({a} = 1) of [])",
"({a: a} = 1 of [])",
"(({a: a} = 1) of [])",
"({'a': a} = 1 of [])",
"(({'a': a} = 1) of [])",
"({\"a\": a} = 1 of [])",
"(({\"a\": a} = 1) of [])",
"({[Symbol.iterator]: a} = 1 of [])",
"(({[Symbol.iterator]: a} = 1) of [])",
"({0: a} = 1 of [])",
"(({0: a} = 1) of [])",
"({a = 1} = 1 of [])",
"(({a = 1} = 1) of [])",
"({a: a = 1} = 1 of [])",
"(({a: a = 1} = 1) of [])",
"({'a': a = 1} = 1 of [])",
"(({'a': a = 1} = 1) of [])",
"({\"a\": a = 1} = 1 of [])",
"(({\"a\": a = 1} = 1) of [])",
"({[Symbol.iterator]: a = 1} = 1 of [])",
"(({[Symbol.iterator]: a = 1} = 1) of [])",
"({0: a = 1} = 1 of [])",
"(({0: a = 1} = 1) of [])",
"(function a() {} of [])",
"([1] of [])",
"({a: 1} of [])"
// VarDeclarations
"(var a = 1 of [])",
"(var a, b of [])",
"(var [a] = 1 of [])",
"(var [a], b of [])",
"(var [a = 1] = 1 of [])",
"(var [a = 1], b of [])",
"(var [a = 1 = 1, ...b] of [])",
"(var [a = 1, ...b], c of [])",
"(var {a} = 1 of [])",
"(var {a}, b of [])",
"(var {a: a} = 1 of [])",
"(var {a: a}, b of [])",
"(var {'a': a} = 1 of [])",
"(var {'a': a}, b of [])",
"(var {\"a\": a} = 1 of [])",
"(var {\"a\": a}, b of [])",
"(var {[Symbol.iterator]: a} = 1 of [])",
"(var {[Symbol.iterator]: a}, b of [])",
"(var {0: a} = 1 of [])",
"(var {0: a}, b of [])",
"(var {a = 1} = 1 of [])",
"(var {a = 1}, b of [])",
"(var {a: a = 1} = 1 of [])",
"(var {a: a = 1}, b of [])",
"(var {'a': a = 1} = 1 of [])",
"(var {'a': a = 1}, b of [])",
"(var {\"a\": a = 1} = 1 of [])",
"(var {\"a\": a = 1}, b of [])",
"(var {[Symbol.iterator]: a = 1} = 1 of [])",
"(var {[Symbol.iterator]: a = 1}, b of [])",
"(var {0: a = 1} = 1 of [])",
"(var {0: a = 1}, b of [])",
// LexicalDeclartions
"(let a = 1 of [])",
"(let a, b of [])",
"(let [a] = 1 of [])",
"(let [a], b of [])",
"(let [a = 1] = 1 of [])",
"(let [a = 1], b of [])",
"(let [a = 1, ...b] = 1 of [])",
"(let [a = 1, ...b], c of [])",
"(let {a} = 1 of [])",
"(let {a}, b of [])",
"(let {a: a} = 1 of [])",
"(let {a: a}, b of [])",
"(let {'a': a} = 1 of [])",
"(let {'a': a}, b of [])",
"(let {\"a\": a} = 1 of [])",
"(let {\"a\": a}, b of [])",
"(let {[Symbol.iterator]: a} = 1 of [])",
"(let {[Symbol.iterator]: a}, b of [])",
"(let {0: a} = 1 of [])",
"(let {0: a}, b of [])",
"(let {a = 1} = 1 of [])",
"(let {a = 1}, b of [])",
"(let {a: a = 1} = 1 of [])",
"(let {a: a = 1}, b of [])",
"(let {'a': a = 1} = 1 of [])",
"(let {'a': a = 1}, b of [])",
"(let {\"a\": a = 1} = 1 of [])",
"(let {\"a\": a = 1}, b of [])",
"(let {[Symbol.iterator]: a = 1} = 1 of [])",
"(let {[Symbol.iterator]: a = 1}, b of [])",
"(let {0: a = 1} = 1 of [])",
"(let {0: a = 1}, b of [])",
"(const a = 1 of [])",
"(const a, b of [])",
"(const [a] = 1 of [])",
"(const [a], b of [])",
"(const [a = 1] = 1 of [])",
"(const [a = 1], b of [])",
"(const [a = 1, ...b] = 1 of [])",
"(const [a = 1, ...b], b of [])",
"(const {a} = 1 of [])",
"(const {a}, b of [])",
"(const {a: a} = 1 of [])",
"(const {a: a}, b of [])",
"(const {'a': a} = 1 of [])",
"(const {'a': a}, b of [])",
"(const {\"a\": a} = 1 of [])",
"(const {\"a\": a}, b of [])",
"(const {[Symbol.iterator]: a} = 1 of [])",
"(const {[Symbol.iterator]: a}, b of [])",
"(const {0: a} = 1 of [])",
"(const {0: a}, b of [])",
"(const {a = 1} = 1 of [])",
"(const {a = 1}, b of [])",
"(const {a: a = 1} = 1 of [])",
"(const {a: a = 1}, b of [])",
"(const {'a': a = 1} = 1 of [])",
"(const {'a': a = 1}, b of [])",
"(const {\"a\": a = 1} = 1 of [])",
"(const {\"a\": a = 1}, b of [])",
"(const {[Symbol.iterator]: a = 1} = 1 of [])",
"(const {[Symbol.iterator]: a = 1}, b of [])",
"(const {0: a = 1} = 1 of [])",
"(const {0: a = 1}, b of [])",
NULL
};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonyAsyncIteration};
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
}
TEST(ForAwaitOfFunctionDeclaration) {
// clang-format off
const char* context_data[][2] = {
{ "async function f() {", "}" },
{ "async function f() { 'use strict'; ", "}" },
{ NULL, NULL }
};
const char* data[] = {
"for await (x of []) function d() {};",
"for await (x of []) function d() {}; return d;",
"for await (x of []) function* g() {};",
"for await (x of []) function* g() {}; return g;",
// TODO(caitp): handle async function declarations in ParseScopedStatement.
// "for await (x of []) async function a() {};",
// "for await (x of []) async function a() {}; return a;",
NULL
};
// clang-format on
static const ParserFlag always_flags[] = {kAllowHarmonyAsyncIteration};
RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
arraysize(always_flags));
}
// 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
let testFailed = false;
let testFailure;
(async function() {
const kNext = 1;
const kThrow = 2;
const kReturn = 4;
const kReturnPrimitive = kReturn | 32;
function async(iterable, features = kNext, log = []) {
// Helper to turn a synchronous iterable into an asynchronous iterable,
// without using the [Async-from-Sync Iterator].
let it = iterable[Symbol.iterator]();
let methods = {
next(sentValue) {
return new Promise(function(resolve, reject) {
let {value, done} = it.next(sentValue);
Promise.resolve(value).then(function(value) {
log.push('.next() -> resolved ' + value);
resolve({value, done});
}, function(value) {
log.push('.next() -> rejected ' + value);
reject(value);
});
});
},
throw(sentValue) {
return new Promise(function(resolve, reject) {
let throwMethod = it.throw;
if (typeof throwMethod !== 'function') {
log.push('.throw(' + sentValue + ')');
return reject(sentValue);
}
let {value, done} = throwMethod.call(it, sentValue);
Promise.resolve(value).then(function(value) {
log.push('.throw() -> resolved ' + value);
resolve({ value, done });
}, function(value) {
log.push('.throw() -> rejected ' + value);
reject(value);
});
});
},
return(sentValue) {
return new Promise(function(resolve, reject) {
let returnMethod = it.return;
if (typeof returnMethod !== 'function') {
log.push('.return(' + sentValue + ')');
if ((features & kReturnPrimitive) === kReturnPrimitive)
return resolve(sentValue);
return resolve({value: sentValue, done: true});
}
let {value, done} = returnMethod.call(it, sentValue);
Promise.resolve(value).then(function(value) {
log.push('.return() -> resolved ' + value);
if ((features & kReturnPrimitive) === kReturnPrimitive)
return resolve(value);
resolve({ value, done });
}, function(value) {
log.push('.return() -> rejected ' + value);
reject(value);
});
});
}
};
return {
[Symbol.asyncIterator]() {
log.push('[Symbol.asyncIterator]()')
return this;
},
next: (features & kNext) ? methods.next : undefined,
throw: (features & kThrow) ? methods.throw : undefined,
return: (features & kReturn) ? methods.return : undefined
};
}
let testDone;
let test;
async function testBindingIdentifierVarDeclarationStatement() {
let sum = 0;
testDone = false;
for await (var value of async([100, 200, 300, 400, 500])) sum += value;
testDone = true;
return sum;
}
test = testBindingIdentifierVarDeclarationStatement();
assertFalse(testDone);
assertEquals(1500, await test);
assertTrue(testDone);
async function testBindingIdentifierVarDeclarationBlockStatement() {
let sum = 0;
testDone = false;
for await (var value of async([100, 200, 300, 400, 500])) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
sum += value;
}
testDone = true;
return sum;
}
test = testBindingIdentifierVarDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(1500, await test);
assertTrue(testDone);
async function testObjectBindingPatternVarDeclarationStatement() {
let sum = 0, keys = [];
let collection = [
{key: 'first', value: 10}, {key: undefined, value: 20}, {value: 30},
{key: 'last', value: 40}
];
testDone = false;
for await (var {key = 'unknown', value} of async(collection))
keys.push(key), sum += value;
testDone = true;
return {keys, sum};
}
test = testObjectBindingPatternVarDeclarationStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testObjectBindingPatternVarDeclarationBlockStatement() {
let sum = 0, keys = [];
let collection = [
{key: 'first', value: 10}, {key: undefined, value: 20}, {value: 30},
{key: 'last', value: 40}
];
testDone = false;
for await (var {key = 'unknown', value} of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
test = testObjectBindingPatternVarDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testArrayBindingPatternVarDeclarationStatement() {
let sum = 0, keys = [];
let collection = [['first', 10], [undefined, 20], [, 30], ['last', 40]];
testDone = false;
for await (var [key = 'unknown', value] of async(collection))
keys.push(key), sum += value;
testDone = true;
return {keys, sum};
}
test = testArrayBindingPatternVarDeclarationStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testArrayBindingPatternVarDeclarationBlockStatement() {
let sum = 0, keys = [];
let collection = [['first', 10], [undefined, 20], [, 30], ['last', 40]];
testDone = false;
for await (var [key = 'unknown', value] of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
test = testArrayBindingPatternVarDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
// --------------------------------------------------------------------------
async function testBindingIdentifierLetDeclarationStatement() {
let sum = 0;
testDone = false;
for await (let value of async([100, 200, 300, 400, 500])) sum += value;
testDone = true;
return sum;
}
test = testBindingIdentifierLetDeclarationStatement();
assertFalse(testDone);
assertEquals(1500, await test);
assertTrue(testDone);
async function testBindingIdentifierLetDeclarationBlockStatement() {
let sum = 0;
testDone = false;
for await (let value of async([100, 200, 300, 400, 500])) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
sum += value;
}
testDone = true;
return sum;
}
test = testBindingIdentifierLetDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(1500, await test);
assertTrue(testDone);
async function testObjectBindingPatternLetDeclarationStatement() {
let sum = 0, keys = [];
let collection = [
{key: 'first', value: 10}, {key: undefined, value: 20}, {value: 30},
{key: 'last', value: 40}
];
testDone = false;
for await (let {key = 'unknown', value} of async(collection))
keys.push(key), sum += value;
testDone = true;
return {keys, sum};
}
test = testObjectBindingPatternLetDeclarationStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testObjectBindingPatternLetDeclarationBlockStatement() {
let sum = 0, keys = [];
let collection = [
{key: 'first', value: 10}, {key: undefined, value: 20}, {value: 30},
{key: 'last', value: 40}
];
testDone = false;
for await (let {key = 'unknown', value} of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
let threwEarly = false;
test = testObjectBindingPatternLetDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testObjectBindingPatternTDZLetDeclarationStatement() {
// See https://codereview.chromium.org/1218543003
let sum = 0;
testDone = false;
let value = { value: 1 };
try {
for await (let {value} of async([value])) sum += value;
} catch (error) {
threwEarly = true;
throw { sum, error, toString() { return 'TestError' } };
}
}
test = testObjectBindingPatternTDZLetDeclarationStatement();
assertTrue(threwEarly, 'Async function promise should be rejected');
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(0, e.sum);
assertInstanceof(e.error, ReferenceError);
testDone = true;
}
assertTrue(testDone, 'Awaited promise should be rejected');
async function testObjectBindingPatternTDZLetDeclarationBlockStatement() {
// See https://codereview.chromium.org/1218543003
let sum = 0;
testDone = false;
let value = { value: 1 };
try {
for await (let {value} of async([value])) {
sum += value;
}
} catch (error) {
threwEarly = true;
throw { sum, error, toString() { return 'TestError' } };
}
}
threwEarly = false;
test = testObjectBindingPatternTDZLetDeclarationBlockStatement();
assertTrue(threwEarly, 'Async function promise should be rejected');
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(0, e.sum);
assertInstanceof(e.error, ReferenceError);
testDone = true;
}
assertTrue(testDone, 'Awaited promise should be rejected');
async function testArrayBindingPatternLetDeclarationStatement() {
let sum = 0, keys = [];
let collection = [['first', 10], [undefined, 20], [, 30], ['last', 40]];
testDone = false;
for await (let [key = 'unknown', value] of async(collection))
keys.push(key), sum += value;
testDone = true;
return {keys, sum};
}
test = testArrayBindingPatternLetDeclarationStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testArrayBindingPatternLetDeclarationBlockStatement() {
let sum = 0, keys = [];
let collection = [['first', 10], [undefined, 20], [, 30], ['last', 40]];
testDone = false;
for await (let [key = 'unknown', value] of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
test = testArrayBindingPatternLetDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testArrayBindingPatternTDZLetDeclarationStatement() {
// See https://codereview.chromium.org/1218543003
let sum = 0;
testDone = false;
let value = [1];
try {
for await (let [value] of async([value])) sum += value;
} catch (error) {
threwEarly = true;
throw { sum, error, toString() { return 'TestError' } };
}
}
threwEarly = false;
test = testArrayBindingPatternTDZLetDeclarationStatement();
assertTrue(threwEarly, 'Async function promise should be rejected');
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(0, e.sum);
assertInstanceof(e.error, ReferenceError);
testDone = true;
}
assertTrue(testDone, 'Awaited promise should be rejected');
async function testArrayBindingPatternTDZLetDeclarationBlockStatement() {
// See https://codereview.chromium.org/1218543003
let sum = 0;
testDone = false;
let value = [1];
try {
for await (let [value] of async([value])) {
sum += value;
}
} catch (error) {
threwEarly = true;
throw { sum, error, toString() { return 'TestError' } };
}
}
threwEarly = false;
test = testArrayBindingPatternTDZLetDeclarationBlockStatement();
assertTrue(threwEarly, 'Async function promise should be rejected');
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(0, e.sum);
assertInstanceof(e.error, ReferenceError);
testDone = true;
}
assertTrue(testDone, 'Awaited promise should be rejected');
// --------------------------------------------------------------------------
async function testBindingIdentifierConstDeclarationStatement() {
let sum = 0;
testDone = false;
for await (let value of async([100, 200, 300, 400, 500])) sum += value;
testDone = true;
return sum;
}
test = testBindingIdentifierConstDeclarationStatement();
assertFalse(testDone);
assertEquals(1500, await test);
assertTrue(testDone);
async function testBindingIdentifierConstDeclarationBlockStatement() {
let sum = 0;
testDone = false;
for await (const value of async([100, 200, 300, 400, 500])) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
sum += value;
}
testDone = true;
return sum;
}
test = testBindingIdentifierConstDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(1500, await test);
assertTrue(testDone);
async function testObjectBindingPatternConstDeclarationStatement() {
let sum = 0, keys = [];
let collection = [
{key: 'first', value: 10}, {key: undefined, value: 20}, {value: 30},
{key: 'last', value: 40}
];
testDone = false;
for await (const {key = 'unknown', value} of async(collection))
keys.push(key), sum += value;
testDone = true;
return {keys, sum};
}
test = testObjectBindingPatternConstDeclarationStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testObjectBindingPatternConstDeclarationBlockStatement() {
let sum = 0, keys = [];
let collection = [
{key: 'first', value: 10}, {key: undefined, value: 20}, {value: 30},
{key: 'last', value: 40}
];
testDone = false;
for await (const {key = 'unknown', value} of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
test = testObjectBindingPatternConstDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testObjectBindingPatternTDZConstDeclarationStatement() {
// See https://codereview.chromium.org/1218543003
let sum = 0;
testDone = false;
const value = { value: 1 };
try {
for await (const {value} of async([value])) sum += value;
} catch (error) {
threwEarly = true;
throw { sum, error, toString() { return 'TestError' } };
}
}
threwEarly = false;
test = testObjectBindingPatternTDZConstDeclarationStatement();
assertTrue(threwEarly, 'Async function promise should be rejected');
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(0, e.sum);
assertInstanceof(e.error, ReferenceError);
testDone = true;
}
assertTrue(testDone, 'Awaited promise should be rejected');
async function testObjectBindingPatternTDZConstDeclarationBlockStatement() {
// See https://codereview.chromium.org/1218543003
let sum = 0;
testDone = false;
const value = { value: 1 };
try {
for await (const {value} of async([value])) {
sum += value;
}
} catch (error) {
threwEarly = true;
throw { sum, error, toString() { return 'TestError' } };
}
}
threwEarly = false;
test = testObjectBindingPatternTDZConstDeclarationBlockStatement();
assertTrue(threwEarly, 'Async function promise should be rejected');
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(0, e.sum);
assertInstanceof(e.error, ReferenceError);
testDone = true;
}
assertTrue(testDone, 'Awaited promise should be rejected');
async function testArrayBindingPatternConstDeclarationStatement() {
let sum = 0, keys = [];
let collection = [['first', 10], [undefined, 20], [, 30], ['last', 40]];
testDone = false;
for await (const [key = 'unknown', value] of async(collection))
keys.push(key), sum += value;
testDone = true;
return {keys, sum};
}
test = testArrayBindingPatternConstDeclarationStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testArrayBindingPatternConstDeclarationBlockStatement() {
let sum = 0, keys = [];
let collection = [['first', 10], [undefined, 20], [, 30], ['last', 40]];
testDone = false;
for await (const [key = 'unknown', value] of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
test = testArrayBindingPatternLetDeclarationBlockStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 100}, await test);
assertTrue(testDone);
async function testArrayBindingPatternTDZConstDeclarationStatement() {
// See https://codereview.chromium.org/1218543003
let sum = 0;
testDone = false;
const value = [1];
try {
for await (const [value] of async([value])) sum += value;
} catch (error) {
threwEarly = true;
throw { sum, error, toString() { return 'TestError' } };
}
}
threwEarly = false;
test = testArrayBindingPatternTDZConstDeclarationStatement();
assertTrue(threwEarly, 'Async function promise should be rejected');
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(0, e.sum);
assertInstanceof(e.error, ReferenceError);
testDone = true;
}
assertTrue(testDone, 'Awaited promise should be rejected');
async function testArrayBindingPatternTDZConstDeclarationBlockStatement() {
// See https://codereview.chromium.org/1218543003
let sum = 0;
testDone = false;
const value = [1];
try {
for await (const [value] of async([value])) {
sum += value;
}
} catch (error) {
threwEarly = true;
throw { sum, error, toString() { return 'TestError' } };
}
}
threwEarly = false;
test = testArrayBindingPatternTDZConstDeclarationBlockStatement();
assertTrue(threwEarly, 'Async function promise should be rejected');
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(0, e.sum);
assertInstanceof(e.error, ReferenceError);
testDone = true;
}
assertTrue(testDone, 'Awaited promise should be rejected');
// --------------------------------------------------------------------------
async function testBindingIdentifierLHSStatement() {
let sum = 0;
let value;
testDone = false;
for await (value of async([100, 200, 300, 400, 500])) sum += value;
testDone = true;
return sum;
}
test = testBindingIdentifierLHSStatement();
assertFalse(testDone);
assertEquals(1500, await test);
assertTrue(testDone);
async function testBindingIdentifierLHSBlockStatement() {
let sum = 0;
let value;
testDone = false;
for await (value of async([100, 200, 300, 400, 500])) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
sum += value;
}
testDone = true;
return sum;
}
test = testBindingIdentifierLHSStatement();
assertFalse(testDone);
assertEquals(1500, await test);
assertTrue(testDone);
async function testObjectBindingPatternLHSStatement() {
let sum = 0;
let keys = [];
let value;
let key;
let collection = [
{key: 'first', value: 1}, {key: undefined, value: 2}, {value: 3},
{key: 'last', value: 4}
];
testDone = false;
for await ({key = 'unknown', value} of async(collection))
keys.push(key), sum += value;
testDone = true;
return {keys, sum};
}
test = testObjectBindingPatternLHSStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 10}, await test);
assertTrue(testDone);
async function testObjectBindingPatternLHSBlockStatement() {
let sum = 0;
let keys = [];
let value;
let key;
let collection = [
{key: 'first', value: 1}, {key: undefined, value: 2}, {value: 3},
{key: 'last', value: 4}
];
testDone = false;
for await ({key = 'unknown', value} of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
test = testObjectBindingPatternLHSBlockStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 10}, await test);
assertTrue(testDone);
async function testArrayBindingPatternLHSStatement() {
let sum = 0;
let keys = [];
let value;
let key;
let collection = [['first', 1], [undefined, 2], [, 3], ['last', 4]];
testDone = false;
for await ([key = 'unknown', value] of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
test = testArrayBindingPatternLHSStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 10}, await test);
assertTrue(testDone);
async function testArrayBindingPatternLHSBlockStatement() {
let sum = 0;
let keys = [];
let value;
let key;
let collection = [
{key: 'first', value: 1}, {key: undefined, value: 2}, {value: 3},
{key: 'last', value: 4}
];
testDone = false;
for await ({key = 'unknown', value} of async(collection)) {
'use strict';
let strict = (function() { return this === undefined; })();
assertFalse(strict);
keys.push(key);
sum += value;
}
testDone = true;
return {keys, sum};
}
test = testArrayBindingPatternLHSBlockStatement();
assertFalse(testDone);
assertEquals(
{keys: ['first', 'unknown', 'unknown', 'last'], sum: 10}, await test);
assertTrue(testDone);
// --------------------------------------------------------------------------
async function testBreakStatementReturnMethodNotPresent() {
let log = [];
let collection = [1, 2, 3, 4, 5];
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(collection, kNext, log)) {
sum += x;
if (++i === 3) break;
}
testDone = true;
return { sum, log };
}
test = testBreakStatementReturnMethodNotPresent();
assertFalse(testDone);
assertEquals({sum: 6, log: ['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.next() -> resolved 3']},
await test);
assertTrue(testDone);
async function testBreakStatementReturnMethodPresent() {
let log = [];
let collection = [1, 2, 3, 4, 5];
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(collection, kNext|kReturn, log)) {
sum += x;
if (++i === 2) break;
}
testDone = true;
return { sum, log };
}
test = testBreakStatementReturnMethodPresent();
assertFalse(testDone);
assertEquals({sum: 3, log: ['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return(undefined)']},
await test);
assertTrue(testDone);
async function testBreakStatementReturnMethodAwaitIterResult() {
let log = [];
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
sync_iter.return = function() {
return {
value: new Promise(function(resolve, reject) {
Promise.resolve().then(function() {
resolve('break!');
});
}),
done: true
};
};
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturn, log)) {
sum += x;
if (++i === 2) break;
}
testDone = true;
return { sum, log };
}
test = testBreakStatementReturnMethodAwaitIterResult();
assertFalse(testDone);
assertEquals({sum: 3,
log: ['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> resolved break!' ]},
await test);
assertTrue(testDone);
async function testBreakStatementReturnMethodAwaitRejection(log) {
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
let sum = 0;
sync_iter.return = function() {
return {
value: new Promise(function(resolve, reject) {
Promise.resolve().then(function() {
reject('break! ' + sum);
});
}),
done: true
};
};
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturn, log)) {
sum += x;
if (++i === 2) break;
}
return { sum, log };
}
let log = [];
test = testBreakStatementReturnMethodAwaitRejection(log);
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals(log, ['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> rejected break! 3']);
assertEquals('break! 3', e);
testDone = true;
}
assertTrue(testDone, 'Promise should be rejected');
async function testBreakStatementReturnMethodPrimitiveValue(log) {
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
sync_iter.return = function() {
return { value: 'break! primitive!', done: true };
}
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturnPrimitive, log)) {
sum += x;
if (++i === 2) break;
}
return { sum, log };
}
log = [];
test = testBreakStatementReturnMethodPrimitiveValue(log);
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals(['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> resolved break! primitive!'],
log);
assertInstanceof(e, TypeError);
testDone = true;
}
assertTrue(testDone, 'Promise should be rejected');
async function testReturnStatementReturnMethodNotPresent() {
let log = [];
let collection = [1, 2, 3, 4, 5];
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(collection, kNext, log)) {
sum += x;
if (++i === 3) {
testDone = true;
return { sum, log };
}
}
}
test = testReturnStatementReturnMethodNotPresent();
assertFalse(testDone);
assertEquals({sum: 6, log: ['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.next() -> resolved 3']},
await test);
assertTrue(testDone);
async function testReturnStatementReturnMethodPresent() {
let log = [];
let collection = [1, 2, 3, 4, 5];
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(collection, kNext|kReturn, log)) {
sum += x;
if (++i === 2) {
testDone = true;
return { sum, log };
}
}
}
test = testReturnStatementReturnMethodPresent();
assertFalse(testDone);
assertEquals({sum: 3, log: ['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return(undefined)']},
await test);
assertTrue(testDone);
async function testReturnStatementReturnMethodAwaitIterResult() {
let log = [];
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
sync_iter.return = function() {
return {
value: new Promise(function(resolve, reject) {
Promise.resolve().then(function() {
testDone = true;
resolve('return!');
});
}),
done: true
};
};
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturn, log)) {
sum += x;
if (++i === 2) return { sum, log };
}
}
test = testReturnStatementReturnMethodAwaitIterResult();
assertFalse(testDone);
assertEquals({sum: 3,
log: ['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> resolved return!' ]},
await test);
assertTrue(testDone);
async function testReturnStatementReturnMethodAwaitRejection(log) {
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
let sum = 0;
sync_iter.return = function() {
return {
value: new Promise(function(resolve, reject) {
Promise.resolve().then(function() {
reject('return! ' + sum);
});
}),
done: true
};
};
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturn, log)) {
sum += x;
if (++i === 2) return { sum, log };
}
}
log = [];
test = testReturnStatementReturnMethodAwaitRejection(log);
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals('return! 3', e);
assertEquals(['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> rejected return! 3'],
log);
testDone = true;
}
assertTrue(testDone, 'Promise should be rejected');
async function testReturnStatementReturnMethodPrimitiveValue(log) {
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
sync_iter.return = function() {
return { value: 'return! primitive!', done: true };
}
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturnPrimitive, log)) {
sum += x;
if (++i === 2) break;
}
return { sum, log };
}
log = [];
test = testReturnStatementReturnMethodPrimitiveValue(log);
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals(['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> resolved return! primitive!'],
log);
assertInstanceof(e, TypeError);
testDone = true;
}
assertTrue(testDone, 'Promise should be rejected');
async function testThrowStatementReturnMethodNotPresent() {
let log = [];
let collection = [1, 2, 3, 4, 5];
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(collection, kNext|kThrow, log)) {
sum += x;
if (++i === 3) {
throw { sum, log, toString() { return 'TestError'; } };
}
}
return { sum, log };
}
test = testThrowStatementReturnMethodNotPresent();
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals('TestError', e.toString());
assertEquals(6, e.sum);
assertEquals(['[Symbol.asyncIterator]()', '.next() -> resolved 1',
'.next() -> resolved 2', '.next() -> resolved 3'
], e.log);
testDone = true;
}
assertTrue(testDone, 'Awaited Promise should be rejected');
async function testThrowStatementReturnMethodPresent() {
let log = [];
let collection = [1, 2, 3, 4, 5];
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(collection, kNext|kThrow|kReturn, log)) {
sum += x;
if (++i === 2) {
throw { sum, log, toString() { return 'TestError2'; } };
}
}
return { sum, log };
}
test = testThrowStatementReturnMethodPresent();
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals('TestError2', e.toString());
assertEquals(3, e.sum);
assertEquals(['[Symbol.asyncIterator]()', '.next() -> resolved 1',
'.next() -> resolved 2', '.return(undefined)'
], e.log);
testDone = true;
}
assertTrue(testDone, 'Awaited Promise should be rejected');
async function testThrowStatementReturnMethodAwaitIterResult(log) {
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
sync_iter.return = function() {
return {
value: new Promise(function(resolve, reject) {
Promise.resolve().then(function() {
testDone = true;
resolve('throw!');
});
}),
done: true
};
};
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturn, log)) {
sum += x;
if (++i === 2) throw 'Boo!!';
}
}
log = [];
test = testThrowStatementReturnMethodAwaitIterResult(log);
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals('Boo!!', e);
assertEquals(['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> resolved throw!' ], log);
testDone = true;
}
assertTrue(testDone, 'Awaited Promise should be rejected');
async function testThrowStatementReturnMethodAwaitRejection(log) {
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
let sum = 0;
sync_iter.return = function() {
return {
value: new Promise(function(resolve, reject) {
Promise.resolve().then(function() {
reject('return! ' + sum);
});
}),
done: true
};
};
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturn, log)) {
sum += x;
if (++i === 2) throw 'Boo!!';
}
}
log = [];
test = testThrowStatementReturnMethodAwaitRejection(log);
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals('Boo!!', e);
assertEquals(['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> rejected return! 3'],
log);
testDone = true;
}
assertTrue(testDone, 'Promise should be rejected');
async function testThrowStatementReturnMethodPrimitiveValue(log) {
let collection = [1, 2, 3, 4, 5];
let sync_iter = collection[Symbol.iterator]();
sync_iter.return = function() {
return { value: 'return! primitive!', done: true };
}
let sum = 0;
let i = 0;
testDone = false;
for await (var x of async(sync_iter, kNext|kReturnPrimitive, log)) {
sum += x;
if (++i === 2) throw 'Boo!!';
}
}
log = [];
test = testThrowStatementReturnMethodPrimitiveValue(log);
assertFalse(testDone);
try {
await test;
} catch (e) {
assertEquals(['[Symbol.asyncIterator]()',
'.next() -> resolved 1',
'.next() -> resolved 2',
'.return() -> resolved return! primitive!'],
log);
// AsyncIteratorClose does not require Throw completions to be of type
// Object
assertEquals('Boo!!', e);
testDone = true;
}
assertTrue(testDone, 'Promise should be rejected');
})().catch(function(error) {
testFailed = true;
testFailure = error;
});
%RunMicrotasks();
if (testFailed) {
throw testFailure;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment