harmony-scoping: better error messages for let declarations in sloppy mode.
authordslomov <dslomov@chromium.org>
Tue, 18 Nov 2014 18:51:20 +0000 (10:51 -0800)
committerCommit bot <commit-bot@chromium.org>
Tue, 18 Nov 2014 18:51:26 +0000 (18:51 +0000)
R=rossberg@chromium.org
BUG=v8:2198
LOG=N

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

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

src/ast-value-factory.h
src/messages.js
src/parser.cc
src/preparser.cc
test/cctest/test-parsing.cc
test/mjsunit/harmony/block-non-strict-errors.js [new file with mode: 0644]

index 9ccd511..7b82e4a 100644 (file)
@@ -251,6 +251,7 @@ class AstValue : public ZoneObject {
   F(get_template_callsite, "GetTemplateCallSite")       \
   F(initialize_const_global, "initializeConstGlobal")   \
   F(initialize_var_global, "initializeVarGlobal")       \
+  F(let, "let")                                         \
   F(make_reference_error, "MakeReferenceErrorEmbedded") \
   F(make_syntax_error, "MakeSyntaxErrorEmbedded")       \
   F(make_type_error, "MakeTypeErrorEmbedded")           \
index 8472454..bd6fbfd 100644 (file)
@@ -180,7 +180,8 @@ var kMessages = {
   unexpected_super:              ["'super' keyword unexpected here"],
   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"]
+  duplicate_constructor:         ["A class may only have one constructor"],
+  lexical_strict_mode:           ["Lexical declarations are currently only allowed in strict mode"],
 };
 
 
index 98abbe4..5e75304 100644 (file)
@@ -2479,12 +2479,20 @@ Statement* Parser::ParseExpressionOrLabelledStatement(
 
   // Parsed expression statement, or the context-sensitive 'module' keyword.
   // Only expect semicolon in the former case.
+  // Also detect attempts at 'let' declarations in sloppy mode.
   if (!FLAG_harmony_modules || peek() != Token::IDENTIFIER ||
       scanner()->HasAnyLineTerminatorBeforeNext() ||
       expr->AsVariableProxy() == NULL ||
       expr->AsVariableProxy()->raw_name() !=
           ast_value_factory()->module_string() ||
       scanner()->literal_contains_escapes()) {
+    if (peek() == Token::IDENTIFIER && expr->AsVariableProxy() != NULL &&
+        expr->AsVariableProxy()->raw_name() ==
+            ast_value_factory()->let_string()) {
+      ReportMessage("lexical_strict_mode", NULL);
+      *ok = false;
+      return NULL;
+    }
     ExpectSemicolon(CHECK_OK);
   }
   return factory()->NewExpressionStatement(expr, pos);
@@ -3214,6 +3222,7 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
   Expect(Token::FOR, CHECK_OK);
   Expect(Token::LPAREN, CHECK_OK);
   for_scope->set_start_position(scanner()->location().beg_pos);
+  bool is_let_identifier_expression = false;
   if (peek() != Token::SEMICOLON) {
     if (peek() == Token::VAR ||
         (peek() == Token::CONST && strict_mode() == SLOPPY)) {
@@ -3325,6 +3334,10 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
       Expression* expression = ParseExpression(false, CHECK_OK);
       ForEachStatement::VisitMode mode;
       bool accept_OF = expression->IsVariableProxy();
+      is_let_identifier_expression =
+        expression->IsVariableProxy() &&
+        expression->AsVariableProxy()->raw_name() ==
+            ast_value_factory()->let_string();
 
       if (CheckInOrOf(accept_OF, &mode)) {
         expression = this->CheckAndRewriteReferenceExpression(
@@ -3357,6 +3370,13 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
   Target target(&this->target_stack_, loop);
 
   // Parsed initializer at this point.
+  // Detect attempts at 'let' declarations in sloppy mode.
+  if (peek() == Token::IDENTIFIER && strict_mode() == SLOPPY &&
+      is_let_identifier_expression) {
+    ReportMessage("lexical_strict_mode", NULL);
+    *ok = false;
+    return NULL;
+  }
   Expect(Token::SEMICOLON, CHECK_OK);
 
   // If there are let bindings, then condition and the next statement of the
index 1a2ddb6..6b0d7d9 100644 (file)
@@ -509,6 +509,13 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) {
     // accept "native function" in the preparser.
   }
   // Parsed expression statement.
+  // Detect attempts at 'let' declarations in sloppy mode.
+  if (peek() == Token::IDENTIFIER && strict_mode() == SLOPPY &&
+      expr.IsIdentifier() && expr.AsIdentifier().IsLet()) {
+    ReportMessage("lexical_strict_mode", NULL);
+    *ok = false;
+    return Statement::Default();
+  }
   ExpectSemicolon(CHECK_OK);
   return Statement::ExpressionStatement(expr);
 }
@@ -688,6 +695,7 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
 
   Expect(Token::FOR, CHECK_OK);
   Expect(Token::LPAREN, CHECK_OK);
+  bool is_let_identifier_expression = false;
   if (peek() != Token::SEMICOLON) {
     if (peek() == Token::VAR || peek() == Token::CONST ||
         (peek() == Token::LET && strict_mode() == STRICT)) {
@@ -709,6 +717,8 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
       }
     } else {
       Expression lhs = ParseExpression(false, CHECK_OK);
+      is_let_identifier_expression =
+          lhs.IsIdentifier() && lhs.AsIdentifier().IsLet();
       if (CheckInOrOf(lhs.IsIdentifier())) {
         ParseExpression(true, CHECK_OK);
         Expect(Token::RPAREN, CHECK_OK);
@@ -720,6 +730,13 @@ PreParser::Statement PreParser::ParseForStatement(bool* ok) {
   }
 
   // Parsed initializer at this point.
+  // Detect attempts at 'let' declarations in sloppy mode.
+  if (peek() == Token::IDENTIFIER && strict_mode() == SLOPPY &&
+      is_let_identifier_expression) {
+    ReportMessage("lexical_strict_mode", NULL);
+    *ok = false;
+    return Statement::Default();
+  }
   Expect(Token::SEMICOLON, CHECK_OK);
 
   if (peek() != Token::SEMICOLON) {
index 2f42ead..a36bd92 100644 (file)
@@ -4432,3 +4432,28 @@ TEST(ScanUnterminatedTemplateLiterals) {
   RunParserSyncTest(context_data, data, kError, NULL, 0, always_flags,
                     arraysize(always_flags));
 }
+
+
+TEST(LexicalScopingSloppyMode) {
+  const char* context_data[][2] = {
+      {"", ""},
+      {"function f() {", "}"},
+      {"{", "}"},
+      {NULL, NULL}};
+  const char* bad_data[] = {
+    "let x = 1;",
+    "for(let x = 1;;){}",
+    "for(let x of []){}",
+    "for(let x in []){}",
+    NULL};
+  static const ParserFlag always_flags[] = {kAllowHarmonyScoping};
+  RunParserSyncTest(context_data, bad_data, kError, NULL, 0, always_flags,
+                    arraysize(always_flags));
+
+  const char* good_data[] = {
+    "let = 1;",
+    "for(let = 1;;){}",
+    NULL};
+  RunParserSyncTest(context_data, good_data, kSuccess, NULL, 0, always_flags,
+                    arraysize(always_flags));
+}
diff --git a/test/mjsunit/harmony/block-non-strict-errors.js b/test/mjsunit/harmony/block-non-strict-errors.js
new file mode 100644 (file)
index 0000000..de27cd2
--- /dev/null
@@ -0,0 +1,35 @@
+// 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.
+
+// Flags: --harmony-scoping
+
+function CheckError(source) {
+  var exception = null;
+  try {
+    eval(source);
+  } catch (e) {
+    exception = e;
+  }
+  assertNotNull(exception);
+  assertEquals(
+      "Lexical declarations are currently only allowed in strict mode",
+      exception.message);
+}
+
+
+function CheckOk(source) {
+  eval(source);
+}
+
+CheckError("let x = 1;");
+CheckError("{ let x = 1; }");
+CheckError("function f() { let x = 1; }");
+CheckError("for (let x = 1; x < 1; x++) {}");
+CheckError("for (let x of []) {}");
+CheckError("for (let x in []) {}");
+
+CheckOk("let = 1;");
+CheckOk("{ let = 1; }");
+CheckOk("function f() { let = 1; }");
+CheckOk("for (let = 1; let < 1; let++) {}");