From 1fb76f055a4a0d0a2f5e5d2784b0f070fc279b2b Mon Sep 17 00:00:00 2001 From: caitpotter88 Date: Tue, 7 Apr 2015 12:28:33 -0700 Subject: [PATCH] [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} --- src/messages.js | 4 +- src/parser.cc | 57 +++++++++++++------ src/parser.h | 2 +- src/preparser.cc | 46 +++++++++------ src/preparser.h | 5 +- .../for-in-let-loop-initializers-strict.js | 11 ++++ .../for-in-let-loop-initializers-strict.out | 7 +++ .../for-in-loop-initializers-strict.js | 11 ++++ .../for-in-loop-initializers-strict.out | 7 +++ test/message/for-of-let-loop-initializers.js | 11 ++++ test/message/for-of-let-loop-initializers.out | 7 +++ .../for-of-loop-initializers-sloppy.js | 10 ++++ .../for-of-loop-initializers-sloppy.out | 7 +++ .../for-of-loop-initializers-strict.js | 11 ++++ .../for-of-loop-initializers-strict.out | 7 +++ 15 files changed, 163 insertions(+), 40 deletions(-) create mode 100644 test/message/for-in-let-loop-initializers-strict.js create mode 100644 test/message/for-in-let-loop-initializers-strict.out create mode 100644 test/message/for-in-loop-initializers-strict.js create mode 100644 test/message/for-in-loop-initializers-strict.out create mode 100644 test/message/for-of-let-loop-initializers.js create mode 100644 test/message/for-of-let-loop-initializers.out create mode 100644 test/message/for-of-loop-initializers-sloppy.js create mode 100644 test/message/for-of-loop-initializers-sloppy.out create mode 100644 test/message/for-of-loop-initializers-strict.js create mode 100644 test/message/for-of-loop-initializers-strict.out diff --git a/src/messages.js b/src/messages.js index c1d38f22c..e7e0938d5 100644 --- a/src/messages.js +++ b/src/messages.js @@ -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."] }; diff --git a/src/parser.cc b/src/parser.cc index a23547ee3..608a6bdaf 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -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* names, - const AstRawString** out, - bool* ok) { + ZoneList* 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* 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* 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 diff --git a/src/parser.h b/src/parser.h index d93faaf0e..4929bbb91 100644 --- a/src/parser.h +++ b/src/parser.h @@ -909,9 +909,9 @@ class Parser : public ParserBase { ZoneList* names, bool* ok); Block* ParseVariableDeclarations(VariableDeclarationContext var_context, - VariableDeclarationProperties* decl_props, ZoneList* names, const AstRawString** out, + Scanner::Location* first_initializer_loc, bool* ok); Statement* ParseExpressionOrLabelledStatement( ZoneList* labels, bool* ok); diff --git a/src/preparser.cc b/src/preparser.cc index 5a6a094f6..832e81b97 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -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); diff --git a/src/preparser.h b/src/preparser.h index 92c762d36..35b65853b 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -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 { 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); diff --git a/test/message/for-in-let-loop-initializers-strict.js b/test/message/for-in-let-loop-initializers-strict.js new file mode 100644 index 000000000..a58f2fd27 --- /dev/null +++ b/test/message/for-in-let-loop-initializers-strict.js @@ -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; + } +} diff --git a/test/message/for-in-let-loop-initializers-strict.out b/test/message/for-in-let-loop-initializers-strict.out new file mode 100644 index 000000000..6c8ca9dfe --- /dev/null +++ b/test/message/for-in-let-loop-initializers-strict.out @@ -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. diff --git a/test/message/for-in-loop-initializers-strict.js b/test/message/for-in-loop-initializers-strict.js new file mode 100644 index 000000000..6aa092550 --- /dev/null +++ b/test/message/for-in-loop-initializers-strict.js @@ -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; + } +} diff --git a/test/message/for-in-loop-initializers-strict.out b/test/message/for-in-loop-initializers-strict.out new file mode 100644 index 000000000..41d7cbd28 --- /dev/null +++ b/test/message/for-in-loop-initializers-strict.out @@ -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. diff --git a/test/message/for-of-let-loop-initializers.js b/test/message/for-of-let-loop-initializers.js new file mode 100644 index 000000000..4ac0d549c --- /dev/null +++ b/test/message/for-of-let-loop-initializers.js @@ -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; + } +} diff --git a/test/message/for-of-let-loop-initializers.out b/test/message/for-of-let-loop-initializers.out new file mode 100644 index 000000000..3b43e9f64 --- /dev/null +++ b/test/message/for-of-let-loop-initializers.out @@ -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. diff --git a/test/message/for-of-loop-initializers-sloppy.js b/test/message/for-of-loop-initializers-sloppy.js new file mode 100644 index 000000000..685e2e6d2 --- /dev/null +++ b/test/message/for-of-loop-initializers-sloppy.js @@ -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; + } +} diff --git a/test/message/for-of-loop-initializers-sloppy.out b/test/message/for-of-loop-initializers-sloppy.out new file mode 100644 index 000000000..2961d0cec --- /dev/null +++ b/test/message/for-of-loop-initializers-sloppy.out @@ -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. diff --git a/test/message/for-of-loop-initializers-strict.js b/test/message/for-of-loop-initializers-strict.js new file mode 100644 index 000000000..5b3dddc3c --- /dev/null +++ b/test/message/for-of-loop-initializers-strict.js @@ -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; + } +} diff --git a/test/message/for-of-loop-initializers-strict.out b/test/message/for-of-loop-initializers-strict.out new file mode 100644 index 000000000..e29bd84df --- /dev/null +++ b/test/message/for-of-loop-initializers-strict.out @@ -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. -- 2.34.1