Add initial parser support for harmony iteration
authorwingo@igalia.com <wingo@igalia.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 6 Jun 2013 14:38:26 +0000 (14:38 +0000)
committerwingo@igalia.com <wingo@igalia.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 6 Jun 2013 14:38:26 +0000 (14:38 +0000)
This commit adds initial parser support for harmony iteration.
Specifically, it will parse:

  for (x of y) {}
  for (let x of y) {}
  for (var x of y) {}

The semantics are still unimplemented.

TEST=mjsunit/harmony/for-of-syntax
BUG=v8:2214
R=rossberg@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14984 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

15 files changed:
src/ast.cc
src/ast.h
src/flag-definitions.h
src/full-codegen.cc
src/hydrogen.cc
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
src/prettyprinter.cc
src/rewriter.cc
src/scanner.h
src/typing.cc
test/cctest/test-parsing.cc
test/mjsunit/harmony/iteration-syntax.js [new file with mode: 0644]

index b4c0430..7edf747 100644 (file)
@@ -1156,6 +1156,7 @@ DONT_SELFOPTIMIZE_NODE(DoWhileStatement)
 DONT_SELFOPTIMIZE_NODE(WhileStatement)
 DONT_SELFOPTIMIZE_NODE(ForStatement)
 DONT_SELFOPTIMIZE_NODE(ForInStatement)
+DONT_SELFOPTIMIZE_NODE(ForOfStatement)
 
 DONT_CACHE_NODE(ModuleLiteral)
 
index 2ffa473..e1945b0 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -90,6 +90,7 @@ namespace internal {
   V(WhileStatement)                             \
   V(ForStatement)                               \
   V(ForInStatement)                             \
+  V(ForOfStatement)                             \
   V(TryCatchStatement)                          \
   V(TryFinallyStatement)                        \
   V(DebuggerStatement)
@@ -874,47 +875,79 @@ class ForStatement: public IterationStatement {
 };
 
 
-class ForInStatement: public IterationStatement {
+class ForEachStatement: public IterationStatement {
  public:
-  DECLARE_NODE_TYPE(ForInStatement)
+  enum VisitMode {
+    ENUMERATE,   // for (each in subject) body;
+    ITERATE      // for (each of subject) body;
+  };
 
-  void Initialize(Expression* each, Expression* enumerable, Statement* body) {
+  void Initialize(Expression* each, Expression* subject, Statement* body) {
     IterationStatement::Initialize(body);
     each_ = each;
-    enumerable_ = enumerable;
-    for_in_type_ = SLOW_FOR_IN;
+    subject_ = subject;
   }
 
   Expression* each() const { return each_; }
-  Expression* enumerable() const { return enumerable_; }
+  Expression* subject() const { return subject_; }
 
   virtual BailoutId ContinueId() const { return EntryId(); }
   virtual BailoutId StackCheckId() const { return body_id_; }
   BailoutId BodyId() const { return body_id_; }
   BailoutId PrepareId() const { return prepare_id_; }
 
-  TypeFeedbackId ForInFeedbackId() const { return reuse(PrepareId()); }
-  void RecordTypeFeedback(TypeFeedbackOracle* oracle);
-  enum ForInType { FAST_FOR_IN, SLOW_FOR_IN };
-  ForInType for_in_type() const { return for_in_type_; }
-
  protected:
-  ForInStatement(Isolate* isolate, ZoneStringList* labels)
+  ForEachStatement(Isolate* isolate, ZoneStringList* labels)
       : IterationStatement(isolate, labels),
         each_(NULL),
-        enumerable_(NULL),
+        subject_(NULL),
         body_id_(GetNextId(isolate)),
         prepare_id_(GetNextId(isolate)) {
   }
 
  private:
   Expression* each_;
-  Expression* enumerable_;
+  Expression* subject_;
+  const BailoutId body_id_;
+  const BailoutId prepare_id_;
+};
+
+
+class ForInStatement: public ForEachStatement {
+ public:
+  DECLARE_NODE_TYPE(ForInStatement)
+
+  Expression* enumerable() const {
+    return subject();
+  }
+
+  TypeFeedbackId ForInFeedbackId() const { return reuse(PrepareId()); }
+  void RecordTypeFeedback(TypeFeedbackOracle* oracle);
+  enum ForInType { FAST_FOR_IN, SLOW_FOR_IN };
+  ForInType for_in_type() const { return for_in_type_; }
+
+ protected:
+  ForInStatement(Isolate* isolate, ZoneStringList* labels)
+      : ForEachStatement(isolate, labels),
+        for_in_type_(SLOW_FOR_IN) {
+  }
 
   ForInType for_in_type_;
+};
 
-  const BailoutId body_id_;
-  const BailoutId prepare_id_;
+
+class ForOfStatement: public ForEachStatement {
+ public:
+  DECLARE_NODE_TYPE(ForOfStatement)
+
+  Expression* iterable() const {
+    return subject();
+  }
+
+ protected:
+  ForOfStatement(Isolate* isolate, ZoneStringList* labels)
+      : ForEachStatement(isolate, labels) {
+  }
 };
 
 
@@ -2853,10 +2886,25 @@ class AstNodeFactory BASE_EMBEDDED {
   STATEMENT_WITH_LABELS(DoWhileStatement)
   STATEMENT_WITH_LABELS(WhileStatement)
   STATEMENT_WITH_LABELS(ForStatement)
-  STATEMENT_WITH_LABELS(ForInStatement)
   STATEMENT_WITH_LABELS(SwitchStatement)
 #undef STATEMENT_WITH_LABELS
 
+  ForEachStatement* NewForEachStatement(ForEachStatement::VisitMode visit_mode,
+                                        ZoneStringList* labels) {
+    switch (visit_mode) {
+      case ForEachStatement::ENUMERATE: {
+        ForInStatement* stmt = new(zone_) ForInStatement(isolate_, labels);
+        VISIT_AND_RETURN(ForInStatement, stmt);
+      }
+      case ForEachStatement::ITERATE: {
+        ForOfStatement* stmt = new(zone_) ForOfStatement(isolate_, labels);
+        VISIT_AND_RETURN(ForOfStatement, stmt);
+      }
+    }
+    UNREACHABLE();
+    return NULL;
+  }
+
   ModuleStatement* NewModuleStatement(VariableProxy* proxy, Block* body) {
     ModuleStatement* stmt = new(zone_) ModuleStatement(proxy, body);
     VISIT_AND_RETURN(ModuleStatement, stmt)
index 5a2ff8a..49dac4a 100644 (file)
@@ -170,6 +170,7 @@ DEFINE_bool(harmony_array_buffer, false,
             "enable harmony array buffer")
 DEFINE_implication(harmony_typed_arrays, harmony_array_buffer)
 DEFINE_bool(harmony_generators, false, "enable harmony generators")
+DEFINE_bool(harmony_iteration, false, "enable harmony iteration (for-of)")
 DEFINE_bool(harmony, false, "enable all harmony features (except typeof)")
 DEFINE_implication(harmony, harmony_scoping)
 DEFINE_implication(harmony, harmony_modules)
@@ -178,6 +179,7 @@ DEFINE_implication(harmony, harmony_proxies)
 DEFINE_implication(harmony, harmony_collections)
 DEFINE_implication(harmony, harmony_observation)
 DEFINE_implication(harmony, harmony_generators)
+DEFINE_implication(harmony, harmony_iteration)
 DEFINE_implication(harmony_modules, harmony_scoping)
 DEFINE_implication(harmony_observation, harmony_collections)
 // TODO[dslomov] add harmony => harmony_typed_arrays
index ad2a994..c32309a 100644 (file)
@@ -163,6 +163,12 @@ void BreakableStatementChecker::VisitForInStatement(ForInStatement* stmt) {
 }
 
 
+void BreakableStatementChecker::VisitForOfStatement(ForOfStatement* stmt) {
+  // Mark for in statements breakable if the iterable expression is.
+  Visit(stmt->iterable());
+}
+
+
 void BreakableStatementChecker::VisitTryCatchStatement(
     TryCatchStatement* stmt) {
   // Mark try catch as breakable to avoid adding a break slot in front of it.
@@ -1383,6 +1389,11 @@ void FullCodeGenerator::VisitForStatement(ForStatement* stmt) {
 }
 
 
+void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
+  // TODO(wingo): Implement.
+}
+
+
 void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
   Comment cmnt(masm_, "[ TryCatchStatement");
   SetStatementPosition(stmt);
index d1cfb8e..60a86fb 100644 (file)
@@ -5447,6 +5447,14 @@ void HOptimizedGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
 }
 
 
+void HOptimizedGraphBuilder::VisitForOfStatement(ForOfStatement* stmt) {
+  ASSERT(!HasStackOverflow());
+  ASSERT(current_block() != NULL);
+  ASSERT(current_block()->HasPredecessor());
+  return Bailout("ForOfStatement");
+}
+
+
 void HOptimizedGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
   ASSERT(!HasStackOverflow());
   ASSERT(current_block() != NULL);
index 283160e..27f7a82 100644 (file)
@@ -549,6 +549,7 @@ Parser::Parser(CompilationInfo* info)
       allow_natives_syntax_(false),
       allow_lazy_(false),
       allow_generators_(false),
+      allow_for_of_(false),
       stack_overflow_(false),
       parenthesized_function_(false),
       zone_(info->zone()),
@@ -560,6 +561,7 @@ Parser::Parser(CompilationInfo* info)
   set_allow_natives_syntax(FLAG_allow_natives_syntax || info->is_native());
   set_allow_lazy(false);  // Must be explicitly enabled.
   set_allow_generators(FLAG_harmony_generators);
+  set_allow_for_of(FLAG_harmony_iteration);
 }
 
 
@@ -1028,7 +1030,7 @@ Module* Parser::ParseModule(bool* ok) {
     }
 
     default: {
-      ExpectContextualKeyword("at", CHECK_OK);
+      ExpectContextualKeyword(CStrVector("at"), CHECK_OK);
       Module* result = ParseModuleUrl(CHECK_OK);
       ExpectSemicolon(CHECK_OK);
       return result;
@@ -1200,7 +1202,7 @@ Block* Parser::ParseImportDeclaration(bool* ok) {
     names.Add(name, zone());
   }
 
-  ExpectContextualKeyword("from", CHECK_OK);
+  ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
   Module* module = ParseModuleSpecifier(CHECK_OK);
   ExpectSemicolon(CHECK_OK);
 
@@ -2622,6 +2624,18 @@ WhileStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) {
 }
 
 
+bool Parser::CheckInOrOf(ForEachStatement::VisitMode* visit_mode) {
+  if (Check(Token::IN)) {
+    *visit_mode = ForEachStatement::ENUMERATE;
+    return true;
+  } else if (allow_for_of() && CheckContextualKeyword(CStrVector("of"))) {
+    *visit_mode = ForEachStatement::ITERATE;
+    return true;
+  }
+  return false;
+}
+
+
 Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
   // ForStatement ::
   //   'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
@@ -2642,14 +2656,14 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
       Handle<String> name;
       Block* variable_statement =
           ParseVariableDeclarations(kForStatement, NULL, NULL, &name, CHECK_OK);
+      ForEachStatement::VisitMode mode;
 
-      if (peek() == Token::IN && !name.is_null()) {
+      if (!name.is_null() && CheckInOrOf(&mode)) {
         Interface* interface =
             is_const ? Interface::NewConst() : Interface::NewValue();
-        ForInStatement* loop = factory()->NewForInStatement(labels);
+        ForEachStatement* loop = factory()->NewForEachStatement(mode, labels);
         Target target(&this->target_stack_, loop);
 
-        Expect(Token::IN, CHECK_OK);
         Expression* enumerable = ParseExpression(true, CHECK_OK);
         Expect(Token::RPAREN, CHECK_OK);
 
@@ -2676,7 +2690,9 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
          ParseVariableDeclarations(kForStatement, &decl_props, NULL, &name,
                                    CHECK_OK);
       bool accept_IN = !name.is_null() && decl_props != kHasInitializers;
-      if (peek() == Token::IN && accept_IN) {
+      ForEachStatement::VisitMode mode;
+
+      if (accept_IN && CheckInOrOf(&mode)) {
         // Rewrite a for-in statement of the form
         //
         //   for (let x in e) b
@@ -2698,11 +2714,10 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
         Handle<String> tempname = heap_factory->InternalizeString(tempstr);
         Variable* temp = top_scope_->DeclarationScope()->NewTemporary(tempname);
         VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
-        ForInStatement* loop = factory()->NewForInStatement(labels);
+        ForEachStatement* loop = factory()->NewForEachStatement(mode, labels);
         Target target(&this->target_stack_, loop);
 
         // The expression does not see the loop variable.
-        Expect(Token::IN, CHECK_OK);
         top_scope_ = saved_scope;
         Expression* enumerable = ParseExpression(true, CHECK_OK);
         top_scope_ = for_scope;
@@ -2732,7 +2747,9 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
       }
     } else {
       Expression* expression = ParseExpression(false, CHECK_OK);
-      if (peek() == Token::IN) {
+      ForEachStatement::VisitMode mode;
+
+      if (CheckInOrOf(&mode)) {
         // Signal a reference error if the expression is an invalid
         // left-hand side expression.  We could report this as a syntax
         // error here but for compatibility with JSC we choose to report
@@ -2742,15 +2759,14 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
               isolate()->factory()->invalid_lhs_in_for_in_string();
           expression = NewThrowReferenceError(message);
         }
-        ForInStatement* loop = factory()->NewForInStatement(labels);
+        ForEachStatement* loop = factory()->NewForEachStatement(mode, labels);
         Target target(&this->target_stack_, loop);
 
-        Expect(Token::IN, CHECK_OK);
         Expression* enumerable = ParseExpression(true, CHECK_OK);
         Expect(Token::RPAREN, CHECK_OK);
 
         Statement* body = ParseStatement(NULL, CHECK_OK);
-        if (loop) loop->Initialize(expression, enumerable, body);
+        loop->Initialize(expression, enumerable, body);
         top_scope_ = saved_scope;
         for_scope->set_end_position(scanner().location().end_pos);
         for_scope = for_scope->FinalizeBlockScope();
@@ -2804,10 +2820,10 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
     result->AddStatement(init, zone());
     result->AddStatement(loop, zone());
     result->set_scope(for_scope);
-    if (loop) loop->Initialize(NULL, cond, next, body);
+    loop->Initialize(NULL, cond, next, body);
     return result;
   } else {
-    if (loop) loop->Initialize(init, cond, next, body);
+    loop->Initialize(init, cond, next, body);
     return loop;
   }
 }
@@ -4511,6 +4527,7 @@ preparser::PreParser::PreParseResult Parser::LazyParseFunctionLiteral(
     reusable_preparser_->set_allow_natives_syntax(allow_natives_syntax());
     reusable_preparser_->set_allow_lazy(true);
     reusable_preparser_->set_allow_generators(allow_generators());
+    reusable_preparser_->set_allow_for_of(allow_for_of());
   }
   preparser::PreParser::PreParseResult result =
       reusable_preparser_->PreParseLazyFunction(top_scope_->language_mode(),
@@ -4608,6 +4625,16 @@ bool Parser::Check(Token::Value token) {
 }
 
 
+bool Parser::CheckContextualKeyword(Vector<const char> keyword) {
+  if (peek() == Token::IDENTIFIER &&
+      scanner().is_next_contextual_keyword(keyword)) {
+    Consume(Token::IDENTIFIER);
+    return true;
+  }
+  return false;
+}
+
+
 void Parser::ExpectSemicolon(bool* ok) {
   // Check for automatic semicolon insertion according to
   // the rules given in ECMA-262, section 7.9, page 21.
@@ -4625,12 +4652,10 @@ void Parser::ExpectSemicolon(bool* ok) {
 }
 
 
-void Parser::ExpectContextualKeyword(const char* keyword, bool* ok) {
+void Parser::ExpectContextualKeyword(Vector<const char> keyword, bool* ok) {
   Expect(Token::IDENTIFIER, ok);
   if (!*ok) return;
-  Handle<String> symbol = GetSymbol();
-  if (!*ok) return;
-  if (!symbol->IsUtf8EqualTo(CStrVector(keyword))) {
+  if (!scanner().is_literal_contextual_keyword(keyword)) {
     *ok = false;
     ReportUnexpectedToken(scanner().current_token());
   }
@@ -5768,6 +5793,7 @@ ScriptDataImpl* PreParserApi::PreParse(Utf16CharacterStream* source) {
   preparser::PreParser preparser(&scanner, &recorder, stack_limit);
   preparser.set_allow_lazy(true);
   preparser.set_allow_generators(FLAG_harmony_generators);
+  preparser.set_allow_for_of(FLAG_harmony_iteration);
   preparser.set_allow_harmony_scoping(FLAG_harmony_scoping);
   scanner.Initialize(source);
   preparser::PreParser::PreParseResult result = preparser.PreParseProgram();
index eea617f..ae600d6 100644 (file)
@@ -437,6 +437,7 @@ class Parser BASE_EMBEDDED {
   bool allow_modules() { return scanner().HarmonyModules(); }
   bool allow_harmony_scoping() { return scanner().HarmonyScoping(); }
   bool allow_generators() const { return allow_generators_; }
+  bool allow_for_of() const { return allow_for_of_; }
 
   void set_allow_natives_syntax(bool allow) { allow_natives_syntax_ = allow; }
   void set_allow_lazy(bool allow) { allow_lazy_ = allow; }
@@ -445,6 +446,7 @@ class Parser BASE_EMBEDDED {
     scanner().SetHarmonyScoping(allow);
   }
   void set_allow_generators(bool allow) { allow_generators_ = allow; }
+  void set_allow_for_of(bool allow) { allow_for_of_ = allow; }
 
   // Parses the source code represented by the compilation info and sets its
   // function literal.  Returns false (and deallocates any allocated AST
@@ -721,13 +723,16 @@ class Parser BASE_EMBEDDED {
 
   bool is_generator() const { return current_function_state_->is_generator(); }
 
+  bool CheckInOrOf(ForEachStatement::VisitMode* visit_mode);
+
   bool peek_any_identifier();
 
   INLINE(void Consume(Token::Value token));
   void Expect(Token::Value token, bool* ok);
   bool Check(Token::Value token);
   void ExpectSemicolon(bool* ok);
-  void ExpectContextualKeyword(const char* keyword, bool* ok);
+  bool CheckContextualKeyword(Vector<const char> keyword);
+  void ExpectContextualKeyword(Vector<const char> keyword, bool* ok);
 
   Handle<String> LiteralString(PretenureFlag tenured) {
     if (scanner().is_literal_ascii()) {
@@ -850,6 +855,7 @@ class Parser BASE_EMBEDDED {
   bool allow_natives_syntax_;
   bool allow_lazy_;
   bool allow_generators_;
+  bool allow_for_of_;
   bool stack_overflow_;
   // If true, the next (and immediately following) function literal is
   // preceded by a parenthesis.
index 3bf88ca..243a3ed 100644 (file)
@@ -659,6 +659,17 @@ PreParser::Statement PreParser::ParseWhileStatement(bool* ok) {
 }
 
 
+bool PreParser::CheckInOrOf() {
+  if (peek() == i::Token::IN ||
+      (allow_for_of() &&
+       scanner_->is_next_contextual_keyword(v8::internal::CStrVector("of")))) {
+    Next();
+    return true;
+  }
+  return false;
+}
+
+
 PreParser::Statement PreParser::ParseForStatement(bool* ok) {
   // ForStatement ::
   //   'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
@@ -675,8 +686,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
           kForStatement, &decl_props, &decl_count, CHECK_OK);
       bool accept_IN = decl_count == 1 &&
           !(is_let && decl_props == kHasInitializers);
-      if (peek() == i::Token::IN && accept_IN) {
-        Expect(i::Token::IN, CHECK_OK);
+      if (accept_IN && CheckInOrOf()) {
         ParseExpression(true, CHECK_OK);
         Expect(i::Token::RPAREN, CHECK_OK);
 
@@ -685,8 +695,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
       }
     } else {
       ParseExpression(false, CHECK_OK);
-      if (peek() == i::Token::IN) {
-        Expect(i::Token::IN, CHECK_OK);
+      if (CheckInOrOf()) {
         ParseExpression(true, CHECK_OK);
         Expect(i::Token::RPAREN, CHECK_OK);
 
index e3a036f..786316e 100644 (file)
@@ -130,6 +130,7 @@ class PreParser {
         allow_lazy_(false),
         allow_natives_syntax_(false),
         allow_generators_(false),
+        allow_for_of_(false),
         parenthesized_function_(false) { }
 
   ~PreParser() {}
@@ -139,6 +140,7 @@ class PreParser {
   bool allow_modules() const { return scanner_->HarmonyModules(); }
   bool allow_harmony_scoping() const { return scanner_->HarmonyScoping(); }
   bool allow_generators() const { return allow_generators_; }
+  bool allow_for_of() const { return allow_for_of_; }
 
   void set_allow_natives_syntax(bool allow) { allow_natives_syntax_ = allow; }
   void set_allow_lazy(bool allow) { allow_lazy_ = allow; }
@@ -147,6 +149,7 @@ class PreParser {
     scanner_->SetHarmonyScoping(allow);
   }
   void set_allow_generators(bool allow) { allow_generators_ = allow; }
+  void set_allow_for_of(bool allow) { allow_for_of_ = allow; }
 
   // Pre-parse the program from the character stream; returns true on
   // success (even if parsing failed, the pre-parse data successfully
@@ -655,6 +658,8 @@ class PreParser {
   }
   void ExpectSemicolon(bool* ok);
 
+  bool CheckInOrOf();
+
   static int Precedence(i::Token::Value tok, bool accept_IN);
 
   void SetStrictModeViolation(i::Scanner::Location,
@@ -678,6 +683,7 @@ class PreParser {
   bool allow_lazy_;
   bool allow_natives_syntax_;
   bool allow_generators_;
+  bool allow_for_of_;
   bool parenthesized_function_;
 };
 } }  // v8::preparser
index 3a1eca7..23cad95 100644 (file)
@@ -255,6 +255,17 @@ void PrettyPrinter::VisitForInStatement(ForInStatement* node) {
 }
 
 
+void PrettyPrinter::VisitForOfStatement(ForOfStatement* node) {
+  PrintLabels(node->labels());
+  Print("for (");
+  Visit(node->each());
+  Print(" of ");
+  Visit(node->iterable());
+  Print(") ");
+  Visit(node->body());
+}
+
+
 void PrettyPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
   Print("try ");
   Visit(node->try_block());
@@ -929,6 +940,14 @@ void AstPrinter::VisitForInStatement(ForInStatement* node) {
 }
 
 
+void AstPrinter::VisitForOfStatement(ForOfStatement* node) {
+  IndentedScope indent(this, "FOR OF");
+  PrintIndentedVisit("FOR", node->each());
+  PrintIndentedVisit("OF", node->iterable());
+  PrintIndentedVisit("BODY", node->body());
+}
+
+
 void AstPrinter::VisitTryCatchStatement(TryCatchStatement* node) {
   IndentedScope indent(this, "TRY CATCH");
   PrintIndentedVisit("TRY", node->try_block());
index 44fe050..df5c353 100644 (file)
@@ -168,6 +168,11 @@ void Processor::VisitForInStatement(ForInStatement* node) {
 }
 
 
+void Processor::VisitForOfStatement(ForOfStatement* node) {
+  VisitIterationStatement(node);
+}
+
+
 void Processor::VisitTryCatchStatement(TryCatchStatement* node) {
   // Rewrite both try and catch blocks (reversed order).
   bool set_after_catch = is_set_;
index 92418f7..368ec1b 100644 (file)
@@ -178,6 +178,11 @@ class LiteralBuffer {
 
   bool is_ascii() { return is_ascii_; }
 
+  bool is_contextual_keyword(Vector<const char> keyword) {
+    return is_ascii() && keyword.length() == position_ &&
+        (memcmp(keyword.start(), backing_store_.start(), position_) == 0);
+  }
+
   Vector<const uc16> utf16_literal() {
     ASSERT(!is_ascii_);
     ASSERT((position_ & 0x1) == 0);
@@ -325,6 +330,10 @@ class Scanner {
     ASSERT_NOT_NULL(current_.literal_chars);
     return current_.literal_chars->is_ascii();
   }
+  bool is_literal_contextual_keyword(Vector<const char> keyword) {
+    ASSERT_NOT_NULL(next_.literal_chars);
+    return current_.literal_chars->is_contextual_keyword(keyword);
+  }
   int literal_length() const {
     ASSERT_NOT_NULL(current_.literal_chars);
     return current_.literal_chars->length();
@@ -361,6 +370,10 @@ class Scanner {
     ASSERT_NOT_NULL(next_.literal_chars);
     return next_.literal_chars->is_ascii();
   }
+  bool is_next_contextual_keyword(Vector<const char> keyword) {
+    ASSERT_NOT_NULL(next_.literal_chars);
+    return next_.literal_chars->is_contextual_keyword(keyword);
+  }
   int next_literal_length() const {
     ASSERT_NOT_NULL(next_.literal_chars);
     return next_.literal_chars->length();
index 3e4144e..4ba6721 100644 (file)
@@ -224,6 +224,13 @@ void AstTyper::VisitForInStatement(ForInStatement* stmt) {
 }
 
 
+void AstTyper::VisitForOfStatement(ForOfStatement* stmt) {
+  ASSERT(!HasStackOverflow());
+  CHECK_ALIVE(Visit(stmt->iterable()));
+  CHECK_ALIVE(Visit(stmt->body()));
+}
+
+
 void AstTyper::VisitTryCatchStatement(TryCatchStatement* stmt) {
   ASSERT(!HasStackOverflow());
   CHECK_ALIVE(Visit(stmt->try_block()));
index 6367c74..62a5bcc 100644 (file)
@@ -1078,6 +1078,7 @@ enum ParserFlag {
   kAllowHarmonyScoping,
   kAllowModules,
   kAllowGenerators,
+  kAllowForOf,
   kParserFlagCount
 };
 
@@ -1094,7 +1095,8 @@ static bool checkParserFlag(unsigned flags, ParserFlag flag) {
   parser.set_allow_harmony_scoping(checkParserFlag(flags, \
                                                    kAllowHarmonyScoping)); \
   parser.set_allow_modules(checkParserFlag(flags, kAllowModules)); \
-  parser.set_allow_generators(checkParserFlag(flags, kAllowGenerators));
+  parser.set_allow_generators(checkParserFlag(flags, kAllowGenerators)); \
+  parser.set_allow_for_of(checkParserFlag(flags, kAllowForOf));
 
 void TestParserSyncWithFlags(i::Handle<i::String> source, unsigned flags) {
   i::Isolate* isolate = i::Isolate::Current();
diff --git a/test/mjsunit/harmony/iteration-syntax.js b/test/mjsunit/harmony/iteration-syntax.js
new file mode 100644 (file)
index 0000000..21149c0
--- /dev/null
@@ -0,0 +1,65 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --harmony-iteration --harmony-scoping
+
+// Test for-of syntax.
+
+"use strict";
+
+function f() { for (x of y) { } }
+function f() { for (var x of y) { } }
+function f() { for (let x of y) { } }
+
+assertThrows("function f() { for (x of) { } }", SyntaxError);
+assertThrows("function f() { for (x of y z) { } }", SyntaxError);
+assertThrows("function f() { for (x of y;) { } }", SyntaxError);
+
+assertThrows("function f() { for (var x of) { } }", SyntaxError);
+assertThrows("function f() { for (var x of y z) { } }", SyntaxError);
+assertThrows("function f() { for (var x of y;) { } }", SyntaxError);
+
+assertThrows("function f() { for (let x of) { } }", SyntaxError);
+assertThrows("function f() { for (let x of y z) { } }", SyntaxError);
+assertThrows("function f() { for (let x of y;) { } }", SyntaxError);
+
+assertThrows("function f() { for (of y) { } }", SyntaxError);
+assertThrows("function f() { for (of of) { } }", SyntaxError);
+assertThrows("function f() { for (var of y) { } }", SyntaxError);
+assertThrows("function f() { for (var of of) { } }", SyntaxError);
+assertThrows("function f() { for (let of y) { } }", SyntaxError);
+assertThrows("function f() { for (let of of) { } }", SyntaxError);
+
+// Alack, this appears to be valid.
+function f() { for (of of y) { } }
+function f() { for (let of of y) { } }
+function f() { for (var of of y) { } }
+
+// This too, of course.
+function f() { for (of in y) { } }
+function f() { for (var of in y) { } }
+function f() { for (let of in y) { } }