| Index: src/parser.cc
|
| diff --git a/src/parser.cc b/src/parser.cc
|
| index ceeab6cfb175d474254380db190168bea7333b38..5be6d82b67d582b11601b227c0f7b9d739ba2480 100644
|
| --- a/src/parser.cc
|
| +++ b/src/parser.cc
|
| @@ -14,7 +14,6 @@
|
| #include "src/compiler.h"
|
| #include "src/messages.h"
|
| #include "src/parser.h"
|
| -#include "src/pattern-rewriter.h"
|
| #include "src/preparser.h"
|
| #include "src/runtime/runtime.h"
|
| #include "src/scanner-character-streams.h"
|
| @@ -2272,30 +2271,58 @@ Block* Parser::ParseScopedBlock(ZoneList<const AstRawString*>* labels,
|
| }
|
|
|
|
|
| +const AstRawString* Parser::DeclarationParsingResult::SingleName() const {
|
| + if (declarations.length() != 1) return nullptr;
|
| + const Declaration& declaration = declarations.at(0);
|
| + if (declaration.pattern->IsVariableProxy()) {
|
| + return declaration.pattern->AsVariableProxy()->raw_name();
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +
|
| +Block* Parser::DeclarationParsingResult::BuildInitializationBlock(
|
| + ZoneList<const AstRawString*>* names, bool* ok) {
|
| + Block* result =
|
| + descriptor.parser->factory()->NewBlock(NULL, 1, true, descriptor.pos);
|
| + for (auto declaration : declarations) {
|
| + PatternRewriter::DeclareAndInitializeVariables(
|
| + result, &descriptor, &declaration, names, CHECK_OK);
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +
|
| Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
|
| ZoneList<const AstRawString*>* names,
|
| bool* ok) {
|
| // VariableStatement ::
|
| // VariableDeclarations ';'
|
|
|
| - const AstRawString* ignore;
|
| - Block* result = ParseVariableDeclarations(
|
| - var_context, nullptr, names, &ignore, nullptr, nullptr, CHECK_OK);
|
| + // The scope of a var/const declared variable anywhere inside a function
|
| + // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can
|
| + // transform a source-level var/const declaration into a (Function)
|
| + // Scope declaration, and rewrite the source-level initialization into an
|
| + // assignment statement. We use a block to collect multiple assignments.
|
| + //
|
| + // We mark the block as initializer block because we don't want the
|
| + // rewriter to add a '.result' assignment to such a block (to get compliant
|
| + // behavior for code such as print(eval('var x = 7')), and for cosmetic
|
| + // reasons when pretty-printing. Also, unless an assignment (initialization)
|
| + // is inside an initializer block, it is ignored.
|
| +
|
| + DeclarationParsingResult parsing_result;
|
| + ParseVariableDeclarations(var_context, &parsing_result, CHECK_OK);
|
| ExpectSemicolon(CHECK_OK);
|
| +
|
| + Block* result = parsing_result.BuildInitializationBlock(names, CHECK_OK);
|
| return result;
|
| }
|
|
|
|
|
| -// If the variable declaration declares exactly one non-const
|
| -// variable, then *out is set to that variable. In all other cases,
|
| -// *out is untouched; in particular, it is the caller's responsibility
|
| -// to initialize it properly. This mechanism is used for the parsing
|
| -// of 'for-in' loops.
|
| -Block* Parser::ParseVariableDeclarations(
|
| - VariableDeclarationContext var_context, int* num_decl,
|
| - ZoneList<const AstRawString*>* names, const AstRawString** out,
|
| - Scanner::Location* first_initializer_loc, Scanner::Location* bindings_loc,
|
| - bool* ok) {
|
| +void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context,
|
| + DeclarationParsingResult* parsing_result,
|
| + bool* ok) {
|
| // VariableDeclarations ::
|
| // ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[',']
|
| //
|
| @@ -2310,125 +2337,108 @@ Block* Parser::ParseVariableDeclarations(
|
| // ConstBinding ::
|
| // BindingPattern '=' AssignmentExpression
|
|
|
| - PatternRewriter::DeclarationDescriptor decl;
|
| - decl.parser = this;
|
| - decl.pos = peek_position();
|
| - decl.mode = VAR;
|
| + parsing_result->descriptor.parser = this;
|
| + parsing_result->descriptor.pos = peek_position();
|
| + parsing_result->descriptor.mode = VAR;
|
| // True if the binding needs initialization. 'let' and 'const' declared
|
| // bindings are created uninitialized by their declaration nodes and
|
| // need initialization. 'var' declared bindings are always initialized
|
| // immediately by their declaration nodes.
|
| - decl.needs_init = false;
|
| - decl.is_const = false;
|
| - decl.init_op = Token::INIT_VAR;
|
| - decl.names = names;
|
| + parsing_result->descriptor.needs_init = false;
|
| + parsing_result->descriptor.is_const = false;
|
| + parsing_result->descriptor.init_op = Token::INIT_VAR;
|
| if (peek() == Token::VAR) {
|
| if (is_strong(language_mode())) {
|
| Scanner::Location location = scanner()->peek_location();
|
| ReportMessageAt(location, "strong_var");
|
| *ok = false;
|
| - return NULL;
|
| + return;
|
| }
|
| Consume(Token::VAR);
|
| } else if (peek() == Token::CONST) {
|
| Consume(Token::CONST);
|
| if (is_sloppy(language_mode())) {
|
| - decl.mode = CONST_LEGACY;
|
| - decl.init_op = Token::INIT_CONST_LEGACY;
|
| + parsing_result->descriptor.mode = CONST_LEGACY;
|
| + parsing_result->descriptor.init_op = Token::INIT_CONST_LEGACY;
|
| ++use_counts_[v8::Isolate::kLegacyConst];
|
| } else {
|
| DCHECK(var_context != kStatement);
|
| - decl.mode = CONST;
|
| - decl.init_op = Token::INIT_CONST;
|
| + parsing_result->descriptor.mode = CONST;
|
| + parsing_result->descriptor.init_op = Token::INIT_CONST;
|
| }
|
| - decl.is_const = true;
|
| - decl.needs_init = true;
|
| + parsing_result->descriptor.is_const = true;
|
| + parsing_result->descriptor.needs_init = true;
|
| } else if (peek() == Token::LET && is_strict(language_mode())) {
|
| Consume(Token::LET);
|
| DCHECK(var_context != kStatement);
|
| - decl.mode = LET;
|
| - decl.needs_init = true;
|
| - decl.init_op = Token::INIT_LET;
|
| + parsing_result->descriptor.mode = LET;
|
| + parsing_result->descriptor.needs_init = true;
|
| + parsing_result->descriptor.init_op = Token::INIT_LET;
|
| } else {
|
| UNREACHABLE(); // by current callers
|
| }
|
|
|
| - decl.declaration_scope = DeclarationScope(decl.mode);
|
| - decl.scope = scope_;
|
| + parsing_result->descriptor.declaration_scope =
|
| + DeclarationScope(parsing_result->descriptor.mode);
|
| + parsing_result->descriptor.scope = scope_;
|
|
|
|
|
| - // The scope of a var/const declared variable anywhere inside a function
|
| - // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can
|
| - // transform a source-level var/const declaration into a (Function)
|
| - // Scope declaration, and rewrite the source-level initialization into an
|
| - // assignment statement. We use a block to collect multiple assignments.
|
| - //
|
| - // We mark the block as initializer block because we don't want the
|
| - // rewriter to add a '.result' assignment to such a block (to get compliant
|
| - // behavior for code such as print(eval('var x = 7')), and for cosmetic
|
| - // reasons when pretty-printing. Also, unless an assignment (initialization)
|
| - // is inside an initializer block, it is ignored.
|
| - //
|
| - // Create new block with one expected declaration.
|
| - decl.block = factory()->NewBlock(NULL, 1, true, decl.pos);
|
| - int nvars = 0; // the number of variables declared
|
| + bool first_declaration = true;
|
| int bindings_start = peek_position();
|
| - const AstRawString* first_name = NULL;
|
| bool is_for_iteration_variable;
|
| do {
|
| if (fni_ != NULL) fni_->Enter();
|
|
|
| - // Parse variable name.
|
| - if (nvars > 0) Consume(Token::COMMA);
|
| + // Parse name.
|
| + if (!first_declaration) Consume(Token::COMMA);
|
|
|
| - PatternRewriter pattern_rewriter;
|
| + Expression* pattern;
|
| {
|
| ExpressionClassifier pattern_classifier;
|
| Token::Value next = peek();
|
| - Expression* pattern =
|
| - ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
|
| - ValidateBindingPattern(&pattern_classifier, CHECK_OK);
|
| - pattern_rewriter = PatternRewriter(&decl, pattern);
|
| - if (!allow_harmony_destructuring() &&
|
| - !pattern_rewriter.IsSingleVariableBinding()) {
|
| + pattern = ParsePrimaryExpression(&pattern_classifier, ok);
|
| + if (!*ok) return;
|
| + ValidateBindingPattern(&pattern_classifier, ok);
|
| + if (!*ok) return;
|
| + if (!allow_harmony_destructuring() && !pattern->IsVariableProxy()) {
|
| ReportUnexpectedToken(next);
|
| *ok = false;
|
| - return nullptr;
|
| + return;
|
| }
|
| -
|
| - // TODO(dslomov): unify
|
| }
|
|
|
| Scanner::Location variable_loc = scanner()->location();
|
| - const bool single_name = pattern_rewriter.IsSingleVariableBinding();
|
| - if (single_name) {
|
| - if (!first_name) first_name = pattern_rewriter.SingleName();
|
| - if (fni_ != NULL) fni_->PushVariableName(pattern_rewriter.SingleName());
|
| + const AstRawString* single_name =
|
| + pattern->IsVariableProxy() ? pattern->AsVariableProxy()->raw_name()
|
| + : nullptr;
|
| + if (single_name != nullptr) {
|
| + if (fni_ != NULL) fni_->PushVariableName(single_name);
|
| }
|
|
|
| is_for_iteration_variable =
|
| var_context == kForStatement &&
|
| (peek() == Token::IN || PeekContextualKeyword(CStrVector("of")));
|
| - if (is_for_iteration_variable && decl.mode == CONST) {
|
| - decl.needs_init = false;
|
| + if (is_for_iteration_variable && parsing_result->descriptor.mode == CONST) {
|
| + parsing_result->descriptor.needs_init = false;
|
| }
|
|
|
| Expression* value = NULL;
|
| - decl.pos = -1;
|
| - decl.initializer_position = -1;
|
| // Harmony consts have non-optional initializers.
|
| - if (peek() == Token::ASSIGN ||
|
| - (decl.mode == CONST && !is_for_iteration_variable)) {
|
| - Expect(Token::ASSIGN, CHECK_OK);
|
| - decl.pos = position();
|
| + int initializer_position = RelocInfo::kNoPosition;
|
| + if (peek() == Token::ASSIGN || (parsing_result->descriptor.mode == CONST &&
|
| + !is_for_iteration_variable)) {
|
| + Expect(Token::ASSIGN, ok);
|
| + if (!*ok) return;
|
| ExpressionClassifier classifier;
|
| value = ParseAssignmentExpression(var_context != kForStatement,
|
| - &classifier, CHECK_OK);
|
| - ValidateExpression(&classifier, CHECK_OK);
|
| + &classifier, ok);
|
| + if (!*ok) return;
|
| + ValidateExpression(&classifier, ok);
|
| + if (!*ok) return;
|
| variable_loc.end_pos = scanner()->location().end_pos;
|
|
|
| - if (first_initializer_loc && !first_initializer_loc->IsValid()) {
|
| - *first_initializer_loc = variable_loc;
|
| + if (!parsing_result->first_initializer_loc.IsValid()) {
|
| + parsing_result->first_initializer_loc = variable_loc;
|
| }
|
|
|
| // Don't infer if it is "a = function(){...}();"-like expression.
|
| @@ -2441,31 +2451,25 @@ Block* Parser::ParseVariableDeclarations(
|
| }
|
| }
|
| // End position of the initializer is after the assignment expression.
|
| - decl.initializer_position = scanner()->location().end_pos;
|
| + initializer_position = scanner()->location().end_pos;
|
| } else {
|
| // End position of the initializer is after the variable.
|
| - decl.initializer_position = position();
|
| + initializer_position = position();
|
| }
|
|
|
| // Make sure that 'const x' and 'let x' initialize 'x' to undefined.
|
| - if (value == NULL && decl.needs_init) {
|
| + if (value == NULL && parsing_result->descriptor.needs_init) {
|
| value = GetLiteralUndefined(position());
|
| }
|
|
|
| - pattern_rewriter.DeclareAndInitializeVariables(value, &nvars, CHECK_OK);
|
| -
|
| if (single_name && fni_ != NULL) fni_->Leave();
|
| + parsing_result->declarations.Add(DeclarationParsingResult::Declaration(
|
| + pattern, initializer_position, value));
|
| + first_declaration = false;
|
| } while (peek() == Token::COMMA);
|
|
|
| - if (bindings_loc) {
|
| - *bindings_loc =
|
| - Scanner::Location(bindings_start, scanner()->location().end_pos);
|
| - }
|
| -
|
| - if (num_decl) *num_decl = nvars;
|
| - *out = first_name;
|
| -
|
| - return decl.block;
|
| + parsing_result->bindings_loc =
|
| + Scanner::Location(bindings_start, scanner()->location().end_pos);
|
| }
|
|
|
|
|
| @@ -3232,7 +3236,6 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
|
| Block* inner_block = factory()->NewBlock(NULL, names->length() + 4, false,
|
| RelocInfo::kNoPosition);
|
| ZoneList<Variable*> inner_vars(names->length(), zone());
|
| -
|
| // For each let variable x:
|
| // make statement: let/const x = temp_x.
|
| VariableMode mode = is_const ? CONST : LET;
|
| @@ -3248,6 +3251,7 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
|
| proxy, temp_proxy, RelocInfo::kNoPosition);
|
| Statement* assignment_statement =
|
| factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
|
| + DCHECK(init->position() != RelocInfo::kNoPosition);
|
| proxy->var()->set_initializer_position(init->position());
|
| inner_block->AddStatement(assignment_statement, zone());
|
| }
|
| @@ -3384,21 +3388,19 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
| Scope* saved_scope = scope_;
|
| Scope* for_scope = NewScope(scope_, BLOCK_SCOPE);
|
| scope_ = for_scope;
|
| -
|
| Expect(Token::FOR, CHECK_OK);
|
| Expect(Token::LPAREN, CHECK_OK);
|
| for_scope->set_start_position(scanner()->location().beg_pos);
|
| bool is_let_identifier_expression = false;
|
| + DeclarationParsingResult parsing_result;
|
| if (peek() != Token::SEMICOLON) {
|
| if (peek() == Token::VAR ||
|
| (peek() == Token::CONST && is_sloppy(language_mode()))) {
|
| - const AstRawString* name = NULL;
|
| - Scanner::Location first_initializer_loc = Scanner::Location::invalid();
|
| - Scanner::Location bindings_loc = Scanner::Location::invalid();
|
| - int num_decl;
|
| - Block* variable_statement = ParseVariableDeclarations(
|
| - kForStatement, &num_decl, nullptr, &name, &first_initializer_loc,
|
| - &bindings_loc, CHECK_OK);
|
| + ParseVariableDeclarations(kForStatement, &parsing_result, CHECK_OK);
|
| + Block* variable_statement =
|
| + parsing_result.BuildInitializationBlock(nullptr, CHECK_OK);
|
| +
|
| + int num_decl = parsing_result.declarations.length();
|
| bool accept_IN = num_decl >= 1;
|
| bool accept_OF = true;
|
| ForEachStatement::VisitMode mode;
|
| @@ -3410,18 +3412,21 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
| if (num_decl != 1) {
|
| const char* loop_type =
|
| mode == ForEachStatement::ITERATE ? "for-of" : "for-in";
|
| - ParserTraits::ReportMessageAt(
|
| - bindings_loc, "for_inof_loop_multi_bindings", loop_type);
|
| + ParserTraits::ReportMessageAt(parsing_result.bindings_loc,
|
| + "for_inof_loop_multi_bindings",
|
| + loop_type);
|
| *ok = false;
|
| return nullptr;
|
| }
|
| - if (first_initializer_loc.IsValid() &&
|
| + if (parsing_result.first_initializer_loc.IsValid() &&
|
| (is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
|
| if (mode == ForEachStatement::ITERATE) {
|
| - ReportMessageAt(first_initializer_loc, "for_of_loop_initializer");
|
| + ReportMessageAt(parsing_result.first_initializer_loc,
|
| + "for_of_loop_initializer");
|
| } else {
|
| // TODO(caitp): This should be an error in sloppy mode too.
|
| - ReportMessageAt(first_initializer_loc, "for_in_loop_initializer");
|
| + ReportMessageAt(parsing_result.first_initializer_loc,
|
| + "for_in_loop_initializer");
|
| }
|
| *ok = false;
|
| return nullptr;
|
| @@ -3433,8 +3438,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
| Expression* enumerable = ParseExpression(true, CHECK_OK);
|
| Expect(Token::RPAREN, CHECK_OK);
|
|
|
| - VariableProxy* each =
|
| - scope_->NewUnresolved(factory(), name, each_beg_pos, each_end_pos);
|
| + VariableProxy* each = scope_->NewUnresolved(
|
| + factory(), parsing_result.SingleName(), each_beg_pos, each_end_pos);
|
| Statement* body = ParseSubStatement(NULL, CHECK_OK);
|
| InitializeForEachStatement(loop, each, enumerable, body);
|
| Block* result =
|
| @@ -3453,13 +3458,12 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
| } else if ((peek() == Token::LET || peek() == Token::CONST) &&
|
| is_strict(language_mode())) {
|
| is_const = peek() == Token::CONST;
|
| - const AstRawString* name = NULL;
|
| - Scanner::Location first_initializer_loc = Scanner::Location::invalid();
|
| - Scanner::Location bindings_loc = Scanner::Location::invalid();
|
| - int num_decl;
|
| - Block* variable_statement = ParseVariableDeclarations(
|
| - kForStatement, &num_decl, &lexical_bindings, &name,
|
| - &first_initializer_loc, &bindings_loc, CHECK_OK);
|
| + ParseVariableDeclarations(kForStatement, &parsing_result, CHECK_OK);
|
| + DCHECK(parsing_result.descriptor.pos != RelocInfo::kNoPosition);
|
| + Block* variable_statement =
|
| + parsing_result.BuildInitializationBlock(&lexical_bindings, CHECK_OK);
|
| +
|
| + int num_decl = parsing_result.declarations.length();
|
| bool accept_IN = num_decl >= 1;
|
| bool accept_OF = true;
|
| ForEachStatement::VisitMode mode;
|
| @@ -3471,17 +3475,20 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
| if (num_decl != 1) {
|
| const char* loop_type =
|
| mode == ForEachStatement::ITERATE ? "for-of" : "for-in";
|
| - ParserTraits::ReportMessageAt(
|
| - bindings_loc, "for_inof_loop_multi_bindings", loop_type);
|
| + ParserTraits::ReportMessageAt(parsing_result.bindings_loc,
|
| + "for_inof_loop_multi_bindings",
|
| + loop_type);
|
| *ok = false;
|
| return nullptr;
|
| }
|
| - if (first_initializer_loc.IsValid() &&
|
| + if (parsing_result.first_initializer_loc.IsValid() &&
|
| (is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
|
| if (mode == ForEachStatement::ITERATE) {
|
| - ReportMessageAt(first_initializer_loc, "for_of_loop_initializer");
|
| + ReportMessageAt(parsing_result.first_initializer_loc,
|
| + "for_of_loop_initializer");
|
| } else {
|
| - ReportMessageAt(first_initializer_loc, "for_in_loop_initializer");
|
| + ReportMessageAt(parsing_result.first_initializer_loc,
|
| + "for_in_loop_initializer");
|
| }
|
| *ok = false;
|
| return nullptr;
|
| @@ -3515,8 +3522,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
| scope_ = for_scope;
|
| Expect(Token::RPAREN, CHECK_OK);
|
|
|
| - VariableProxy* each =
|
| - scope_->NewUnresolved(factory(), name, each_beg_pos, each_end_pos);
|
| + VariableProxy* each = scope_->NewUnresolved(
|
| + factory(), parsing_result.SingleName(), each_end_pos);
|
| Statement* body = ParseSubStatement(NULL, CHECK_OK);
|
| Block* body_block =
|
| factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
|
|
|