[destructuring] Grand for statement parsing unification.
authordslomov <dslomov@chromium.org>
Thu, 21 May 2015 17:47:07 +0000 (10:47 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 21 May 2015 17:46:45 +0000 (17:46 +0000)
Also support patterns in ``for (var p in/of ...)``

This CL extends the rewriting we used to do for ``for (let p in/of...)`` to
``for (var p in/of ...)``. For all for..in/of loop declaring variable,
we rewrite
   for (var/let/const pattern in/of e) b
into
   for (x' in/of e) { var/let/const pattern = e; b }

This adds a small complication for debugger: for a statement
   for (var v in/of e) ...
we used to have
   var v;
   for (v in/of e) ...
and there was a separate breakpoint on ``var v`` line.
This breakpoint is actually useless since it is immediately followed by
a breakpoint on evaluation of ``e``, so this CL removes that breakpoint
location.

Similiraly, for let, it used to be that
  for (let v in/of e) ...
became
  for (x' in/of e) { let v; v  = x'; ... }
``let v``generetaed a useless breakpoint (with the location at the
loop's head. This CL removes that breakpoint as well.

R=arv@chromium.org,rossberg@chromium.org
BUG=v8:811
LOG=N

Review URL: https://codereview.chromium.org/1149043005

Cr-Commit-Position: refs/heads/master@{#28565}

src/parser.cc
src/parser.h
src/pattern-rewriter.cc
test/cctest/test-parsing.cc
test/mjsunit/es6/debug-stepnext-for.js
test/mjsunit/harmony/destructuring.js

index b9d2c2f9e7df66c6e5e0feebf727001aac2915be..ee0f7dc6804848109197ed9878835d791e3d77f4 100644 (file)
@@ -2295,8 +2295,8 @@ const AstRawString* Parser::DeclarationParsingResult::SingleName() const {
 
 Block* Parser::DeclarationParsingResult::BuildInitializationBlock(
     ZoneList<const AstRawString*>* names, bool* ok) {
-  Block* result =
-      descriptor.parser->factory()->NewBlock(NULL, 1, true, descriptor.pos);
+  Block* result = descriptor.parser->factory()->NewBlock(
+      NULL, 1, true, descriptor.declaration_pos);
   for (auto declaration : declarations) {
     PatternRewriter::DeclareAndInitializeVariables(
         result, &descriptor, &declaration, names, CHECK_OK);
@@ -2350,7 +2350,8 @@ void Parser::ParseVariableDeclarations(VariableDeclarationContext var_context,
   //   BindingPattern '=' AssignmentExpression
 
   parsing_result->descriptor.parser = this;
-  parsing_result->descriptor.pos = peek_position();
+  parsing_result->descriptor.declaration_pos = peek_position();
+  parsing_result->descriptor.initialization_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
@@ -3417,11 +3418,10 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
   bool is_let_identifier_expression = false;
   DeclarationParsingResult parsing_result;
   if (peek() != Token::SEMICOLON) {
-    if (peek() == Token::VAR ||
-        (peek() == Token::CONST && is_sloppy(language_mode()))) {
+    if (peek() == Token::VAR || peek() == Token::CONST ||
+        (peek() == Token::LET && is_strict(language_mode()))) {
       ParseVariableDeclarations(kForStatement, &parsing_result, CHECK_OK);
-      Block* variable_statement =
-          parsing_result.BuildInitializationBlock(nullptr, CHECK_OK);
+      is_const = parsing_result.descriptor.mode == CONST;
 
       int num_decl = parsing_result.declarations.length();
       bool accept_IN = num_decl >= 1;
@@ -3454,124 +3454,100 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
           *ok = false;
           return nullptr;
         }
-        ForEachStatement* loop =
-            factory()->NewForEachStatement(mode, labels, stmt_pos);
-        Target target(&this->target_stack_, loop);
 
-        Expression* enumerable = ParseExpression(true, CHECK_OK);
-        Expect(Token::RPAREN, CHECK_OK);
-
-        VariableProxy* each =
-            scope_->NewUnresolved(factory(), parsing_result.SingleName(),
-                                  Variable::NORMAL, each_beg_pos, each_end_pos);
-        Statement* body = ParseSubStatement(NULL, CHECK_OK);
-        InitializeForEachStatement(loop, each, enumerable, body);
-        Block* result =
-            factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition);
-        result->AddStatement(variable_statement, zone());
-        result->AddStatement(loop, zone());
-        scope_ = saved_scope;
-        for_scope->set_end_position(scanner()->location().end_pos);
-        for_scope = for_scope->FinalizeBlockScope();
-        DCHECK(for_scope == NULL);
-        // Parsed for-in loop w/ variable/const declaration.
-        return result;
-      } else {
-        init = variable_statement;
-      }
-    } else if ((peek() == Token::LET || peek() == Token::CONST) &&
-               is_strict(language_mode())) {
-      is_const = peek() == Token::CONST;
-      ParseVariableDeclarations(kForStatement, &parsing_result, CHECK_OK);
-      DCHECK(parsing_result.descriptor.pos != RelocInfo::kNoPosition);
-
-      int num_decl = parsing_result.declarations.length();
-      bool accept_IN = num_decl >= 1;
-      bool accept_OF = true;
-      ForEachStatement::VisitMode mode;
-      int each_beg_pos = scanner()->location().beg_pos;
-      int each_end_pos = scanner()->location().end_pos;
-
-      if (accept_IN && CheckInOrOf(accept_OF, &mode, ok)) {
-        if (!*ok) return nullptr;
-        if (num_decl != 1) {
-          const char* loop_type =
-              mode == ForEachStatement::ITERATE ? "for-of" : "for-in";
-          ParserTraits::ReportMessageAt(
-              parsing_result.bindings_loc,
-              MessageTemplate::kForInOfLoopMultiBindings, loop_type);
-          *ok = false;
-          return nullptr;
-        }
-        if (parsing_result.first_initializer_loc.IsValid() &&
-            (is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
-          if (mode == ForEachStatement::ITERATE) {
-            ReportMessageAt(parsing_result.first_initializer_loc,
-                            MessageTemplate::kForOfLoopInitializer);
-          } else {
-            ReportMessageAt(parsing_result.first_initializer_loc,
-                            MessageTemplate::kForInLoopInitializer);
-          }
-          *ok = false;
-          return nullptr;
+        DCHECK(parsing_result.declarations.length() == 1);
+        Block* init_block = nullptr;
+
+        // special case for legacy for (var/const x =.... in)
+        if (is_sloppy(language_mode()) &&
+            !IsLexicalVariableMode(parsing_result.descriptor.mode) &&
+            parsing_result.declarations[0].initializer != nullptr) {
+          VariableProxy* single_var = scope_->NewUnresolved(
+              factory(), parsing_result.SingleName(), Variable::NORMAL,
+              each_beg_pos, each_end_pos);
+          init_block = factory()->NewBlock(
+              nullptr, 2, true, parsing_result.descriptor.declaration_pos);
+          init_block->AddStatement(
+              factory()->NewExpressionStatement(
+                  factory()->NewAssignment(
+                      Token::ASSIGN, single_var,
+                      parsing_result.declarations[0].initializer,
+                      RelocInfo::kNoPosition),
+                  RelocInfo::kNoPosition),
+              zone());
         }
-        // Rewrite a for-in statement of the form
+
+        // Rewrite a for-in/of statement of the form
         //
-        //   for (let/const x in e) b
+        //   for (let/const/var x in/of e) b
         //
         // into
         //
         //   <let x' be a temporary variable>
-        //   for (x' in e) {
-        //     let/const x;
+        //   for (x' in/of e) {
+        //     let/const/var x;
         //     x = x';
         //     b;
         //   }
 
-        // TODO(keuchel): Move the temporary variable to the block scope, after
-        // implementing stack allocated block scoped variables.
         Variable* temp = scope_->DeclarationScope()->NewTemporary(
             ast_value_factory()->dot_for_string());
-        VariableProxy* temp_proxy =
-            factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos);
         ForEachStatement* loop =
             factory()->NewForEachStatement(mode, labels, stmt_pos);
         Target target(&this->target_stack_, loop);
 
-        // The expression does not see the loop variable.
+        // The expression does not see the lexical loop variables.
         scope_ = saved_scope;
         Expression* enumerable = ParseExpression(true, CHECK_OK);
         scope_ = for_scope;
         Expect(Token::RPAREN, CHECK_OK);
 
         Statement* body = ParseSubStatement(NULL, CHECK_OK);
+
         Block* body_block =
             factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
 
-        auto each_initialization_block = factory()->NewBlock(
-            nullptr, 1, true, parsing_result.descriptor.pos);
+        auto each_initialization_block =
+            factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition);
         {
           DCHECK(parsing_result.declarations.length() == 1);
           DeclarationParsingResult::Declaration decl =
               parsing_result.declarations[0];
-          decl.initializer = temp_proxy;
+          auto descriptor = parsing_result.descriptor;
+          descriptor.declaration_pos = RelocInfo::kNoPosition;
+          decl.initializer = factory()->NewVariableProxy(temp);
+
           PatternRewriter::DeclareAndInitializeVariables(
-              each_initialization_block, &parsing_result.descriptor, &decl,
-              &lexical_bindings, CHECK_OK);
+              each_initialization_block, &descriptor, &decl,
+              IsLexicalVariableMode(descriptor.mode) ? &lexical_bindings
+                                                     : nullptr,
+              CHECK_OK);
         }
 
         body_block->AddStatement(each_initialization_block, zone());
         body_block->AddStatement(body, zone());
+        VariableProxy* temp_proxy =
+            factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos);
         InitializeForEachStatement(loop, temp_proxy, enumerable, body_block);
         scope_ = saved_scope;
         for_scope->set_end_position(scanner()->location().end_pos);
         for_scope = for_scope->FinalizeBlockScope();
-        body_block->set_scope(for_scope);
-        // Parsed for-in loop w/ let declaration.
-        return loop;
+        if (for_scope != nullptr) {
+          body_block->set_scope(for_scope);
+        }
+        // Parsed for-in loop w/ variable declarations.
+        if (init_block != nullptr) {
+          init_block->AddStatement(loop, zone());
+          return init_block;
+        } else {
+          return loop;
+        }
       } else {
-        init = parsing_result.BuildInitializationBlock(&lexical_bindings,
-                                                       CHECK_OK);
+        init = parsing_result.BuildInitializationBlock(
+            IsLexicalVariableMode(parsing_result.descriptor.mode)
+                ? &lexical_bindings
+                : nullptr,
+            CHECK_OK);
       }
     } else {
       Scanner::Location lhs_location = scanner()->peek_location();
@@ -3579,9 +3555,9 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
       ForEachStatement::VisitMode mode;
       bool accept_OF = expression->IsVariableProxy();
       is_let_identifier_expression =
-        expression->IsVariableProxy() &&
-        expression->AsVariableProxy()->raw_name() ==
-            ast_value_factory()->let_string();
+          expression->IsVariableProxy() &&
+          expression->AsVariableProxy()->raw_name() ==
+              ast_value_factory()->let_string();
 
       if (CheckInOrOf(accept_OF, &mode, ok)) {
         if (!*ok) return nullptr;
index 22b3a5d9f8d119824d70369f1a7320545e7d2fed..cda26df2803a1d6290bec2e279c3150ef84523bb 100644 (file)
@@ -950,7 +950,8 @@ class Parser : public ParserBase<ParserTraits> {
     VariableMode mode;
     bool is_const;
     bool needs_init;
-    int pos;
+    int declaration_pos;
+    int initialization_pos;
     Token::Value init_op;
   };
 
index 8767b1f8d7424bf5e31279320c014aecc609f269..e414205184119003e6be5f46fc31ff6ce94d3a2e 100644 (file)
@@ -51,7 +51,8 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
   const AstRawString* name = pattern->raw_name();
   VariableProxy* proxy = parser->NewUnresolved(name, descriptor_->mode);
   Declaration* declaration = factory()->NewVariableDeclaration(
-      proxy, descriptor_->mode, descriptor_->scope, descriptor_->pos);
+      proxy, descriptor_->mode, descriptor_->scope,
+      descriptor_->declaration_pos);
   Variable* var = parser->Declare(declaration, descriptor_->mode != VAR, ok_);
   if (!*ok_) return;
   DCHECK_NOT_NULL(var);
@@ -126,7 +127,9 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
     ZoneList<Expression*>* arguments =
         new (zone()) ZoneList<Expression*>(3, zone());
     // We have at least 1 parameter.
-    arguments->Add(factory()->NewStringLiteral(name, descriptor_->pos), zone());
+    arguments->Add(
+        factory()->NewStringLiteral(name, descriptor_->declaration_pos),
+        zone());
     CallRuntime* initialize;
 
     if (descriptor_->is_const) {
@@ -140,13 +143,14 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
       initialize = factory()->NewCallRuntime(
           ast_value_factory()->initialize_const_global_string(),
           Runtime::FunctionForId(Runtime::kInitializeConstGlobal), arguments,
-          descriptor_->pos);
+          descriptor_->initialization_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, descriptor_->pos), zone());
+      arguments->Add(factory()->NewNumberLiteral(language_mode,
+                                                 descriptor_->declaration_pos),
+                     zone());
 
       // Be careful not to assign a value to the global variable if
       // we're in a with. The initialization value should not
@@ -160,7 +164,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
         initialize = factory()->NewCallRuntime(
             ast_value_factory()->initialize_var_global_string(),
             Runtime::FunctionForId(Runtime::kInitializeVarGlobal), arguments,
-            descriptor_->pos);
+            descriptor_->declaration_pos);
       } else {
         initialize = NULL;
       }
@@ -184,7 +188,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
     DCHECK_NOT_NULL(proxy->var());
     DCHECK_NOT_NULL(value);
     Assignment* assignment = factory()->NewAssignment(
-        descriptor_->init_op, proxy, value, descriptor_->pos);
+        descriptor_->init_op, proxy, value, descriptor_->initialization_pos);
     block_->AddStatement(
         factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
         zone());
@@ -200,7 +204,7 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
     // property).
     VariableProxy* proxy = initialization_scope->NewUnresolved(factory(), name);
     Assignment* assignment = factory()->NewAssignment(
-        descriptor_->init_op, proxy, value, descriptor_->pos);
+        descriptor_->init_op, proxy, value, descriptor_->initialization_pos);
     block_->AddStatement(
         factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition),
         zone());
index 659680d8288989917b64a7a2cadd9510b7d2e1f4..c7b044d7192631637e14417226e01d4e872ac185 100644 (file)
@@ -6525,6 +6525,32 @@ TEST(DestructuringNegativeTests) {
 }
 
 
+TEST(DestructuringDisallowPatternsInForVarIn) {
+  i::FLAG_harmony_destructuring = true;
+  static const ParserFlag always_flags[] = {kAllowHarmonyDestructuring};
+  const char* context_data[][2] = {
+      {"", ""}, {"function f() {", "}"}, {NULL, NULL}};
+  // clang-format off
+  const char* error_data[] = {
+    "for (var {x} = {} in null);",
+    "for (var {x} = {} of null);",
+    "for (let x = {} in null);",
+    "for (let x = {} of null);",
+    NULL};
+  // clang-format on
+  RunParserSyncTest(context_data, error_data, kError, NULL, 0, always_flags,
+                    arraysize(always_flags));
+
+  // clang-format off
+  const char* success_data[] = {
+    "for (var x = {} in null);",
+    NULL};
+  // clang-format on
+  RunParserSyncTest(context_data, success_data, kSuccess, NULL, 0, always_flags,
+                    arraysize(always_flags));
+}
+
+
 TEST(SpreadArray) {
   i::FLAG_harmony_spread_arrays = true;
 
index 98af911ae3218ff6f6d024e269e8047ae88f9550..79cb3781a79a697b8f8305ceb8967b8d35008bfc 100644 (file)
@@ -81,28 +81,28 @@ Debug.setListener(listener);
 f();
 Debug.setListener(null);         // Break z
 
-print(JSON.stringify(log));
+print("log:\n"+ JSON.stringify(log));
 // The let declaration differs from var in that the loop variable
 // is declared in every iteration.
 var expected = [
   // Entry
   "a2","b2",
-  // Empty for-in-var: var decl, get enumerable
-  "c7","c16",
+  // Empty for-in-var: get enumerable
+  "c16",
   // Empty for-in: get enumerable
   "d12",
-  // For-in-var: var decl, get enumerable, assign, body, assign, body, ...
-  "e7","e16","e11","E4","e11","E4","e11","E4","e11",
+  // For-in-var: get enumerable, assign, body, assign, body, ...
+  "e16","e11","E4","e11","E4","e11","E4","e11",
   // For-in: get enumerable, assign, body, assign, body, ...
   "f12","f7","F4","f7","F4","f7","F4","f7",
-  // For-in-let: get enumerable, next, new let, body, next, new let, ...
-  "g16","g11","g7","G4","g11","g7","G4","g11","g7","G4","g11",
-  // For-of-var: var decl, next(), body, next(), body, ...
-  "h7","h16","H4","h16","H4","h16","H4","h16",
+  // For-in-let: get enumerable, next, body, next,  ...
+  "g16","g11","G4","g11","G4","g11","G4","g11",
+  // For-of-var: next(), body, next(), body, ...
+  "h16","H4","h16","H4","h16","H4","h16",
   // For-of: next(), body, next(), body, ...
   "i12","I4","i12","I4","i12","I4","i12",
-  // For-of-let: next(), new let, body, next(), new let, ...
-  "j16","j7","J4","j16","j7","J4","j16","j7","J4","j16",
+  // For-of-let: next(), body, next(), ...
+  "j16","J4","j16","J4","j16","J4","j16",
   // For-var: var decl, condition, body, next, condition, body, ...
   "k7","k20","K4","k23","k20","K4","k23","k20","K4","k23","k20",
   // For: init, condition, body, next, condition, body, ...
@@ -110,6 +110,7 @@ var expected = [
   // Exit.
   "y0","z0",
 ]
+print("expected:\n"+ JSON.stringify(log));
 
 assertArrayEquals(expected, log);
 assertEquals(48, s);
index adc3b1c13bdb68882c3a371d0b4f233e3a9c7715..731bc6dbe5c6c3a1b9fc10e80dcde7371c16bfb6 100644 (file)
     assertSame(-(i+1), fy());
   }
 
-  var o = { 'a1':1, 'b2':2 };
-  o.__proto__ = null;
+  var o = { __proto__:null, 'a1':1, 'b2':2 };
   let sx = '';
   let sy = '';
   for (let [x,y] in o) {
   assertEquals('ab', sx);
   assertEquals('12', sy);
 }());
+
+
+(function TestForEachVars() {
+  var a = [{x:1, y:-1}, {x:2,y:-2}, {x:3,y:-3}];
+  var sumX = 0;
+  var sumY = 0;
+  var fs = [];
+  for (var {x,y} of a) {
+    sumX += x;
+    sumY += y;
+    fs.push({fx : function() { return x; }, fy : function() { return y }});
+  }
+  assertSame(6, sumX);
+  assertSame(-6, sumY);
+  assertSame(3, fs.length);
+  for (var i = 0; i < fs.length; i++) {
+    var {fx,fy} = fs[i];
+    assertSame(3, fx());
+    assertSame(-3, fy());
+  }
+
+  var o = { __proto__:null, 'a1':1, 'b2':2 };
+  var sx = '';
+  var sy = '';
+  for (var [x,y] in o) {
+    sx += x;
+    sy += y;
+  }
+  assertEquals('ab', sx);
+  assertEquals('12', sy);
+}());