[parser] Disallow Expression in for..of statements

Although the `for..in` statement allows Expressions to define the
iterator, only an AssignmentExpression may occupy this position in the
`for..of` statement.

BUG=v8:4692
LOG=N
R=adamk@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#33420}
This commit is contained in:
mike 2016-01-20 14:04:58 -08:00 committed by Commit bot
parent e7fdf5eaed
commit f7263b6a3f
3 changed files with 126 additions and 4 deletions

View File

@ -3676,7 +3676,15 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
factory()->NewForEachStatement(mode, labels, stmt_pos);
Target target(&this->target_stack_, loop);
Expression* enumerable = ParseExpression(true, CHECK_OK);
Expression* enumerable;
if (mode == ForEachStatement::ITERATE) {
ExpressionClassifier classifier;
enumerable = ParseAssignmentExpression(true, &classifier, CHECK_OK);
enumerable = ParserTraits::RewriteNonPattern(enumerable, &classifier,
CHECK_OK);
} else {
enumerable = ParseExpression(true, CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
@ -3791,7 +3799,16 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
factory()->NewForEachStatement(mode, labels, stmt_pos);
Target target(&this->target_stack_, loop);
Expression* enumerable = ParseExpression(true, CHECK_OK);
Expression* enumerable;
if (mode == ForEachStatement::ITERATE) {
ExpressionClassifier classifier;
enumerable = ParseAssignmentExpression(true, &classifier, CHECK_OK);
enumerable = ParserTraits::RewriteNonPattern(enumerable, &classifier,
CHECK_OK);
} else {
enumerable = ParseExpression(true, CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
// Make a block around the statement in case a lexical binding

View File

@ -950,7 +950,16 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
*ok = false;
return Statement::Default();
}
ParseExpression(true, CHECK_OK);
if (mode == ForEachStatement::ITERATE) {
ExpressionClassifier classifier;
Expression enumerable =
ParseAssignmentExpression(true, &classifier, CHECK_OK);
PreParserTraits::RewriteNonPattern(enumerable, &classifier, CHECK_OK);
} else {
ParseExpression(true, CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK);
return Statement::Default();
@ -980,7 +989,16 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
lhs, lhs_beg_pos, lhs_end_pos, MessageTemplate::kInvalidLhsInFor,
kSyntaxError, CHECK_OK);
}
ParseExpression(true, CHECK_OK);
if (mode == ForEachStatement::ITERATE) {
ExpressionClassifier classifier;
Expression enumerable =
ParseAssignmentExpression(true, &classifier, CHECK_OK);
PreParserTraits::RewriteNonPattern(enumerable, &classifier, CHECK_OK);
} else {
ParseExpression(true, CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK);
return Statement::Default();

View File

@ -4925,6 +4925,23 @@ TEST(ConstParsingInForIn) {
}
TEST(StatementParsingInForIn) {
const char* context_data[][2] = {{"", ""},
{"'use strict';", ""},
{"function foo(){ 'use strict';", "}"},
{NULL, NULL}};
const char* data[] = {"for(x in {}, {}) {}", "for(var x in {}, {}) {}",
"for(let x in {}, {}) {}", "for(const x in {}, {}) {}",
NULL};
static const ParserFlag always_flags[] = {
kAllowHarmonySloppy, kAllowHarmonySloppyLet, kNoLegacyConst};
RunParserSyncTest(context_data, data, kSuccess, nullptr, 0, always_flags,
arraysize(always_flags));
}
TEST(ConstParsingInForInError) {
const char* context_data[][2] = {{"'use strict';", ""},
{"function foo(){ 'use strict';", "}"},
@ -5098,6 +5115,76 @@ TEST(ForOfNoDeclarationsError) {
}
TEST(ForOfInOperator) {
const char* context_data[][2] = {{"", ""},
{"'use strict';", ""},
{"function foo(){ 'use strict';", "}"},
{NULL, NULL}};
const char* data[] = {
"for(x of 'foo' in {}) {}", "for(var x of 'foo' in {}) {}",
"for(let x of 'foo' in {}) {}", "for(const x of 'foo' in {}) {}", NULL};
static const ParserFlag always_flags[] = {
kAllowHarmonySloppy, kAllowHarmonySloppyLet, kNoLegacyConst};
RunParserSyncTest(context_data, data, kSuccess, nullptr, 0, always_flags,
arraysize(always_flags));
}
TEST(ForOfYieldIdentifier) {
const char* context_data[][2] = {{"", ""}, {NULL, NULL}};
const char* data[] = {"for(x of yield) {}", "for(var x of yield) {}",
"for(let x of yield) {}", "for(const x of yield) {}",
NULL};
static const ParserFlag always_flags[] = {
kAllowHarmonySloppy, kAllowHarmonySloppyLet, kNoLegacyConst};
RunParserSyncTest(context_data, data, kSuccess, nullptr, 0, always_flags,
arraysize(always_flags));
}
TEST(ForOfYieldExpression) {
const char* context_data[][2] = {{"", ""},
{"'use strict';", ""},
{"function foo(){ 'use strict';", "}"},
{NULL, NULL}};
const char* data[] = {"function* g() { for(x of yield) {} }",
"function* g() { for(var x of yield) {} }",
"function* g() { for(let x of yield) {} }",
"function* g() { for(const x of yield) {} }", NULL};
static const ParserFlag always_flags[] = {
kAllowHarmonySloppy, kAllowHarmonySloppyLet, kNoLegacyConst};
RunParserSyncTest(context_data, data, kSuccess, nullptr, 0, always_flags,
arraysize(always_flags));
}
TEST(ForOfExpressionError) {
const char* context_data[][2] = {{"", ""},
{"'use strict';", ""},
{"function foo(){ 'use strict';", "}"},
{NULL, NULL}};
const char* data[] = {
"for(x of [], []) {}", "for(var x of [], []) {}",
"for(let x of [], []) {}", "for(const x of [], []) {}",
// AssignmentExpression should be validated statically:
"for(x of { y = 23 }) {}", "for(var x of { y = 23 }) {}",
"for(let x of { y = 23 }) {}", "for(const x of { y = 23 }) {}", NULL};
static const ParserFlag always_flags[] = {
kAllowHarmonySloppy, kAllowHarmonySloppyLet, kNoLegacyConst};
RunParserSyncTest(context_data, data, kError, nullptr, 0, always_flags,
arraysize(always_flags));
}
TEST(InvalidUnicodeEscapes) {
const char* context_data[][2] = {{"", ""},
{"'use strict';", ""},