From c503241945ce25503bbf5c6e8fe506209bb7a0d9 Mon Sep 17 00:00:00 2001 From: Andreas Rossberg Date: Tue, 17 Feb 2015 17:25:49 +0100 Subject: [PATCH] [strong] deprecate empty sub-statements R=marja@chromium.org BUG= Review URL: https://codereview.chromium.org/931223002 Cr-Commit-Position: refs/heads/master@{#26701} --- src/messages.js | 3 ++- src/parser.cc | 37 +++++++++++++++++++++++++--------- src/parser.h | 1 + src/preparser.cc | 30 +++++++++++++++++++-------- src/preparser.h | 1 + test/cctest/test-parsing.cc | 30 +++++++++++++++++++++++++++ test/mjsunit/strong/delete.js | 2 +- test/mjsunit/strong/empty-statement.js | 19 +++++++++++++++++ test/mjsunit/strong/equality.js | 2 +- test/mjsunit/strong/use-strong.js | 2 +- 10 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 test/mjsunit/strong/empty-statement.js diff --git a/src/messages.js b/src/messages.js index 1270013..b554175 100644 --- a/src/messages.js +++ b/src/messages.js @@ -164,6 +164,8 @@ var kMessages = { strong_equal: ["Please don't use '==' or '!=' in strong mode, use '===' or '!==' instead"], strong_delete: ["Please don't use 'delete' in strong mode, use maps or sets instead"], strong_var: ["Please don't use 'var' in strong mode, use 'let' or 'const' instead"], + strong_empty: ["Please don't use empty sub-statements in strong mode, make them explicit with '{}' instead"], + sloppy_lexical: ["Block-scoped declarations (let, const, function, class) not yet supported outside strict mode"], malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"], generator_poison_pill: ["'caller' and 'arguments' properties may not be accessed on generator functions."], cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"], @@ -179,7 +181,6 @@ var kMessages = { extends_value_not_a_function: ["Class extends value ", "%0", " is not a function or null"], prototype_parent_not_an_object: ["Class extends value does not have valid prototype property ", "%0"], duplicate_constructor: ["A class may only have one constructor"], - sloppy_lexical: ["Block-scoped declarations (let, const, function, class) not yet supported outside strict mode"], super_constructor_call: ["A 'super' constructor call may only appear as the first statement of a function, and its arguments may not access 'this'. Other forms are not yet supported."], duplicate_proto: ["Duplicate __proto__ fields are not allowed in object literals"], param_after_rest: ["Rest parameter must be last formal parameter"], diff --git a/src/parser.cc b/src/parser.cc index 5f849a5..e886e3a 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1642,6 +1642,20 @@ Statement* Parser::ParseExportDeclaration(bool* ok) { Statement* Parser::ParseStatement(ZoneList* labels, bool* ok) { // Statement :: + // EmptyStatement + // ... + + if (peek() == Token::SEMICOLON) { + Next(); + return factory()->NewEmptyStatement(RelocInfo::kNoPosition); + } + return ParseSubStatement(labels, ok); +} + + +Statement* Parser::ParseSubStatement(ZoneList* labels, + bool* ok) { + // Statement :: // Block // VariableStatement // EmptyStatement @@ -1669,6 +1683,11 @@ Statement* Parser::ParseStatement(ZoneList* labels, return ParseBlock(labels, ok); case Token::SEMICOLON: + if (is_strong(language_mode())) { + ReportMessageAt(scanner()->peek_location(), "strong_empty"); + *ok = false; + return NULL; + } Next(); return factory()->NewEmptyStatement(RelocInfo::kNoPosition); @@ -2535,11 +2554,11 @@ IfStatement* Parser::ParseIfStatement(ZoneList* labels, Expect(Token::LPAREN, CHECK_OK); Expression* condition = ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - Statement* then_statement = ParseStatement(labels, CHECK_OK); + Statement* then_statement = ParseSubStatement(labels, CHECK_OK); Statement* else_statement = NULL; if (peek() == Token::ELSE) { Next(); - else_statement = ParseStatement(labels, CHECK_OK); + else_statement = ParseSubStatement(labels, CHECK_OK); } else { else_statement = factory()->NewEmptyStatement(RelocInfo::kNoPosition); } @@ -2684,7 +2703,7 @@ Statement* Parser::ParseWithStatement(ZoneList* labels, Statement* stmt; { BlockState block_state(&scope_, with_scope); with_scope->set_start_position(scanner()->peek_location().beg_pos); - stmt = ParseStatement(labels, CHECK_OK); + stmt = ParseSubStatement(labels, CHECK_OK); with_scope->set_end_position(scanner()->location().end_pos); } return factory()->NewWithStatement(with_scope, expr, stmt, pos); @@ -2869,7 +2888,7 @@ DoWhileStatement* Parser::ParseDoWhileStatement( Target target(&this->target_stack_, loop); Expect(Token::DO, CHECK_OK); - Statement* body = ParseStatement(NULL, CHECK_OK); + Statement* body = ParseSubStatement(NULL, CHECK_OK); Expect(Token::WHILE, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); @@ -2899,7 +2918,7 @@ WhileStatement* Parser::ParseWhileStatement( Expect(Token::LPAREN, CHECK_OK); Expression* cond = ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - Statement* body = ParseStatement(NULL, CHECK_OK); + Statement* body = ParseSubStatement(NULL, CHECK_OK); if (loop != NULL) loop->Initialize(cond, body); return loop; @@ -3262,7 +3281,7 @@ Statement* Parser::ParseForStatement(ZoneList* labels, VariableProxy* each = scope_->NewUnresolved(factory(), name, interface, each_pos); - Statement* body = ParseStatement(NULL, CHECK_OK); + Statement* body = ParseSubStatement(NULL, CHECK_OK); InitializeForEachStatement(loop, each, enumerable, body); Block* result = factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition); @@ -3321,7 +3340,7 @@ Statement* Parser::ParseForStatement(ZoneList* labels, VariableProxy* each = scope_->NewUnresolved( factory(), name, Interface::NewValue(), each_pos); - Statement* body = ParseStatement(NULL, CHECK_OK); + Statement* body = ParseSubStatement(NULL, CHECK_OK); Block* body_block = factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition); Token::Value init_op = is_const ? Token::INIT_CONST : Token::ASSIGN; @@ -3364,7 +3383,7 @@ Statement* Parser::ParseForStatement(ZoneList* labels, Expression* enumerable = ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - Statement* body = ParseStatement(NULL, CHECK_OK); + Statement* body = ParseSubStatement(NULL, CHECK_OK); InitializeForEachStatement(loop, expression, enumerable, body); scope_ = saved_scope; for_scope->set_end_position(scanner()->location().end_pos); @@ -3416,7 +3435,7 @@ Statement* Parser::ParseForStatement(ZoneList* labels, } Expect(Token::RPAREN, CHECK_OK); - Statement* body = ParseStatement(NULL, CHECK_OK); + Statement* body = ParseSubStatement(NULL, CHECK_OK); Statement* result = NULL; if (let_bindings.length() > 0) { diff --git a/src/parser.h b/src/parser.h index 4139ba5..343da07 100644 --- a/src/parser.h +++ b/src/parser.h @@ -715,6 +715,7 @@ class Parser : public ParserBase { Scanner::Location* reserved_loc, bool* ok); void* ParseNamedImports(ZoneList* names, bool* ok); Statement* ParseStatement(ZoneList* labels, bool* ok); + Statement* ParseSubStatement(ZoneList* labels, bool* ok); Statement* ParseFunctionDeclaration(ZoneList* names, bool* ok); Statement* ParseClassDeclaration(ZoneList* names, diff --git a/src/preparser.cc b/src/preparser.cc index 964cdbf..00332ad 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -228,6 +228,14 @@ void PreParser::ParseStatementList(int end_token, bool* ok) { PreParser::Statement PreParser::ParseStatement(bool* ok) { // Statement :: + // EmptyStatement + // ... + return ParseSubStatement(ok); +} + + +PreParser::Statement PreParser::ParseSubStatement(bool* ok) { + // Statement :: // Block // VariableStatement // EmptyStatement @@ -257,6 +265,12 @@ PreParser::Statement PreParser::ParseStatement(bool* ok) { return ParseBlock(ok); case Token::SEMICOLON: + if (is_strong(language_mode())) { + PreParserTraits::ReportMessageAt(scanner()->peek_location(), + "strong_empty"); + *ok = false; + return Statement::Default(); + } Next(); return Statement::Default(); @@ -550,10 +564,10 @@ PreParser::Statement PreParser::ParseIfStatement(bool* ok) { Expect(Token::LPAREN, CHECK_OK); ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - ParseStatement(CHECK_OK); + ParseSubStatement(CHECK_OK); if (peek() == Token::ELSE) { Next(); - ParseStatement(CHECK_OK); + ParseSubStatement(CHECK_OK); } return Statement::Default(); } @@ -636,7 +650,7 @@ PreParser::Statement PreParser::ParseWithStatement(bool* ok) { Scope* with_scope = NewScope(scope_, WITH_SCOPE); BlockState block_state(&scope_, with_scope); - ParseStatement(CHECK_OK); + ParseSubStatement(CHECK_OK); return Statement::Default(); } @@ -678,7 +692,7 @@ PreParser::Statement PreParser::ParseDoWhileStatement(bool* ok) { // 'do' Statement 'while' '(' Expression ')' ';' Expect(Token::DO, CHECK_OK); - ParseStatement(CHECK_OK); + ParseSubStatement(CHECK_OK); Expect(Token::WHILE, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); ParseExpression(true, CHECK_OK); @@ -696,7 +710,7 @@ PreParser::Statement PreParser::ParseWhileStatement(bool* ok) { Expect(Token::LPAREN, CHECK_OK); ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - ParseStatement(ok); + ParseSubStatement(ok); return Statement::Default(); } @@ -733,7 +747,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - ParseStatement(CHECK_OK); + ParseSubStatement(CHECK_OK); return Statement::Default(); } } else { @@ -744,7 +758,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { ParseExpression(true, CHECK_OK); Expect(Token::RPAREN, CHECK_OK); - ParseStatement(CHECK_OK); + ParseSubStatement(CHECK_OK); return Statement::Default(); } } @@ -770,7 +784,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) { } Expect(Token::RPAREN, CHECK_OK); - ParseStatement(ok); + ParseSubStatement(ok); return Statement::Default(); } diff --git a/src/preparser.h b/src/preparser.h index e82753c..1b365af 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -1566,6 +1566,7 @@ class PreParser : public ParserBase { Statement ParseStatementListItem(bool* ok); void ParseStatementList(int end_token, bool* ok); Statement ParseStatement(bool* ok); + Statement ParseSubStatement(bool* ok); Statement ParseFunctionDeclaration(bool* ok); Statement ParseClassDeclaration(bool* ok); Statement ParseBlock(bool* ok); diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index f0c3703..95ce793 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -5517,3 +5517,33 @@ TEST(VarForbiddenInStrongMode) { RunParserSyncTest(sloppy_context_data, let_declarations, kError, NULL, 0, always_flags, arraysize(always_flags)); } + + +TEST(StrongEmptySubStatements) { + const char* sloppy_context_data[][2] = {{"", ""}, {NULL}}; + const char* strict_context_data[][2] = {{"'use strict';", ""}, {NULL}}; + const char* strong_context_data[][2] = {{"'use strong';", ""}, {NULL}}; + + const char* data[] = { + "if (1);", + "if (1) {} else;", + "while (1);", + "do; while (1);", + "for (;;);", + "for (x in []);", + "for (x of []);", + "for (const x = 0;;);", + "for (const x in []);", + "for (const x of []);", + NULL}; + + static const ParserFlag always_flags[] = { + kAllowStrongMode, kAllowHarmonyScoping + }; + RunParserSyncTest(sloppy_context_data, data, kSuccess, NULL, 0, always_flags, + arraysize(always_flags)); + RunParserSyncTest(strict_context_data, data, kSuccess, NULL, 0, always_flags, + arraysize(always_flags)); + RunParserSyncTest(strong_context_data, data, kError, NULL, 0, always_flags, + arraysize(always_flags)); +} diff --git a/test/mjsunit/strong/delete.js b/test/mjsunit/strong/delete.js index 90e229d..349af0b 100644 --- a/test/mjsunit/strong/delete.js +++ b/test/mjsunit/strong/delete.js @@ -1,4 +1,4 @@ -// Copyright 2014 the V8 project authors. All rights reserved. +// 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. diff --git a/test/mjsunit/strong/empty-statement.js b/test/mjsunit/strong/empty-statement.js new file mode 100644 index 0000000..b63fead --- /dev/null +++ b/test/mjsunit/strong/empty-statement.js @@ -0,0 +1,19 @@ +// 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: --strong-mode + +(function NoEmptySubStatement() { + assertThrows("'use strong'; if (1);", SyntaxError); + assertThrows("'use strong'; if (1) {} else;", SyntaxError); + assertThrows("'use strong'; while (1);", SyntaxError); + assertThrows("'use strong'; do; while (1);", SyntaxError); + assertThrows("'use strong'; for (;;);", SyntaxError); + assertThrows("'use strong'; for (x in []);", SyntaxError); + assertThrows("'use strong'; for (x of []);", SyntaxError); + assertThrows("'use strong'; for (let x;;);", SyntaxError); + assertThrows("'use strong'; for (let x in []);", SyntaxError); + assertThrows("'use strong'; for (let x of []);", SyntaxError); + assertThrows("'use strong'; with ({});", SyntaxError); +})(); diff --git a/test/mjsunit/strong/equality.js b/test/mjsunit/strong/equality.js index dd32644..5e2464c 100644 --- a/test/mjsunit/strong/equality.js +++ b/test/mjsunit/strong/equality.js @@ -1,4 +1,4 @@ -// Copyright 2014 the V8 project authors. All rights reserved. +// 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. diff --git a/test/mjsunit/strong/use-strong.js b/test/mjsunit/strong/use-strong.js index d472a6c..bbda266 100644 --- a/test/mjsunit/strong/use-strong.js +++ b/test/mjsunit/strong/use-strong.js @@ -1,4 +1,4 @@ -// Copyright 2014 the V8 project authors. All rights reserved. +// 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. -- 2.7.4