[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:
parent
f089e5c846
commit
1fb76f055a
@ -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."]
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
11
test/message/for-in-let-loop-initializers-strict.js
Normal file
11
test/message/for-in-let-loop-initializers-strict.js
Normal 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;
|
||||
}
|
||||
}
|
7
test/message/for-in-let-loop-initializers-strict.out
Normal file
7
test/message/for-in-let-loop-initializers-strict.out
Normal 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.
|
11
test/message/for-in-loop-initializers-strict.js
Normal file
11
test/message/for-in-loop-initializers-strict.js
Normal 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;
|
||||
}
|
||||
}
|
7
test/message/for-in-loop-initializers-strict.out
Normal file
7
test/message/for-in-loop-initializers-strict.out
Normal 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.
|
11
test/message/for-of-let-loop-initializers.js
Normal file
11
test/message/for-of-let-loop-initializers.js
Normal 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;
|
||||
}
|
||||
}
|
7
test/message/for-of-let-loop-initializers.out
Normal file
7
test/message/for-of-let-loop-initializers.out
Normal 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.
|
10
test/message/for-of-loop-initializers-sloppy.js
Normal file
10
test/message/for-of-loop-initializers-sloppy.js
Normal 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;
|
||||
}
|
||||
}
|
7
test/message/for-of-loop-initializers-sloppy.out
Normal file
7
test/message/for-of-loop-initializers-sloppy.out
Normal 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.
|
11
test/message/for-of-loop-initializers-strict.js
Normal file
11
test/message/for-of-loop-initializers-strict.js
Normal 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;
|
||||
}
|
||||
}
|
7
test/message/for-of-loop-initializers-strict.out
Normal file
7
test/message/for-of-loop-initializers-strict.out
Normal 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.
|
Loading…
Reference in New Issue
Block a user