DONT_SELFOPTIMIZE_NODE(WhileStatement)
DONT_SELFOPTIMIZE_NODE(ForStatement)
DONT_SELFOPTIMIZE_NODE(ForInStatement)
+DONT_SELFOPTIMIZE_NODE(ForOfStatement)
DONT_CACHE_NODE(ModuleLiteral)
V(WhileStatement) \
V(ForStatement) \
V(ForInStatement) \
+ V(ForOfStatement) \
V(TryCatchStatement) \
V(TryFinallyStatement) \
V(DebuggerStatement)
};
-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) {
+ }
};
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)
"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)
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
}
+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.
}
+void FullCodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
+ // TODO(wingo): Implement.
+}
+
+
void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
Comment cmnt(masm_, "[ TryCatchStatement");
SetStatementPosition(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);
allow_natives_syntax_(false),
allow_lazy_(false),
allow_generators_(false),
+ allow_for_of_(false),
stack_overflow_(false),
parenthesized_function_(false),
zone_(info->zone()),
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);
}
}
default: {
- ExpectContextualKeyword("at", CHECK_OK);
+ ExpectContextualKeyword(CStrVector("at"), CHECK_OK);
Module* result = ParseModuleUrl(CHECK_OK);
ExpectSemicolon(CHECK_OK);
return result;
names.Add(name, zone());
}
- ExpectContextualKeyword("from", CHECK_OK);
+ ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
Module* module = ParseModuleSpecifier(CHECK_OK);
ExpectSemicolon(CHECK_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
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);
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
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;
}
} 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
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();
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;
}
}
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(),
}
+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.
}
-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());
}
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();
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; }
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
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()) {
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.
}
+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
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);
}
} 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);
allow_lazy_(false),
allow_natives_syntax_(false),
allow_generators_(false),
+ allow_for_of_(false),
parenthesized_function_(false) { }
~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; }
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
}
void ExpectSemicolon(bool* ok);
+ bool CheckInOrOf();
+
static int Precedence(i::Token::Value tok, bool accept_IN);
void SetStrictModeViolation(i::Scanner::Location,
bool allow_lazy_;
bool allow_natives_syntax_;
bool allow_generators_;
+ bool allow_for_of_;
bool parenthesized_function_;
};
} } // v8::preparser
}
+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());
}
+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());
}
+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_;
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);
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();
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();
}
+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()));
kAllowHarmonyScoping,
kAllowModules,
kAllowGenerators,
+ kAllowForOf,
kParserFlagCount
};
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();
--- /dev/null
+// 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) { } }