Chromium Code Reviews| Index: src/parsing/parser-base.h |
| diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h |
| index 3171e57144e09562b294fd26ff934481c981848d..b5801662a5d62253fc2144b0356297c9fe19f623 100644 |
| --- a/src/parsing/parser-base.h |
| +++ b/src/parsing/parser-base.h |
| @@ -656,6 +656,21 @@ class ParserBase { |
| TailCallExpressionList tail_call_expressions; |
| }; |
| + struct ForInfo { |
| + public: |
| + explicit ForInfo(ParserBase* parser) |
| + : bound_names(1, parser->zone()), |
| + bound_names_are_lexical(false), |
| + mode(ForEachStatement::ENUMERATE), |
| + each_loc(), |
| + parsing_result() {} |
| + ZoneList<const AstRawString*> bound_names; |
| + bool bound_names_are_lexical; |
| + ForEachStatement::VisitMode mode; |
| + Scanner::Location each_loc; |
| + DeclarationParsingResult parsing_result; |
| + }; |
| + |
| DeclarationScope* NewScriptScope() const { |
| return new (zone()) DeclarationScope(zone(), ast_value_factory()); |
| } |
| @@ -1250,7 +1265,7 @@ class ParserBase { |
| StatementT ParseSwitchStatement(ZoneList<const AstRawString*>* labels, |
| bool* ok); |
| StatementT ParseTryStatement(bool* ok); |
| - Statement* ParseForStatement(ZoneList<const AstRawString*>* labels, bool* ok); |
| + StatementT ParseForStatement(ZoneList<const AstRawString*>* labels, bool* ok); |
| bool IsNextLetKeyword(); |
| bool IsTrivialExpression(); |
| @@ -4350,7 +4365,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStatement( |
| case Token::WHILE: |
| return ParseWhileStatement(labels, ok); |
| case Token::FOR: |
| - return impl()->ParseForStatement(labels, ok); |
| + return ParseForStatement(labels, ok); |
| case Token::CONTINUE: |
| case Token::BREAK: |
| case Token::RETURN: |
| @@ -4977,12 +4992,11 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseTryStatement( |
| catch_info, pos); |
| } |
| -Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, |
| - bool* ok) { |
| +template <typename Impl> |
| +typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement( |
| + ZoneList<const AstRawString*>* labels, bool* ok) { |
| int stmt_pos = peek_position(); |
| - Statement* init = NULL; |
| - ZoneList<const AstRawString*> bound_names(1, zone()); |
| - bool bound_names_are_lexical = false; |
| + ForInfo for_info(this); |
| // Create an in-between scope for let-bound iteration variables. |
| BlockState for_state(&scope_state_); |
| @@ -4990,190 +5004,96 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, |
| Expect(Token::LPAREN, CHECK_OK); |
| for_state.set_start_position(scanner()->location().beg_pos); |
| for_state.set_is_hidden(); |
| - DeclarationParsingResult parsing_result; |
| + |
| + StatementT init = impl()->NullStatement(); |
| if (peek() != Token::SEMICOLON) { |
| + // An initializer is present. |
| if (peek() == Token::VAR || peek() == Token::CONST || |
| (peek() == Token::LET && IsNextLetKeyword())) { |
| - ParseVariableDeclarations(kForStatement, &parsing_result, nullptr, |
| - CHECK_OK); |
| - |
| - ForEachStatement::VisitMode mode = ForEachStatement::ENUMERATE; |
| - int each_beg_pos = scanner()->location().beg_pos; |
| - int each_end_pos = scanner()->location().end_pos; |
| - |
| - if (CheckInOrOf(&mode)) { |
| - if (parsing_result.declarations.length() != 1) { |
| - ReportMessageAt(parsing_result.bindings_loc, |
| - MessageTemplate::kForInOfLoopMultiBindings, |
| - ForEachStatement::VisitModeString(mode)); |
| + // The initializer contains declarations. |
| + ParseVariableDeclarations(kForStatement, &for_info.parsing_result, |
| + nullptr, CHECK_OK); |
| + for_info.bound_names_are_lexical = |
|
marja
2016/09/20 19:36:30
bound_names_are_lexical is now redundant, right? M
nickie
2016/09/21 09:20:02
Yes. I'm removing it from ForInfo but I'm keeping
|
| + IsLexicalVariableMode(for_info.parsing_result.descriptor.mode); |
| + for_info.each_loc = scanner()->location(); |
| + |
| + if (CheckInOrOf(&for_info.mode)) { |
| + // Just one declaration followed by in/of. |
| + if (for_info.parsing_result.declarations.length() != 1) { |
| + impl()->ReportMessageAt( |
| + for_info.parsing_result.bindings_loc, |
| + MessageTemplate::kForInOfLoopMultiBindings, |
| + ForEachStatement::VisitModeString(for_info.mode)); |
| *ok = false; |
| - return nullptr; |
| + return impl()->NullStatement(); |
| } |
| - DeclarationParsingResult::Declaration& decl = |
| - parsing_result.declarations[0]; |
| - if (parsing_result.first_initializer_loc.IsValid() && |
| - (is_strict(language_mode()) || mode == ForEachStatement::ITERATE || |
| - IsLexicalVariableMode(parsing_result.descriptor.mode) || |
| - !decl.pattern->IsVariableProxy() || allow_harmony_for_in())) { |
| + if (for_info.parsing_result.first_initializer_loc.IsValid() && |
| + (is_strict(language_mode()) || |
| + for_info.mode == ForEachStatement::ITERATE || |
|
marja
2016/09/21 08:23:21
Random whining unrelated to this CL: Why do we nee
nickie
2016/09/21 09:20:02
I agree. Also, it seems that this is used only lo
|
| + IsLexicalVariableMode(for_info.parsing_result.descriptor.mode) || |
| + !impl()->IsIdentifier( |
| + for_info.parsing_result.declarations[0].pattern) || |
| + allow_harmony_for_in())) { |
| // Only increment the use count if we would have let this through |
| // without the flag. |
| if (allow_harmony_for_in()) { |
| - ++use_counts_[v8::Isolate::kForInInitializer]; |
| + impl()->CountUsage(v8::Isolate::kForInInitializer); |
| } |
| - ReportMessageAt(parsing_result.first_initializer_loc, |
| - MessageTemplate::kForInOfLoopInitializer, |
| - ForEachStatement::VisitModeString(mode)); |
| + impl()->ReportMessageAt( |
| + for_info.parsing_result.first_initializer_loc, |
| + MessageTemplate::kForInOfLoopInitializer, |
| + ForEachStatement::VisitModeString(for_info.mode)); |
| *ok = false; |
| - return nullptr; |
| + return impl()->NullStatement(); |
| } |
| - Block* init_block = nullptr; |
| - bound_names_are_lexical = |
| - IsLexicalVariableMode(parsing_result.descriptor.mode); |
| - |
| - // special case for legacy for (var ... = ... in ...) |
| - if (!bound_names_are_lexical && decl.pattern->IsVariableProxy() && |
| - decl.initializer != nullptr) { |
| - DCHECK(!allow_harmony_for_in()); |
| - ++use_counts_[v8::Isolate::kForInInitializer]; |
| - const AstRawString* name = |
| - decl.pattern->AsVariableProxy()->raw_name(); |
| - VariableProxy* single_var = NewUnresolved(name); |
| - init_block = factory()->NewBlock( |
| - nullptr, 2, true, parsing_result.descriptor.declaration_pos); |
| - init_block->statements()->Add( |
| - factory()->NewExpressionStatement( |
| - factory()->NewAssignment(Token::ASSIGN, single_var, |
| - decl.initializer, kNoSourcePosition), |
| - kNoSourcePosition), |
| - zone()); |
| - } |
| + BlockT init_block = impl()->RewriteForVarInLegacy(&for_info); |
| + |
| + auto loop = |
|
marja
2016/09/20 19:36:30
I was thinking about the use of "auto" here (and b
nickie
2016/09/21 09:20:02
Yes, that's precisely what it does. (BTW, it's us
marja
2016/09/21 10:31:42
Ok, then it's fine.
|
| + factory()->NewForEachStatement(for_info.mode, labels, stmt_pos); |
| + typename Types::Target target(this, loop); |
| + |
| + int each_keyword_pos = scanner()->location().beg_pos; |
| - // Rewrite a for-in/of statement of the form |
| - // |
| - // for (let/const/var x in/of e) b |
| - // |
| - // into |
| - // |
| - // { |
| - // <let x' be a temporary variable> |
| - // for (x' in/of e) { |
| - // let/const/var x; |
| - // x = x'; |
| - // b; |
| - // } |
| - // let x; // for TDZ |
| - // } |
| - |
| - Variable* temp = NewTemporary(ast_value_factory()->dot_for_string()); |
| - ForEachStatement* loop = |
| - factory()->NewForEachStatement(mode, labels, stmt_pos); |
| - ParserTarget target(this, loop); |
| - |
| - int each_keyword_position = scanner()->location().beg_pos; |
| - |
| - Expression* enumerable; |
| - if (mode == ForEachStatement::ITERATE) { |
| + ExpressionT enumerable = impl()->EmptyExpression(); |
| + if (for_info.mode == ForEachStatement::ITERATE) { |
| ExpressionClassifier classifier(this); |
| enumerable = ParseAssignmentExpression(true, CHECK_OK); |
| - RewriteNonPattern(CHECK_OK); |
| + impl()->RewriteNonPattern(CHECK_OK); |
| } else { |
| enumerable = ParseExpression(true, CHECK_OK); |
| } |
| Expect(Token::RPAREN, CHECK_OK); |
| - |
| - Block* body_block = |
| - factory()->NewBlock(NULL, 3, false, kNoSourcePosition); |
| - |
| - Statement* final_loop; |
| + StatementT final_loop = impl()->NullStatement(); |
| { |
| ReturnExprScope no_tail_calls(function_state_, |
| ReturnExprContext::kInsideForInOfBody); |
| BlockState block_state(&scope_state_); |
| block_state.set_start_position(scanner()->location().beg_pos); |
| - Statement* body = ParseScopedStatement(NULL, true, CHECK_OK); |
| - |
| - auto each_initialization_block = |
| - factory()->NewBlock(nullptr, 1, true, kNoSourcePosition); |
| - { |
| - auto descriptor = parsing_result.descriptor; |
| - descriptor.declaration_pos = kNoSourcePosition; |
| - descriptor.initialization_pos = kNoSourcePosition; |
| - decl.initializer = factory()->NewVariableProxy(temp); |
| - |
| - bool is_for_var_of = |
| - mode == ForEachStatement::ITERATE && |
| - parsing_result.descriptor.mode == VariableMode::VAR; |
| - |
| - PatternRewriter::DeclareAndInitializeVariables( |
| - this, each_initialization_block, &descriptor, &decl, |
| - bound_names_are_lexical || is_for_var_of ? &bound_names |
| - : nullptr, |
| - CHECK_OK); |
| - |
| - // Annex B.3.5 prohibits the form |
| - // `try {} catch(e) { for (var e of {}); }` |
| - // So if we are parsing a statement like `for (var ... of ...)` |
| - // we need to walk up the scope chain and look for catch scopes |
| - // which have a simple binding, then compare their binding against |
| - // all of the names declared in the init of the for-of we're |
| - // parsing. |
| - if (is_for_var_of) { |
| - Scope* catch_scope = scope(); |
| - while (catch_scope != nullptr && |
| - !catch_scope->is_declaration_scope()) { |
| - if (catch_scope->is_catch_scope()) { |
| - auto name = catch_scope->catch_variable_name(); |
| - if (name != |
| - ast_value_factory() |
| - ->dot_catch_string()) { // i.e. is a simple binding |
| - if (bound_names.Contains(name)) { |
| - ReportMessageAt(parsing_result.bindings_loc, |
| - MessageTemplate::kVarRedeclaration, name); |
| - *ok = false; |
| - return nullptr; |
| - } |
| - } |
| - } |
| - catch_scope = catch_scope->outer_scope(); |
| - } |
| - } |
| - } |
| + StatementT body = ParseScopedStatement(nullptr, true, CHECK_OK); |
| + |
| + std::pair<BlockT, ExpressionT> body_block_and_each_variable = |
| + impl()->DesugarBindingInForEachStatement(&for_info, CHECK_OK); |
| + body_block_and_each_variable.first->statements()->Add(body, zone()); |
|
adamk
2016/09/20 16:34:31
Please create a variable for body_block, it'll mak
marja
2016/09/21 08:23:21
Why do we need the pair anyway, it looks like we'r
nickie
2016/09/21 09:20:02
After offline discussion, I'm removing the pair an
nickie
2016/09/21 09:20:02
See comment below.
|
| + final_loop = impl()->InitializeForEachStatement( |
| + loop, body_block_and_each_variable.second, enumerable, |
| + body_block_and_each_variable.first, each_keyword_pos); |
| - body_block->statements()->Add(each_initialization_block, zone()); |
| - body_block->statements()->Add(body, zone()); |
| - VariableProxy* temp_proxy = |
| - factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos); |
| - final_loop = InitializeForEachStatement( |
| - loop, temp_proxy, enumerable, body_block, each_keyword_position); |
| block_state.set_end_position(scanner()->location().end_pos); |
| - body_block->set_scope(block_state.FinalizedBlockScope()); |
| + body_block_and_each_variable.first->set_scope( |
| + block_state.FinalizedBlockScope()); |
| } |
| - // Create a TDZ for any lexically-bound names. |
| - if (bound_names_are_lexical) { |
| - DCHECK_NULL(init_block); |
| - |
| - init_block = |
| - factory()->NewBlock(nullptr, 1, false, kNoSourcePosition); |
| - |
| - for (int i = 0; i < bound_names.length(); ++i) { |
| - // TODO(adamk): This needs to be some sort of special |
| - // INTERNAL variable that's invisible to the debugger |
| - // but visible to everything else. |
| - Declaration* tdz_decl = DeclareVariable( |
| - bound_names[i], LET, kNoSourcePosition, CHECK_OK); |
| - tdz_decl->proxy()->var()->set_initializer_position(position()); |
| - } |
| - } |
| + init_block = |
| + impl()->CreateForEachStatementTDZ(init_block, &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 (init_block != nullptr) { |
| + if (!impl()->IsNullStatement(init_block)) { |
| init_block->statements()->Add(final_loop, zone()); |
| init_block->set_scope(for_scope); |
| return init_block; |
| @@ -5182,47 +5102,48 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, |
| return final_loop; |
| } |
| } else { |
| - bound_names_are_lexical = |
| - IsLexicalVariableMode(parsing_result.descriptor.mode); |
| - init = BuildInitializationBlock( |
| - &parsing_result, bound_names_are_lexical ? &bound_names : nullptr, |
| + // One or more declaration not followed by in/of. |
| + init = impl()->BuildInitializationBlock( |
| + &for_info.parsing_result, |
| + for_info.bound_names_are_lexical ? &for_info.bound_names : nullptr, |
| CHECK_OK); |
| } |
| } else { |
| + // The initializer does not contain declarations. |
| int lhs_beg_pos = peek_position(); |
| ExpressionClassifier classifier(this); |
| - Expression* expression = ParseExpressionCoverGrammar(false, CHECK_OK); |
| + ExpressionT expression = ParseExpressionCoverGrammar(false, CHECK_OK); |
| int lhs_end_pos = scanner()->location().end_pos; |
| - ForEachStatement::VisitMode mode = ForEachStatement::ENUMERATE; |
| - bool is_for_each = CheckInOrOf(&mode); |
| + bool is_for_each = CheckInOrOf(&for_info.mode); |
| bool is_destructuring = is_for_each && (expression->IsArrayLiteral() || |
| expression->IsObjectLiteral()); |
| if (is_destructuring) { |
| ValidateAssignmentPattern(CHECK_OK); |
| } else { |
| - RewriteNonPattern(CHECK_OK); |
| + impl()->RewriteNonPattern(CHECK_OK); |
| } |
| if (is_for_each) { |
| + // Initializer is reference followed by in/of. |
| if (!is_destructuring) { |
| - expression = CheckAndRewriteReferenceExpression( |
| + expression = impl()->CheckAndRewriteReferenceExpression( |
| expression, lhs_beg_pos, lhs_end_pos, |
| MessageTemplate::kInvalidLhsInFor, kSyntaxError, CHECK_OK); |
| } |
| - ForEachStatement* loop = |
| - factory()->NewForEachStatement(mode, labels, stmt_pos); |
| - ParserTarget target(this, loop); |
| + auto loop = |
| + factory()->NewForEachStatement(for_info.mode, labels, stmt_pos); |
| + typename Types::Target target(this, loop); |
| - int each_keyword_position = scanner()->location().beg_pos; |
| + int each_keyword_pos = scanner()->location().beg_pos; |
| - Expression* enumerable; |
| - if (mode == ForEachStatement::ITERATE) { |
| + ExpressionT enumerable = impl()->EmptyExpression(); |
| + if (for_info.mode == ForEachStatement::ITERATE) { |
| ExpressionClassifier classifier(this); |
| enumerable = ParseAssignmentExpression(true, CHECK_OK); |
| - RewriteNonPattern(CHECK_OK); |
| + impl()->RewriteNonPattern(CHECK_OK); |
| } else { |
| enumerable = ParseExpression(true, CHECK_OK); |
| } |
| @@ -5237,10 +5158,10 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, |
| // For legacy compat reasons, give for loops similar treatment to |
| // if statements in allowing a function declaration for a body |
| - Statement* body = ParseScopedStatement(NULL, true, CHECK_OK); |
| + StatementT body = ParseScopedStatement(nullptr, true, CHECK_OK); |
| block_state.set_end_position(scanner()->location().end_pos); |
| - Statement* final_loop = InitializeForEachStatement( |
| - loop, expression, enumerable, body, each_keyword_position); |
| + StatementT final_loop = impl()->InitializeForEachStatement( |
| + loop, expression, enumerable, body, each_keyword_pos); |
| Scope* for_scope = for_state.FinalizedBlockScope(); |
| DCHECK_NULL(for_scope); |
| @@ -5251,27 +5172,27 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, |
| return final_loop; |
| } |
| } else { |
| + // Initializer is just an expression. |
| init = factory()->NewExpressionStatement(expression, lhs_beg_pos); |
| } |
| } |
| } |
| - // Standard 'for' loop |
| - ForStatement* loop = factory()->NewForStatement(labels, stmt_pos); |
| - ParserTarget target(this, loop); |
| + // Standard 'for' loop, we have parsed the initializer at this point. |
| + auto loop = factory()->NewForStatement(labels, stmt_pos); |
| + typename Types::Target target(this, loop); |
| - // Parsed initializer at this point. |
| Expect(Token::SEMICOLON, CHECK_OK); |
| - Expression* cond = NULL; |
| - Statement* next = NULL; |
| - Statement* body = NULL; |
| + ExpressionT cond = impl()->EmptyExpression(); |
| + StatementT next = impl()->NullStatement(); |
| + StatementT body = impl()->NullStatement(); |
| // If there are let bindings, then condition and the next statement of the |
| // for loop must be parsed in a new scope. |
| Scope* inner_scope = scope(); |
| // TODO(verwaest): Allocate this through a ScopeState as well. |
| - if (bound_names_are_lexical && bound_names.length() > 0) { |
| + if (for_info.bound_names_are_lexical && for_info.bound_names.length() > 0) { |
| inner_scope = NewScopeWithParent(inner_scope, BLOCK_SCOPE); |
| inner_scope->set_start_position(scanner()->location().beg_pos); |
| } |
| @@ -5284,24 +5205,23 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, |
| Expect(Token::SEMICOLON, CHECK_OK); |
| if (peek() != Token::RPAREN) { |
| - Expression* exp = ParseExpression(true, CHECK_OK); |
| + ExpressionT exp = ParseExpression(true, CHECK_OK); |
| next = factory()->NewExpressionStatement(exp, exp->position()); |
| } |
| Expect(Token::RPAREN, CHECK_OK); |
| - body = ParseScopedStatement(NULL, true, CHECK_OK); |
| + body = ParseScopedStatement(nullptr, true, CHECK_OK); |
| } |
| - Statement* result = NULL; |
| - if (bound_names_are_lexical && bound_names.length() > 0) { |
| - result = DesugarLexicalBindingsInForStatement( |
| - inner_scope, parsing_result.descriptor.mode, &bound_names, loop, init, |
| - cond, next, body, CHECK_OK); |
| + if (for_info.bound_names_are_lexical && for_info.bound_names.length() > 0) { |
| + auto result = impl()->DesugarLexicalBindingsInForStatement( |
| + loop, init, cond, next, body, inner_scope, &for_info, CHECK_OK); |
| for_state.set_end_position(scanner()->location().end_pos); |
| + return result; |
| } else { |
| for_state.set_end_position(scanner()->location().end_pos); |
| Scope* for_scope = for_state.FinalizedBlockScope(); |
| - if (for_scope) { |
| + if (for_scope != nullptr) { |
| // Rewrite a for statement of the form |
| // for (const x = i; c; n) b |
| // |
| @@ -5320,20 +5240,19 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels, |
| // } |
| // just in case b introduces a lexical binding some other way, e.g., if b |
| // is a FunctionDeclaration. |
| - Block* block = factory()->NewBlock(NULL, 2, false, kNoSourcePosition); |
| - if (init != nullptr) { |
| + BlockT block = factory()->NewBlock(NULL, 2, false, kNoSourcePosition); |
| + if (!impl()->IsNullStatement(init)) { |
| block->statements()->Add(init, zone()); |
| } |
| block->statements()->Add(loop, zone()); |
| block->set_scope(for_scope); |
| - loop->Initialize(NULL, cond, next, body); |
| - result = block; |
| + loop->Initialize(init, cond, next, body); |
| + return block; |
| } else { |
| loop->Initialize(init, cond, next, body); |
| - result = loop; |
| + return loop; |
| } |
| } |
| - return result; |
| } |
| #undef CHECK_OK |