Chromium Code Reviews| Index: src/parsing/parser-base.h |
| diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h |
| index 777cad1ac79dfdcdceb8872bdc7bd316ffbb42ea..c2db29a8afbeb50effed013015c6ed30781f091e 100644 |
| --- a/src/parsing/parser-base.h |
| +++ b/src/parsing/parser-base.h |
| @@ -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 |
| @@ -1307,6 +1309,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(); |
| @@ -1503,6 +1507,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; |
| }; |
| @@ -4815,6 +4820,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: |
| @@ -5721,6 +5730,160 @@ 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()->NewForEachStatement(for_info.mode, labels, stmt_pos); |
| + typename Types::Target target(this, loop); |
| + |
| + ExpressionT each_variable = impl()->EmptyExpression(); |
| + |
| + bool has_declarations = false; |
| + |
| + switch (peek()) { |
| + case Token::LET: |
| + if (!IsNextLetKeyword()) goto parse_lhs; |
|
adamk
2017/02/14 18:47:53
This is the first goto in the parser. Given that i
caitp
2017/02/14 20:27:29
Seems harder to read, to me, but alright
caitp
2017/02/14 21:40:58
Done.
|
| + /* falls through */ |
| + case Token::VAR: |
| + case Token::CONST: { |
| + // 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(); |
| + } |
| + break; |
| + } |
| + |
| + default: |
| + parse_lhs : { |
| + // 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); |
| + } |
| + } break; |
| + } |
| + |
| + 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); |
| + |
| + // For legacy compat reasons, give for loops similar treatment to |
|
adamk
2017/02/14 18:47:53
Seems odd that new syntax still maintains this leg
caitp
2017/02/14 20:27:29
The spec doesn't say anything about any of this, b
|
| + // if statements in allowing a function declaration for a body |
| + StatementT body = ParseScopedStatement(nullptr, true, 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) { |
| if (property == Token::SMI || property == Token::NUMBER) return; |