| Index: src/parser.cc
|
| diff --git a/src/parser.cc b/src/parser.cc
|
| index 5e3db128ada4d98a13861b6fcd4bfa37551fdf57..26771e4e347830c81c13377cbd24c7d1d5897213 100644
|
| --- a/src/parser.cc
|
| +++ b/src/parser.cc
|
| @@ -14,6 +14,7 @@
|
| #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"
|
| @@ -2316,15 +2317,18 @@ Block* Parser::ParseVariableDeclarations(
|
| // ConstBinding ::
|
| // BindingPattern '=' AssignmentExpression
|
|
|
| - int pos = peek_position();
|
| - VariableMode mode = VAR;
|
| + PatternRewriter::DeclarationDescriptor decl;
|
| + decl.parser = this;
|
| + decl.pos = peek_position();
|
| + decl.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.
|
| - bool needs_init = false;
|
| - bool is_const = false;
|
| - Token::Value init_op = Token::INIT_VAR;
|
| + decl.needs_init = false;
|
| + decl.is_const = false;
|
| + decl.init_op = Token::INIT_VAR;
|
| + decl.names = names;
|
| if (peek() == Token::VAR) {
|
| if (is_strong(language_mode())) {
|
| Scanner::Location location = scanner()->peek_location();
|
| @@ -2336,27 +2340,29 @@ Block* Parser::ParseVariableDeclarations(
|
| } else if (peek() == Token::CONST) {
|
| Consume(Token::CONST);
|
| if (is_sloppy(language_mode())) {
|
| - mode = CONST_LEGACY;
|
| - init_op = Token::INIT_CONST_LEGACY;
|
| + decl.mode = CONST_LEGACY;
|
| + decl.init_op = Token::INIT_CONST_LEGACY;
|
| ++use_counts_[v8::Isolate::kLegacyConst];
|
| } else {
|
| DCHECK(var_context != kStatement);
|
| - mode = CONST;
|
| - init_op = Token::INIT_CONST;
|
| + decl.mode = CONST;
|
| + decl.init_op = Token::INIT_CONST;
|
| }
|
| - is_const = true;
|
| - needs_init = true;
|
| + decl.is_const = true;
|
| + decl.needs_init = true;
|
| } else if (peek() == Token::LET && is_strict(language_mode())) {
|
| Consume(Token::LET);
|
| DCHECK(var_context != kStatement);
|
| - mode = LET;
|
| - needs_init = true;
|
| - init_op = Token::INIT_LET;
|
| + decl.mode = LET;
|
| + decl.needs_init = true;
|
| + decl.init_op = Token::INIT_LET;
|
| } else {
|
| UNREACHABLE(); // by current callers
|
| }
|
|
|
| - Scope* declaration_scope = DeclarationScope(mode);
|
| + decl.declaration_scope = DeclarationScope(decl.mode);
|
| + decl.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
|
| @@ -2371,10 +2377,9 @@ Block* Parser::ParseVariableDeclarations(
|
| // is inside an initializer block, it is ignored.
|
| //
|
| // Create new block with one expected declaration.
|
| - Block* block = factory()->NewBlock(NULL, 1, true, pos);
|
| + decl.block = factory()->NewBlock(NULL, 1, true, decl.pos);
|
| int nvars = 0; // the number of variables declared
|
| int bindings_start = peek_position();
|
| - const AstRawString* name = NULL;
|
| const AstRawString* first_name = NULL;
|
| bool is_for_iteration_variable;
|
| do {
|
| @@ -2383,101 +2388,46 @@ Block* Parser::ParseVariableDeclarations(
|
| // Parse variable name.
|
| if (nvars > 0) Consume(Token::COMMA);
|
|
|
| + PatternRewriter pattern_rewriter;
|
| {
|
| ExpressionClassifier pattern_classifier;
|
| Token::Value next = peek();
|
| Expression* pattern =
|
| ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
|
| ValidateBindingPattern(&pattern_classifier, CHECK_OK);
|
| - if (pattern->IsVariableProxy() &&
|
| - pattern->AsVariableProxy()->IsValidReferenceExpression()) {
|
| - scope_->RemoveUnresolved(pattern->AsVariableProxy());
|
| - name = pattern->AsVariableProxy()->raw_name();
|
| - } else if (allow_harmony_destructuring()) {
|
| - // TODO(dslomov): really destructure.
|
| - name = ast_value_factory()->GetOneByteString(".temp.variable");
|
| - } else {
|
| + pattern_rewriter = PatternRewriter(&decl, pattern);
|
| + if (!allow_harmony_destructuring() &&
|
| + !pattern_rewriter.IsSingleVariableBinding()) {
|
| ReportUnexpectedToken(next);
|
| *ok = false;
|
| return nullptr;
|
| }
|
| +
|
| + // TODO(dslomov): unify
|
| }
|
|
|
| - if (!first_name) first_name = name;
|
| Scanner::Location variable_loc = scanner()->location();
|
| - if (fni_ != NULL) fni_->PushVariableName(name);
|
| -
|
| - // Declare variable.
|
| - // Note that we *always* must treat the initial value via a separate init
|
| - // assignment for variables and constants because the value must be assigned
|
| - // when the variable is encountered in the source. But the variable/constant
|
| - // is declared (and set to 'undefined') upon entering the function within
|
| - // which the variable or constant is declared. Only function variables have
|
| - // an initial value in the declaration (because they are initialized upon
|
| - // entering the function).
|
| - //
|
| - // If we have a const declaration, in an inner scope, the proxy is always
|
| - // bound to the declared variable (independent of possibly surrounding with
|
| - // statements).
|
| - // For let/const declarations in harmony mode, we can also immediately
|
| - // pre-resolve the proxy because it resides in the same scope as the
|
| - // declaration.
|
| + 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());
|
| + }
|
| +
|
| is_for_iteration_variable =
|
| var_context == kForStatement &&
|
| (peek() == Token::IN || PeekContextualKeyword(CStrVector("of")));
|
| - if (is_for_iteration_variable && mode == CONST) {
|
| - needs_init = false;
|
| + if (is_for_iteration_variable && decl.mode == CONST) {
|
| + decl.needs_init = false;
|
| }
|
|
|
| - VariableProxy* proxy = NewUnresolved(name, mode);
|
| - Declaration* declaration =
|
| - factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
|
| - Variable* var = Declare(declaration, mode != VAR, CHECK_OK);
|
| - DCHECK_NOT_NULL(var);
|
| - DCHECK(!proxy->is_resolved() || proxy->var() == var);
|
| - nvars++;
|
| - if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
|
| - ReportMessage("too_many_variables");
|
| - *ok = false;
|
| - return NULL;
|
| - }
|
| - if (names) names->Add(name, zone());
|
| -
|
| - // Parse initialization expression if present and/or needed. A
|
| - // declaration of the form:
|
| - //
|
| - // var v = x;
|
| - //
|
| - // is syntactic sugar for:
|
| - //
|
| - // var v; v = x;
|
| - //
|
| - // In particular, we need to re-lookup 'v' (in scope_, not
|
| - // declaration_scope) as it may be a different 'v' than the 'v' in the
|
| - // declaration (e.g., if we are inside a 'with' statement or 'catch'
|
| - // block).
|
| - //
|
| - // However, note that const declarations are different! A const
|
| - // declaration of the form:
|
| - //
|
| - // const c = x;
|
| - //
|
| - // is *not* syntactic sugar for:
|
| - //
|
| - // const c; c = x;
|
| - //
|
| - // The "variable" c initialized to x is the same as the declared
|
| - // one - there is no re-lookup (see the last parameter of the
|
| - // Declare() call above).
|
| -
|
| - Scope* initialization_scope = is_const ? declaration_scope : scope_;
|
| Expression* value = NULL;
|
| - int pos = -1;
|
| + decl.pos = -1;
|
| + decl.initializer_position = -1;
|
| // Harmony consts have non-optional initializers.
|
| if (peek() == Token::ASSIGN ||
|
| - (mode == CONST && !is_for_iteration_variable)) {
|
| + (decl.mode == CONST && !is_for_iteration_variable)) {
|
| Expect(Token::ASSIGN, CHECK_OK);
|
| - pos = position();
|
| + decl.pos = position();
|
| ExpressionClassifier classifier;
|
| value = ParseAssignmentExpression(var_context != kForStatement,
|
| &classifier, CHECK_OK);
|
| @@ -2489,129 +2439,29 @@ Block* Parser::ParseVariableDeclarations(
|
| }
|
|
|
| // Don't infer if it is "a = function(){...}();"-like expression.
|
| - if (fni_ != NULL &&
|
| - value->AsCall() == NULL &&
|
| - value->AsCallNew() == NULL) {
|
| - fni_->Infer();
|
| - } else {
|
| - fni_->RemoveLastFunction();
|
| + if (single_name) {
|
| + if (fni_ != NULL && value->AsCall() == NULL &&
|
| + value->AsCallNew() == NULL) {
|
| + fni_->Infer();
|
| + } else {
|
| + fni_->RemoveLastFunction();
|
| + }
|
| }
|
| // End position of the initializer is after the assignment expression.
|
| - var->set_initializer_position(scanner()->location().end_pos);
|
| + decl.initializer_position = scanner()->location().end_pos;
|
| } else {
|
| // End position of the initializer is after the variable.
|
| - var->set_initializer_position(position());
|
| + decl.initializer_position = position();
|
| }
|
|
|
| // Make sure that 'const x' and 'let x' initialize 'x' to undefined.
|
| - if (value == NULL && needs_init) {
|
| + if (value == NULL && decl.needs_init) {
|
| value = GetLiteralUndefined(position());
|
| }
|
|
|
| - // Global variable declarations must be compiled in a specific
|
| - // way. When the script containing the global variable declaration
|
| - // is entered, the global variable must be declared, so that if it
|
| - // doesn't exist (on the global object itself, see ES5 errata) it
|
| - // gets created with an initial undefined value. This is handled
|
| - // by the declarations part of the function representing the
|
| - // top-level global code; see Runtime::DeclareGlobalVariable. If
|
| - // it already exists (in the object or in a prototype), it is
|
| - // *not* touched until the variable declaration statement is
|
| - // executed.
|
| - //
|
| - // Executing the variable declaration statement will always
|
| - // guarantee to give the global object an own property.
|
| - // This way, global variable declarations can shadow
|
| - // properties in the prototype chain, but only after the variable
|
| - // declaration statement has been executed. This is important in
|
| - // browsers where the global object (window) has lots of
|
| - // properties defined in prototype objects.
|
| - if (initialization_scope->is_script_scope() &&
|
| - !IsLexicalVariableMode(mode)) {
|
| - // Compute the arguments for the runtime call.
|
| - ZoneList<Expression*>* arguments =
|
| - new(zone()) ZoneList<Expression*>(3, zone());
|
| - // We have at least 1 parameter.
|
| - arguments->Add(factory()->NewStringLiteral(name, pos), zone());
|
| - CallRuntime* initialize;
|
| -
|
| - if (is_const) {
|
| - arguments->Add(value, zone());
|
| - value = NULL; // zap the value to avoid the unnecessary assignment
|
| -
|
| - // Construct the call to Runtime_InitializeConstGlobal
|
| - // and add it to the initialization statement block.
|
| - // Note that the function does different things depending on
|
| - // the number of arguments (1 or 2).
|
| - initialize = factory()->NewCallRuntime(
|
| - ast_value_factory()->initialize_const_global_string(),
|
| - Runtime::FunctionForId(Runtime::kInitializeConstGlobal), arguments,
|
| - pos);
|
| - } else {
|
| - // Add language mode.
|
| - // We may want to pass singleton to avoid Literal allocations.
|
| - LanguageMode language_mode = initialization_scope->language_mode();
|
| - arguments->Add(factory()->NewNumberLiteral(language_mode, pos), zone());
|
| -
|
| - // Be careful not to assign a value to the global variable if
|
| - // we're in a with. The initialization value should not
|
| - // necessarily be stored in the global object in that case,
|
| - // which is why we need to generate a separate assignment node.
|
| - if (value != NULL && !inside_with()) {
|
| - arguments->Add(value, zone());
|
| - value = NULL; // zap the value to avoid the unnecessary assignment
|
| - // Construct the call to Runtime_InitializeVarGlobal
|
| - // and add it to the initialization statement block.
|
| - initialize = factory()->NewCallRuntime(
|
| - ast_value_factory()->initialize_var_global_string(),
|
| - Runtime::FunctionForId(Runtime::kInitializeVarGlobal), arguments,
|
| - pos);
|
| - } else {
|
| - initialize = NULL;
|
| - }
|
| - }
|
| -
|
| - if (initialize != NULL) {
|
| - block->AddStatement(factory()->NewExpressionStatement(
|
| - initialize, RelocInfo::kNoPosition),
|
| - zone());
|
| - }
|
| - } else if (needs_init) {
|
| - // Constant initializations always assign to the declared constant which
|
| - // is always at the function scope level. This is only relevant for
|
| - // dynamically looked-up variables and constants (the start context for
|
| - // constant lookups is always the function context, while it is the top
|
| - // context for var declared variables). Sigh...
|
| - // For 'let' and 'const' declared variables in harmony mode the
|
| - // initialization also always assigns to the declared variable.
|
| - DCHECK(proxy != NULL);
|
| - DCHECK(proxy->var() != NULL);
|
| - DCHECK(value != NULL);
|
| - Assignment* assignment =
|
| - factory()->NewAssignment(init_op, proxy, value, pos);
|
| - block->AddStatement(
|
| - factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
|
| - zone());
|
| - value = NULL;
|
| - }
|
| -
|
| - // Add an assignment node to the initialization statement block if we still
|
| - // have a pending initialization value.
|
| - if (value != NULL) {
|
| - DCHECK(mode == VAR);
|
| - // 'var' initializations are simply assignments (with all the consequences
|
| - // if they are inside a 'with' statement - they may change a 'with' object
|
| - // property).
|
| - VariableProxy* proxy =
|
| - initialization_scope->NewUnresolved(factory(), name);
|
| - Assignment* assignment =
|
| - factory()->NewAssignment(init_op, proxy, value, pos);
|
| - block->AddStatement(
|
| - factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
|
| - zone());
|
| - }
|
| + pattern_rewriter.DeclareAndInitializeVariables(value, &nvars, CHECK_OK);
|
|
|
| - if (fni_ != NULL) fni_->Leave();
|
| + if (single_name && fni_ != NULL) fni_->Leave();
|
| } while (peek() == Token::COMMA);
|
|
|
| if (bindings_loc) {
|
| @@ -2622,7 +2472,7 @@ Block* Parser::ParseVariableDeclarations(
|
| if (num_decl) *num_decl = nvars;
|
| *out = first_name;
|
|
|
| - return block;
|
| + return decl.block;
|
| }
|
|
|
|
|
|
|