Commit 0c879d1c authored by Kevin Gibbons's avatar Kevin Gibbons Committed by Commit Bot

[parser] forbid for-of loop LHS expressions starting with 'let'

Bug: v8:9160
Change-Id: If3f624c1ccf1ed397daa3e30b3a7ec2a73b7c9b7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1578279Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Kevin Gibbons <bakkot@gmail.com>
Cr-Commit-Position: refs/heads/master@{#60997}
parent 68bd67b0
......@@ -362,6 +362,7 @@ namespace internal {
"Duplicate __proto__ fields are not allowed in object literals") \
T(ForInOfLoopInitializer, \
"% loop variable declaration may not have an initializer.") \
T(ForOfLet, "The left-hand side of a for-of loop may not start with 'let'.") \
T(ForInOfLoopMultiBindings, \
"Invalid left-hand side in % loop: Must have a single binding.") \
T(GeneratorInSingleStatementContext, \
......
......@@ -5443,7 +5443,8 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
Consume(Token::FOR);
Expect(Token::LPAREN);
if (peek() == Token::CONST || (peek() == Token::LET && IsNextLetKeyword())) {
bool starts_with_let = peek() == Token::LET;
if (peek() == Token::CONST || (starts_with_let && IsNextLetKeyword())) {
// The initializer contains lexical declarations,
// so create an in-between scope.
BlockState for_state(zone(), &scope_);
......@@ -5508,10 +5509,12 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
init = impl()->BuildInitializationBlock(&for_info.parsing_result);
} else if (peek() != Token::SEMICOLON) {
// The initializer does not contain declarations.
int lhs_beg_pos = peek_position();
Scanner::Location next_loc = scanner()->peek_location();
int lhs_beg_pos = next_loc.beg_pos;
int lhs_end_pos;
bool is_for_each;
ExpressionT expression;
{
ExpressionParsingScope parsing_scope(impl());
AcceptINScope scope(this, false);
......@@ -5520,6 +5523,10 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForStatement(
lhs_end_pos = end_position();
is_for_each = CheckInOrOf(&for_info.mode);
if (is_for_each) {
if (starts_with_let && for_info.mode == ForEachStatement::ITERATE) {
impl()->ReportMessageAt(next_loc, MessageTemplate::kForOfLet);
return impl()->NullStatement();
}
if (expression->IsPattern()) {
parsing_scope.ValidatePattern(expression, lhs_beg_pos, lhs_end_pos);
} else {
......@@ -5791,8 +5798,9 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
bool has_declarations = false;
Scope* inner_block_scope = NewScope(BLOCK_SCOPE);
bool starts_with_let = peek() == Token::LET;
if (peek() == Token::VAR || peek() == Token::CONST ||
(peek() == Token::LET && IsNextLetKeyword())) {
(starts_with_let && IsNextLetKeyword())) {
// The initializer contains declarations
// 'for' 'await' '(' ForDeclaration 'of' AssignmentExpression ')'
// Statement
......@@ -5826,6 +5834,11 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseForAwaitStatement(
// The initializer does not contain declarations.
// 'for' 'await' '(' LeftHandSideExpression 'of' AssignmentExpression ')'
// Statement
if (starts_with_let) {
impl()->ReportMessageAt(scanner()->peek_location(),
MessageTemplate::kForOfLet);
return impl()->NullStatement();
}
int lhs_beg_pos = peek_position();
BlockState inner_state(&scope_, inner_block_scope);
ExpressionParsingScope parsing_scope(impl());
......
......@@ -6583,6 +6583,34 @@ TEST(ForOfMultipleDeclarationsError) {
RunParserSyncTest(context_data, data, kError);
}
TEST(ForInOfLetExpression) {
const char* sloppy_context_data[][2] = {
{"", ""}, {"function foo(){", "}"}, {nullptr, nullptr}};
const char* strict_context_data[][2] = {
{"'use strict';", ""},
{"function foo(){ 'use strict';", "}"},
{nullptr, nullptr}};
const char* async_context_data[][2] = {
{"async function foo(){", "}"},
{"async function foo(){ 'use strict';", "}"},
{nullptr, nullptr}};
const char* for_let_in[] = {"for (let.x in {}) {}", nullptr};
const char* for_let_of[] = {"for (let.x of []) {}", nullptr};
const char* for_await_let_of[] = {"for await (let.x of []) {}", nullptr};
// The only place `let.x` is legal as a left-hand side expression
// is in sloppy mode in a for-in loop.
RunParserSyncTest(sloppy_context_data, for_let_in, kSuccess);
RunParserSyncTest(strict_context_data, for_let_in, kError);
RunParserSyncTest(sloppy_context_data, for_let_of, kError);
RunParserSyncTest(strict_context_data, for_let_of, kError);
RunParserSyncTest(async_context_data, for_await_let_of, kError);
}
TEST(ForInNoDeclarationsError) {
const char* context_data[][2] = {{"", ""},
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment