[strong] deprecate empty sub-statements
authorAndreas Rossberg <rossberg@chromium.org>
Tue, 17 Feb 2015 16:25:49 +0000 (17:25 +0100)
committerAndreas Rossberg <rossberg@chromium.org>
Tue, 17 Feb 2015 16:26:06 +0000 (16:26 +0000)
R=marja@chromium.org
BUG=

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

Cr-Commit-Position: refs/heads/master@{#26701}

src/messages.js
src/parser.cc
src/parser.h
src/preparser.cc
src/preparser.h
test/cctest/test-parsing.cc
test/mjsunit/strong/delete.js
test/mjsunit/strong/empty-statement.js [new file with mode: 0644]
test/mjsunit/strong/equality.js
test/mjsunit/strong/use-strong.js

index 1270013..b554175 100644 (file)
@@ -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"],
index 5f849a5..e886e3a 100644 (file)
@@ -1642,6 +1642,20 @@ Statement* Parser::ParseExportDeclaration(bool* ok) {
 Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
                                   bool* ok) {
   // Statement ::
+  //   EmptyStatement
+  //   ...
+
+  if (peek() == Token::SEMICOLON) {
+    Next();
+    return factory()->NewEmptyStatement(RelocInfo::kNoPosition);
+  }
+  return ParseSubStatement(labels, ok);
+}
+
+
+Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels,
+                                     bool* ok) {
+  // Statement ::
   //   Block
   //   VariableStatement
   //   EmptyStatement
@@ -1669,6 +1683,11 @@ Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* 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<const AstRawString*>* 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<const AstRawString*>* 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<const AstRawString*>* 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<const AstRawString*>* 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<const AstRawString*>* 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<const AstRawString*>* 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) {
index 4139ba5..343da07 100644 (file)
@@ -715,6 +715,7 @@ class Parser : public ParserBase<ParserTraits> {
                           Scanner::Location* reserved_loc, bool* ok);
   void* ParseNamedImports(ZoneList<const AstRawString*>* names, bool* ok);
   Statement* ParseStatement(ZoneList<const AstRawString*>* labels, bool* ok);
+  Statement* ParseSubStatement(ZoneList<const AstRawString*>* labels, bool* ok);
   Statement* ParseFunctionDeclaration(ZoneList<const AstRawString*>* names,
                                       bool* ok);
   Statement* ParseClassDeclaration(ZoneList<const AstRawString*>* names,
index 964cdbf..00332ad 100644 (file)
@@ -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();
 }
 
index e82753c..1b365af 100644 (file)
@@ -1566,6 +1566,7 @@ class PreParser : public ParserBase<PreParserTraits> {
   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);
index f0c3703..95ce793 100644 (file)
@@ -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));
+}
index 90e229d..349af0b 100644 (file)
@@ -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 (file)
index 0000000..b63fead
--- /dev/null
@@ -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);
+})();
index dd32644..5e2464c 100644 (file)
@@ -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.
 
index d472a6c..bbda266 100644 (file)
@@ -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.