From cb0d146862911b1d56695fe276c2b1f629d955a3 Mon Sep 17 00:00:00 2001 From: "wingo@igalia.com" Date: Thu, 6 Jun 2013 14:38:26 +0000 Subject: [PATCH] Add initial parser support for harmony iteration 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 --- src/ast.cc | 1 + src/ast.h | 82 +++++++++++++++++++++++++------- src/flag-definitions.h | 2 + src/full-codegen.cc | 11 +++++ src/hydrogen.cc | 8 ++++ src/parser.cc | 62 +++++++++++++++++------- src/parser.h | 8 +++- src/preparser.cc | 17 +++++-- src/preparser.h | 6 +++ src/prettyprinter.cc | 19 ++++++++ src/rewriter.cc | 5 ++ src/scanner.h | 13 +++++ src/typing.cc | 7 +++ test/cctest/test-parsing.cc | 4 +- test/mjsunit/harmony/iteration-syntax.js | 65 +++++++++++++++++++++++++ 15 files changed, 269 insertions(+), 41 deletions(-) create mode 100644 test/mjsunit/harmony/iteration-syntax.js diff --git a/src/ast.cc b/src/ast.cc index b4c0430..7edf747 100644 --- a/src/ast.cc +++ b/src/ast.cc @@ -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) diff --git a/src/ast.h b/src/ast.h index 2ffa473..e1945b0 100644 --- 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) diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 5a2ff8a..49dac4a 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -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 diff --git a/src/full-codegen.cc b/src/full-codegen.cc index ad2a994..c32309a 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -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); diff --git a/src/hydrogen.cc b/src/hydrogen.cc index d1cfb8e..60a86fb 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -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); diff --git a/src/parser.cc b/src/parser.cc index 283160e..27f7a82 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -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 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 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 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 keyword, bool* ok) { Expect(Token::IDENTIFIER, ok); if (!*ok) return; - Handle 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(); diff --git a/src/parser.h b/src/parser.h index eea617f..ae600d6 100644 --- a/src/parser.h +++ b/src/parser.h @@ -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 keyword); + void ExpectContextualKeyword(Vector keyword, bool* ok); Handle 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. diff --git a/src/preparser.cc b/src/preparser.cc index 3bf88ca..243a3ed 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -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); diff --git a/src/preparser.h b/src/preparser.h index e3a036f..786316e 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -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 diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc index 3a1eca7..23cad95 100644 --- a/src/prettyprinter.cc +++ b/src/prettyprinter.cc @@ -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()); diff --git a/src/rewriter.cc b/src/rewriter.cc index 44fe050..df5c353 100644 --- a/src/rewriter.cc +++ b/src/rewriter.cc @@ -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_; diff --git a/src/scanner.h b/src/scanner.h index 92418f7..368ec1b 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -178,6 +178,11 @@ class LiteralBuffer { bool is_ascii() { return is_ascii_; } + bool is_contextual_keyword(Vector keyword) { + return is_ascii() && keyword.length() == position_ && + (memcmp(keyword.start(), backing_store_.start(), position_) == 0); + } + Vector 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 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 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(); diff --git a/src/typing.cc b/src/typing.cc index 3e4144e..4ba6721 100644 --- a/src/typing.cc +++ b/src/typing.cc @@ -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())); diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 6367c74..62a5bcc 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -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 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 index 0000000..21149c0 --- /dev/null +++ b/test/mjsunit/harmony/iteration-syntax.js @@ -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) { } } -- 2.7.4