Disallow destructuring in legacy sloppy for-in loop parsing

For web compat reasons, we support an initializer in the declaration
part of a for-in loop. But we should disallow this for destructured
declarations (just as we do for lexical declarations). In fact, without
disallowing it, we crash.

Also fix up the PreParser to have the same restrictions here as the parser
(the lexical check was missing there), verified by running the message tests
with --min-preparse-length=0.

In fixing the logic I've also cleaned up the code a bit, removing the
only-called-once DeclarationParsingResult::SingleName method.

BUG=v8:811
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#32236}
This commit is contained in:
adamk 2015-11-24 17:14:58 -08:00 committed by Commit bot
parent 9278b7b05a
commit ceb92ebfdf
6 changed files with 43 additions and 35 deletions

View File

@ -2381,16 +2381,6 @@ Block* Parser::ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok) {
}
const AstRawString* Parser::DeclarationParsingResult::SingleName() const {
if (declarations.length() != 1) return nullptr;
const Declaration& declaration = declarations.at(0);
if (declaration.pattern->IsVariableProxy()) {
return declaration.pattern->AsVariableProxy()->raw_name();
}
return nullptr;
}
Block* Parser::DeclarationParsingResult::BuildInitializationBlock(
ZoneList<const AstRawString*>* names, bool* ok) {
Block* result = descriptor.parser->factory()->NewBlock(
@ -3678,9 +3668,12 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
*ok = false;
return nullptr;
}
DeclarationParsingResult::Declaration& decl =
parsing_result.declarations[0];
if (parsing_result.first_initializer_loc.IsValid() &&
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE ||
IsLexicalVariableMode(parsing_result.descriptor.mode))) {
IsLexicalVariableMode(parsing_result.descriptor.mode) ||
!decl.pattern->IsVariableProxy())) {
if (mode == ForEachStatement::ITERATE) {
ReportMessageAt(parsing_result.first_initializer_loc,
MessageTemplate::kForOfLoopInitializer);
@ -3693,23 +3686,22 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
return nullptr;
}
DCHECK(parsing_result.declarations.length() == 1);
Block* init_block = nullptr;
// special case for legacy for (var/const x =.... in)
if (!IsLexicalVariableMode(parsing_result.descriptor.mode) &&
parsing_result.declarations[0].initializer != nullptr) {
decl.pattern->IsVariableProxy() && decl.initializer != nullptr) {
const AstRawString* name =
decl.pattern->AsVariableProxy()->raw_name();
VariableProxy* single_var = scope_->NewUnresolved(
factory(), parsing_result.SingleName(), Variable::NORMAL,
each_beg_pos, each_end_pos);
factory(), name, Variable::NORMAL, each_beg_pos, each_end_pos);
init_block = factory()->NewBlock(
nullptr, 2, true, parsing_result.descriptor.declaration_pos);
init_block->statements()->Add(
factory()->NewExpressionStatement(
factory()->NewAssignment(
Token::ASSIGN, single_var,
parsing_result.declarations[0].initializer,
RelocInfo::kNoPosition),
factory()->NewAssignment(Token::ASSIGN, single_var,
decl.initializer,
RelocInfo::kNoPosition),
RelocInfo::kNoPosition),
zone());
}
@ -3752,9 +3744,6 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
auto each_initialization_block =
factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition);
{
DCHECK(parsing_result.declarations.length() == 1);
DeclarationParsingResult::Declaration decl =
parsing_result.declarations[0];
auto descriptor = parsing_result.descriptor;
descriptor.declaration_pos = RelocInfo::kNoPosition;
descriptor.initialization_pos = RelocInfo::kNoPosition;

View File

@ -1020,7 +1020,6 @@ class Parser : public ParserBase<ParserTraits> {
Block* BuildInitializationBlock(ZoneList<const AstRawString*>* names,
bool* ok);
const AstRawString* SingleName() const;
DeclarationDescriptor descriptor;
List<Declaration> declarations;

View File

@ -495,8 +495,8 @@ PreParser::Statement PreParser::ParseVariableStatement(
// VariableStatement ::
// VariableDeclarations ';'
Statement result = ParseVariableDeclarations(var_context, nullptr, nullptr,
nullptr, CHECK_OK);
Statement result = ParseVariableDeclarations(
var_context, nullptr, nullptr, nullptr, nullptr, nullptr, CHECK_OK);
ExpectSemicolon(CHECK_OK);
return result;
}
@ -508,9 +508,9 @@ PreParser::Statement PreParser::ParseVariableStatement(
// to initialize it properly. This mechanism is also used for the parsing
// of 'for-in' loops.
PreParser::Statement PreParser::ParseVariableDeclarations(
VariableDeclarationContext var_context, int* num_decl,
Scanner::Location* first_initializer_loc, Scanner::Location* bindings_loc,
bool* ok) {
VariableDeclarationContext var_context, int* num_decl, bool* is_lexical,
bool* is_binding_pattern, Scanner::Location* first_initializer_loc,
Scanner::Location* bindings_loc, bool* ok) {
// VariableDeclarations ::
// ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
//
@ -526,6 +526,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
// BindingPattern '=' AssignmentExpression
bool require_initializer = false;
bool lexical = false;
bool is_pattern = false;
if (peek() == Token::VAR) {
if (is_strong(language_mode())) {
Scanner::Location location = scanner()->peek_location();
@ -589,7 +590,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
}
}
bool is_pattern = pattern.IsObjectLiteral() || pattern.IsArrayLiteral();
is_pattern = pattern.IsObjectLiteral() || pattern.IsArrayLiteral();
bool is_for_iteration_variable =
var_context == kForStatement &&
@ -623,7 +624,9 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
Scanner::Location(bindings_start, scanner()->location().end_pos);
}
if (num_decl != NULL) *num_decl = nvars;
if (num_decl != nullptr) *num_decl = nvars;
if (is_lexical != nullptr) *is_lexical = lexical;
if (is_binding_pattern != nullptr) *is_binding_pattern = is_pattern;
return Statement::Default();
}
@ -912,11 +915,13 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
if (peek() == Token::VAR || (peek() == Token::CONST && allow_const()) ||
(peek() == Token::LET && IsNextLetKeyword())) {
int decl_count;
bool is_lexical;
bool is_binding_pattern;
Scanner::Location first_initializer_loc = Scanner::Location::invalid();
Scanner::Location bindings_loc = Scanner::Location::invalid();
ParseVariableDeclarations(kForStatement, &decl_count,
&first_initializer_loc, &bindings_loc,
CHECK_OK);
ParseVariableDeclarations(kForStatement, &decl_count, &is_lexical,
&is_binding_pattern, &first_initializer_loc,
&bindings_loc, CHECK_OK);
bool accept_IN = decl_count >= 1;
if (accept_IN && CheckInOrOf(&mode, ok)) {
if (!*ok) return Statement::Default();
@ -930,7 +935,8 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
return Statement::Default();
}
if (first_initializer_loc.IsValid() &&
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE ||
is_lexical || is_binding_pattern)) {
if (mode == ForEachStatement::ITERATE) {
ReportMessageAt(first_initializer_loc,
MessageTemplate::kForOfLoopInitializer);

View File

@ -1845,7 +1845,8 @@ class PreParser : public ParserBase<PreParserTraits> {
Statement ParseVariableStatement(VariableDeclarationContext var_context,
bool* ok);
Statement ParseVariableDeclarations(VariableDeclarationContext var_context,
int* num_decl,
int* num_decl, bool* is_lexical,
bool* is_binding_pattern,
Scanner::Location* first_initializer_loc,
Scanner::Location* bindings_loc,
bool* ok);

View File

@ -0,0 +1,9 @@
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Flags: --harmony-destructuring-bind
function f() {
for (var [x, y] = {} in {});
}

View File

@ -0,0 +1,4 @@
*%(basename)s:8: SyntaxError: for-in loop variable declaration may not have an initializer.
for (var [x, y] = {} in {});
^^^^^^
SyntaxError: for-in loop variable declaration may not have an initializer.