[es6] emit error when for-in loop declarations are initialized in strict mode

The ES6 grammar forbids the initialization of variable declarations in IterationStatements.

This CL will report `for (var x = y in z)` as a SyntaxError in strict mode (as done in JSC). It is possible that this could break sites in sloppy mode, and so that change can wait.

BUG=
R=
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#27639}
This commit is contained in:
caitpotter88 2015-04-07 12:28:33 -07:00 committed by Commit bot
parent f089e5c846
commit 1fb76f055a
15 changed files with 163 additions and 40 deletions

View File

@ -197,7 +197,9 @@ var kMessages = {
duplicate_proto: ["Duplicate __proto__ fields are not allowed in object literals"],
param_after_rest: ["Rest parameter must be last formal parameter"],
constructor_noncallable: ["Class constructors cannot be invoked without 'new'"],
array_not_subclassable: ["Subclassing Arrays is not currently supported."]
array_not_subclassable: ["Subclassing Arrays is not currently supported."],
for_in_loop_initializer: ["for-in loop variable declaration may not have an initializer."],
for_of_loop_initializer: ["for-of loop variable declaration may not have an initializer."]
};

View File

@ -2218,7 +2218,7 @@ Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
const AstRawString* ignore;
Block* result =
ParseVariableDeclarations(var_context, NULL, names, &ignore, CHECK_OK);
ParseVariableDeclarations(var_context, names, &ignore, nullptr, CHECK_OK);
ExpectSemicolon(CHECK_OK);
return result;
}
@ -2231,10 +2231,8 @@ Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
// of 'for-in' loops.
Block* Parser::ParseVariableDeclarations(
VariableDeclarationContext var_context,
VariableDeclarationProperties* decl_props,
ZoneList<const AstRawString*>* names,
const AstRawString** out,
bool* ok) {
ZoneList<const AstRawString*>* names, const AstRawString** out,
Scanner::Location* first_initializer_loc, bool* ok) {
// VariableDeclarations ::
// ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[',']
//
@ -2314,6 +2312,7 @@ Block* Parser::ParseVariableDeclarations(
// Parse variable name.
if (nvars > 0) Consume(Token::COMMA);
name = ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
Scanner::Location variable_loc = scanner()->location();
if (fni_ != NULL) fni_->PushVariableName(name);
// Declare variable.
@ -2388,6 +2387,12 @@ Block* Parser::ParseVariableDeclarations(
Expect(Token::ASSIGN, CHECK_OK);
pos = position();
value = ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
variable_loc.end_pos = scanner()->location().end_pos;
if (first_initializer_loc && !first_initializer_loc->IsValid()) {
*first_initializer_loc = variable_loc;
}
// Don't infer if it is "a = function(){...}();"-like expression.
if (fni_ != NULL &&
value->AsCall() == NULL &&
@ -2396,7 +2401,6 @@ Block* Parser::ParseVariableDeclarations(
} else {
fni_->RemoveLastFunction();
}
if (decl_props != NULL) *decl_props = kHasInitializers;
// End position of the initializer is after the assignment expression.
var->set_initializer_position(scanner()->location().end_pos);
} else {
@ -3363,17 +3367,27 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
if (peek() == Token::VAR ||
(peek() == Token::CONST && is_sloppy(language_mode()))) {
const AstRawString* name = NULL;
VariableDeclarationProperties decl_props = kHasNoInitializers;
Block* variable_statement =
ParseVariableDeclarations(kForStatement, &decl_props, NULL, &name,
CHECK_OK);
bool accept_OF = decl_props == kHasNoInitializers;
Scanner::Location first_initializer_loc = Scanner::Location::invalid();
Block* variable_statement = ParseVariableDeclarations(
kForStatement, nullptr, &name, &first_initializer_loc, CHECK_OK);
bool accept_OF = true;
ForEachStatement::VisitMode mode;
int each_beg_pos = scanner()->location().beg_pos;
int each_end_pos = scanner()->location().end_pos;
if (name != NULL && CheckInOrOf(accept_OF, &mode, ok)) {
if (!*ok) return nullptr;
if (first_initializer_loc.IsValid() &&
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
if (mode == ForEachStatement::ITERATE) {
ReportMessageAt(first_initializer_loc, "for_of_loop_initializer");
} else {
// TODO(caitp): This should be an error in sloppy mode too.
ReportMessageAt(first_initializer_loc, "for_in_loop_initializer");
}
*ok = false;
return nullptr;
}
ForEachStatement* loop =
factory()->NewForEachStatement(mode, labels, stmt_pos);
Target target(&this->target_stack_, loop);
@ -3402,19 +3416,28 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
is_strict(language_mode())) {
is_const = peek() == Token::CONST;
const AstRawString* name = NULL;
VariableDeclarationProperties decl_props = kHasNoInitializers;
Scanner::Location first_initializer_loc = Scanner::Location::invalid();
Block* variable_statement =
ParseVariableDeclarations(kForStatement, &decl_props,
&lexical_bindings, &name, CHECK_OK);
bool accept_IN = name != NULL && decl_props != kHasInitializers;
bool accept_OF = decl_props == kHasNoInitializers;
ParseVariableDeclarations(kForStatement, &lexical_bindings, &name,
&first_initializer_loc, CHECK_OK);
bool accept_IN = name != NULL;
bool accept_OF = true;
ForEachStatement::VisitMode mode;
int each_beg_pos = scanner()->location().beg_pos;
int each_end_pos = scanner()->location().end_pos;
if (accept_IN && CheckInOrOf(accept_OF, &mode, ok)) {
if (!*ok) return nullptr;
if (first_initializer_loc.IsValid() &&
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
if (mode == ForEachStatement::ITERATE) {
ReportMessageAt(first_initializer_loc, "for_of_loop_initializer");
} else {
ReportMessageAt(first_initializer_loc, "for_in_loop_initializer");
}
*ok = false;
return nullptr;
}
// Rewrite a for-in statement of the form
//
// for (let/const x in e) b

View File

@ -909,9 +909,9 @@ class Parser : public ParserBase<ParserTraits> {
ZoneList<const AstRawString*>* names,
bool* ok);
Block* ParseVariableDeclarations(VariableDeclarationContext var_context,
VariableDeclarationProperties* decl_props,
ZoneList<const AstRawString*>* names,
const AstRawString** out,
Scanner::Location* first_initializer_loc,
bool* ok);
Statement* ParseExpressionOrLabelledStatement(
ZoneList<const AstRawString*>* labels, bool* ok);

View File

@ -410,10 +410,8 @@ PreParser::Statement PreParser::ParseVariableStatement(
// VariableStatement ::
// VariableDeclarations ';'
Statement result = ParseVariableDeclarations(var_context,
NULL,
NULL,
CHECK_OK);
Statement result =
ParseVariableDeclarations(var_context, nullptr, nullptr, CHECK_OK);
ExpectSemicolon(CHECK_OK);
return result;
}
@ -425,10 +423,8 @@ 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,
VariableDeclarationProperties* decl_props,
int* num_decl,
bool* ok) {
VariableDeclarationContext var_context, int* num_decl,
Scanner::Location* first_initializer_loc, bool* ok) {
// VariableDeclarations ::
// ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
//
@ -486,13 +482,18 @@ PreParser::Statement PreParser::ParseVariableDeclarations(
// Parse variable name.
if (nvars > 0) Consume(Token::COMMA);
ParseIdentifier(kDontAllowEvalOrArguments, CHECK_OK);
Scanner::Location variable_loc = scanner()->location();
nvars++;
if (peek() == Token::ASSIGN || require_initializer ||
// require initializers for multiple consts.
(is_strict_const && peek() == Token::COMMA)) {
Expect(Token::ASSIGN, CHECK_OK);
ParseAssignmentExpression(var_context != kForStatement, CHECK_OK);
if (decl_props != NULL) *decl_props = kHasInitializers;
variable_loc.end_pos = scanner()->location().end_pos;
if (first_initializer_loc && !first_initializer_loc->IsValid()) {
*first_initializer_loc = variable_loc;
}
}
} while (peek() == Token::COMMA);
@ -728,20 +729,31 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
Expect(Token::LPAREN, CHECK_OK);
bool is_let_identifier_expression = false;
if (peek() != Token::SEMICOLON) {
ForEachStatement::VisitMode visit_mode;
ForEachStatement::VisitMode mode;
if (peek() == Token::VAR || peek() == Token::CONST ||
(peek() == Token::LET && is_strict(language_mode()))) {
bool is_lexical = peek() == Token::LET ||
(peek() == Token::CONST && is_strict(language_mode()));
int decl_count;
VariableDeclarationProperties decl_props = kHasNoInitializers;
ParseVariableDeclarations(
kForStatement, &decl_props, &decl_count, CHECK_OK);
bool has_initializers = decl_props == kHasInitializers;
Scanner::Location first_initializer_loc = Scanner::Location::invalid();
ParseVariableDeclarations(kForStatement, &decl_count,
&first_initializer_loc, CHECK_OK);
bool has_initializers = first_initializer_loc.IsValid();
bool accept_IN = decl_count == 1 && !(is_lexical && has_initializers);
bool accept_OF = !has_initializers;
if (accept_IN && CheckInOrOf(accept_OF, &visit_mode, ok)) {
bool accept_OF = true;
if (accept_IN && CheckInOrOf(accept_OF, &mode, ok)) {
if (!*ok) return Statement::Default();
if (first_initializer_loc.IsValid() &&
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE)) {
if (mode == ForEachStatement::ITERATE) {
ReportMessageAt(first_initializer_loc, "for_of_loop_initializer");
} else {
// TODO(caitp): This should be an error in sloppy mode, too.
ReportMessageAt(first_initializer_loc, "for_in_loop_initializer");
}
*ok = false;
return Statement::Default();
}
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
ParseSubStatement(CHECK_OK);
@ -751,7 +763,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
Expression lhs = ParseExpression(false, CHECK_OK);
is_let_identifier_expression =
lhs.IsIdentifier() && lhs.AsIdentifier().IsLet();
if (CheckInOrOf(lhs.IsIdentifier(), &visit_mode, ok)) {
if (CheckInOrOf(lhs.IsIdentifier(), &mode, ok)) {
if (!*ok) return Statement::Default();
ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);

View File

@ -162,9 +162,6 @@ class ParserBase : public Traits {
kForStatement
};
// If a list of variable declarations includes any initializers.
enum VariableDeclarationProperties { kHasInitializers, kHasNoInitializers };
class Checkpoint;
class ObjectLiteralCheckerBase;
@ -1588,8 +1585,8 @@ class PreParser : public ParserBase<PreParserTraits> {
Statement ParseVariableStatement(VariableDeclarationContext var_context,
bool* ok);
Statement ParseVariableDeclarations(VariableDeclarationContext var_context,
VariableDeclarationProperties* decl_props,
int* num_decl,
Scanner::Location* first_initializer_loc,
bool* ok);
Statement ParseExpressionOrLabelledStatement(bool* ok);
Statement ParseIfStatement(bool* ok);

View File

@ -0,0 +1,11 @@
// Copyright 2014 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.
//
'use strict';
function test() {
for (let x = void 0 in [1, 2, 3]) {
return x;
}
}

View File

@ -0,0 +1,7 @@
# Copyright 2014 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.
*%(basename)s:8: SyntaxError: for-in loop variable declaration may not have an initializer.
for (let x = void 0 in [1, 2, 3]) {
^^^^^^^^^^
SyntaxError: for-in loop variable declaration may not have an initializer.

View File

@ -0,0 +1,11 @@
// Copyright 2014 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.
//
'use strict';
function test() {
for (var x = void 0 in [1, 2, 3]) {
return x;
}
}

View File

@ -0,0 +1,7 @@
# Copyright 2014 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.
*%(basename)s:8: SyntaxError: for-in loop variable declaration may not have an initializer.
for (var x = void 0 in [1, 2, 3]) {
^^^^^^^^^^
SyntaxError: for-in loop variable declaration may not have an initializer.

View File

@ -0,0 +1,11 @@
// Copyright 2014 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.
//
'use strict';
function test() {
for (let x = void 0 of [1, 2, 3]) {
return x;
}
}

View File

@ -0,0 +1,7 @@
# Copyright 2014 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.
*%(basename)s:8: SyntaxError: for-of loop variable declaration may not have an initializer.
for (let x = void 0 of [1, 2, 3]) {
^^^^^^^^^^
SyntaxError: for-of loop variable declaration may not have an initializer.

View File

@ -0,0 +1,10 @@
// Copyright 2014 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.
//
function test() {
for (var x = void 0 of [1, 2, 3]) {
return x;
}
}

View File

@ -0,0 +1,7 @@
# Copyright 2014 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.
*%(basename)s:7: SyntaxError: for-of loop variable declaration may not have an initializer.
for (var x = void 0 of [1, 2, 3]) {
^^^^^^^^^^
SyntaxError: for-of loop variable declaration may not have an initializer.

View File

@ -0,0 +1,11 @@
// Copyright 2014 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.
//
'use strict';
function test() {
for (var x = void 0 of [1, 2, 3]) {
return x;
}
}

View File

@ -0,0 +1,7 @@
# Copyright 2014 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.
*%(basename)s:8: SyntaxError: for-of loop variable declaration may not have an initializer.
for (var x = void 0 of [1, 2, 3]) {
^^^^^^^^^^
SyntaxError: for-of loop variable declaration may not have an initializer.