Tests and fixes for (pre)parse errors related to strict reserved words.
authormarja@chromium.org <marja@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 4 Feb 2014 12:19:53 +0000 (12:19 +0000)
committermarja@chromium.org <marja@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 4 Feb 2014 12:19:53 +0000 (12:19 +0000)
This contains the following fixes:

- We had strict_reserved_word and unexpected_strict_reserved, which one to use
was totally mixed in Parser and PreParser. Removed strict_reserved_word.
- When we saw a strict future reserved word when expecting something completely
different (such as "(" in "function foo interface"), Parser reports unexpected
identifier, whereas PreParser used to report unexpected strict reserved
word. Fixed PreParser to report unexpected identifier too.
- Unified parser and preparser error locations when the name of a function is a
strict reserved word. Now both point to the name.

BUG=3126
LOG=N
R=ulan@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@19067 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/messages.js
src/parser.cc
src/preparser.cc
test/cctest/test-parsing.cc

index 41e1d97..85351fb 100644 (file)
@@ -170,7 +170,6 @@ var kMessages = {
   strict_lhs_assignment:         ["Assignment to eval or arguments is not allowed in strict mode"],
   strict_lhs_postfix:            ["Postfix increment/decrement may not have eval or arguments operand in strict mode"],
   strict_lhs_prefix:             ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
-  strict_reserved_word:          ["Use of future reserved word in strict mode"],
   strict_delete:                 ["Delete of an unqualified identifier in strict mode."],
   strict_delete_property:        ["Cannot delete property '", "%0", "' of ", "%1"],
   strict_const:                  ["Use of const in strict mode."],
index 303c5ee..50551ea 100644 (file)
@@ -4321,17 +4321,13 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
         return NULL;
       }
       if (name_is_strict_reserved) {
-        int start_pos = scope->start_position();
-        int position = function_token_pos != RelocInfo::kNoPosition
-            ? function_token_pos : (start_pos > 0 ? start_pos - 1 : start_pos);
-        Scanner::Location location = Scanner::Location(position, start_pos);
-        ReportMessageAt(location, "strict_reserved_word",
+        ReportMessageAt(function_name_location, "unexpected_strict_reserved",
                         Vector<const char*>::empty());
         *ok = false;
         return NULL;
       }
       if (reserved_loc.IsValid()) {
-        ReportMessageAt(reserved_loc, "strict_reserved_word",
+        ReportMessageAt(reserved_loc, "unexpected_strict_reserved",
                         Vector<const char*>::empty());
         *ok = false;
         return NULL;
index c8a59bf..21010ae 100644 (file)
@@ -121,7 +121,9 @@ void PreParser::ReportUnexpectedToken(Token::Value token) {
     return ReportMessageAt(source_location, "unexpected_reserved", NULL);
   case Token::FUTURE_STRICT_RESERVED_WORD:
     return ReportMessageAt(source_location,
-                           "unexpected_strict_reserved", NULL);
+                           is_classic_mode() ? "unexpected_token_identifier"
+                                             : "unexpected_strict_reserved",
+                           NULL);
   default:
     const char* name = Token::String(token);
     ReportMessageAt(source_location, "unexpected_token", name);
@@ -304,7 +306,7 @@ PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) {
     // as name of strict function.
     const char* type = "strict_function_name";
     if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
-      type = "strict_reserved_word";
+      type = "unexpected_strict_reserved";
     }
     ReportMessageAt(location, type, NULL);
     *ok = false;
@@ -1511,7 +1513,7 @@ PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
       if (!is_classic_mode()) {
         Scanner::Location location = scanner()->location();
         ReportMessageAt(location.beg_pos, location.end_pos,
-                        "strict_reserved_word", NULL);
+                        "unexpected_strict_reserved", NULL);
         *ok = false;
       }
       // FALLTHROUGH
@@ -1565,7 +1567,7 @@ void PreParser::StrictModeIdentifierViolation(Scanner::Location location,
   if (identifier.IsFutureReserved()) {
     type = "reserved_word";
   } else if (identifier.IsFutureStrictReserved() || identifier.IsYield()) {
-    type = "strict_reserved_word";
+    type = "unexpected_strict_reserved";
   }
   if (!is_classic_mode()) {
     ReportMessageAt(location, type, NULL);
index c2651dc..e5cddbf 100644 (file)
@@ -1347,6 +1347,60 @@ TEST(PreparserStrictOctal) {
 }
 
 
+namespace {
+
+const char* use_strict_prefix = "\"use strict\";\n";
+
+const char* strict_catch_variable_preparse = "strict_catch_variable";
+const char* strict_catch_variable_parse =
+    "SyntaxError: Catch variable may not be eval or arguments in strict mode";
+
+const char* strict_function_name_preparse = "strict_function_name";
+const char* strict_function_name_parse =
+    "SyntaxError: Function name may not be eval or arguments in strict mode";
+
+const char* strict_lhs_assignment_preparse = "strict_lhs_assignment";
+const char* strict_lhs_assignment_parse =
+    "SyntaxError: Assignment to eval or arguments is not allowed in strict "
+    "mode";
+
+const char* strict_lhs_postfix_preparse = "strict_lhs_postfix";
+const char* strict_lhs_postfix_parse =
+    "SyntaxError: Postfix increment/decrement may not have eval or arguments "
+    "operand in strict mode";
+
+const char* strict_lhs_prefix_preparse = "strict_lhs_prefix";
+const char* strict_lhs_prefix_parse =
+    "SyntaxError: Prefix increment/decrement may not have eval or arguments "
+    "operand in strict mode";
+
+const char* strict_param_name_preparse = "strict_param_name";
+const char* strict_param_name_parse =
+    "SyntaxError: Parameter name eval or arguments is not allowed in strict "
+    "mode";
+
+const char* strict_var_name_preparse = "strict_var_name";
+const char* strict_var_name_parse =
+    "SyntaxError: Variable name may not be eval or arguments in strict mode";
+
+const char* unexpected_strict_reserved_preparse = "unexpected_strict_reserved";
+const char* unexpected_strict_reserved_parse =
+    "SyntaxError: Unexpected strict mode reserved word";
+
+const char* unexpected_token_identifier_preparse =
+    "unexpected_token_identifier";
+const char* unexpected_token_identifier_parse =
+    "SyntaxError: Unexpected identifier";
+
+struct ParseErrorTestCase {
+  const char* source;
+  int error_location_beg;
+  int error_location_end;
+  const char* preparse_error_message;
+  const char* parse_error_message;
+};
+
+
 void VerifyPreParseAndParseNoError(v8::Handle<v8::String> source) {
   v8::ScriptData* preparse = v8::ScriptData::PreCompile(source);
   CHECK(!preparse->HasError());
@@ -1381,6 +1435,8 @@ void VerifyPreParseAndParseErrorMessages(v8::Handle<v8::String> source,
   CHECK_EQ(error_location_end, try_catch.Message()->GetEndPosition());
 }
 
+}  // namespace
+
 
 TEST(ErrorsEvalAndArguments) {
   // Tests that both preparsing and parsing produce the right kind of errors for
@@ -1392,48 +1448,9 @@ TEST(ErrorsEvalAndArguments) {
   v8::Local<v8::Context> context = v8::Context::New(isolate);
   v8::Context::Scope context_scope(context);
 
-  const char* use_strict_prefix = "\"use strict\";\n";
   int prefix_length = i::StrLength(use_strict_prefix);
 
-  const char* strict_var_name_preparse = "strict_var_name";
-  const char* strict_var_name_parse =
-      "SyntaxError: Variable name may not be eval or arguments in strict mode";
-
-  const char* strict_catch_variable_preparse = "strict_catch_variable";
-  const char* strict_catch_variable_parse =
-      "SyntaxError: Catch variable may not be eval or arguments in strict mode";
-
-  const char* strict_function_name_preparse = "strict_function_name";
-  const char* strict_function_name_parse =
-      "SyntaxError: Function name may not be eval or arguments in strict mode";
-
-  const char* strict_param_name_preparse = "strict_param_name";
-  const char* strict_param_name_parse =
-      "SyntaxError: Parameter name eval or arguments is not allowed in strict "
-      "mode";
-
-  const char* strict_lhs_assignment_preparse = "strict_lhs_assignment";
-  const char* strict_lhs_assignment_parse =
-      "SyntaxError: Assignment to eval or arguments is not allowed in strict "
-      "mode";
-
-  const char* strict_lhs_prefix_preparse = "strict_lhs_prefix";
-  const char* strict_lhs_prefix_parse =
-      "SyntaxError: Prefix increment/decrement may not have eval or arguments "
-      "operand in strict mode";
-
-  const char* strict_lhs_postfix_preparse = "strict_lhs_postfix";
-  const char* strict_lhs_postfix_parse =
-      "SyntaxError: Postfix increment/decrement may not have eval or arguments "
-      "operand in strict mode";
-
-  struct TestCase {
-    const char* source;
-    int error_location_beg;
-    int error_location_end;
-    const char* preparse_error_message;
-    const char* parse_error_message;
-  } test_cases[] = {
+  ParseErrorTestCase test_cases[] = {
     {"var eval = 42;", 4, 8, strict_var_name_preparse, strict_var_name_parse},
     {"var arguments = 42;", 4, 13, strict_var_name_preparse,
      strict_var_name_parse},
@@ -1472,13 +1489,105 @@ TEST(ErrorsEvalAndArguments) {
   for (int i = 0; test_cases[i].source; ++i) {
     v8::Handle<v8::String> source =
         v8::String::NewFromUtf8(isolate, test_cases[i].source);
-
     VerifyPreParseAndParseNoError(source);
 
     v8::Handle<v8::String> strict_source = v8::String::Concat(
         v8::String::NewFromUtf8(isolate, use_strict_prefix),
         v8::String::NewFromUtf8(isolate, test_cases[i].source));
+    VerifyPreParseAndParseErrorMessages(
+        strict_source,
+        test_cases[i].error_location_beg + prefix_length,
+        test_cases[i].error_location_end + prefix_length,
+        test_cases[i].preparse_error_message,
+        test_cases[i].parse_error_message);
+  }
+
+  // Test cases which produce an error also in non-strict mode.
+  ParseErrorTestCase error_test_cases[] = {
+    // In this case we expect something completely different, "(", and get
+    // "eval". It's always "unexpected identifier, whether we are in strict mode
+    // or not.
+    {"function foo eval", 13, 17, unexpected_token_identifier_preparse,
+     unexpected_token_identifier_parse},
+    {"\"use strict\"; function foo eval", 27, 31,
+     unexpected_token_identifier_preparse, unexpected_token_identifier_parse},
+    {NULL, 0, 0, NULL, NULL}
+  };
 
+  for (int i = 0; error_test_cases[i].source; ++i) {
+    v8::Handle<v8::String> source =
+        v8::String::NewFromUtf8(isolate, error_test_cases[i].source);
+    VerifyPreParseAndParseErrorMessages(
+        source,
+        error_test_cases[i].error_location_beg,
+        error_test_cases[i].error_location_end,
+        error_test_cases[i].preparse_error_message,
+        error_test_cases[i].parse_error_message);
+  }
+
+  // Test cases where only a sub-scope is strict.
+  ParseErrorTestCase scoped_test_cases[] = {
+    {"var eval = 1; function foo() { \"use strict\"; var arguments = 2; }",
+     49, 58, strict_var_name_preparse, strict_var_name_parse},
+    {NULL, 0, 0, NULL, NULL}
+  };
+
+  for (int i = 0; scoped_test_cases[i].source; ++i) {
+    v8::Handle<v8::String> source =
+        v8::String::NewFromUtf8(isolate, scoped_test_cases[i].source);
+    VerifyPreParseAndParseErrorMessages(
+        source,
+        scoped_test_cases[i].error_location_beg,
+        scoped_test_cases[i].error_location_end,
+        scoped_test_cases[i].preparse_error_message,
+        scoped_test_cases[i].parse_error_message);
+  }
+}
+
+
+TEST(ErrorsFutureStrictReservedWords) {
+  // Tests that both preparsing and parsing produce the right kind of errors for
+  // using future strict reserved words as identifiers. Without the strict mode,
+  // it's ok to use future strict reserved words as identifiers. With the strict
+  // mode, it isn't.
+  v8::Isolate* isolate = CcTest::isolate();
+  v8::HandleScope handles(isolate);
+  v8::Local<v8::Context> context = v8::Context::New(isolate);
+  v8::Context::Scope context_scope(context);
+
+  const char* use_strict_prefix = "\"use strict\";\n";
+  int prefix_length = i::StrLength(use_strict_prefix);
+
+  ParseErrorTestCase test_cases[] = {
+    {"var interface = 42;", 4, 13, unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {"var foo, interface;", 9, 18, unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {"try { } catch (interface) { }", 15, 24,
+     unexpected_strict_reserved_preparse, unexpected_strict_reserved_parse},
+    {"function interface() { }", 9, 18, unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {"function foo(interface) { }", 13, 22, unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {"function foo(bar, interface) { }", 18, 27,
+     unexpected_strict_reserved_preparse, unexpected_strict_reserved_parse},
+    {"interface = 1;", 0, 9, unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {"++interface;", 2, 11, unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {"interface++;", 0, 9, unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {NULL, 0, 0, NULL, NULL}
+  };
+
+  for (int i = 0; test_cases[i].source; ++i) {
+    v8::Handle<v8::String> source =
+        v8::String::NewFromUtf8(isolate, test_cases[i].source);
+    VerifyPreParseAndParseNoError(source);
+
+    v8::Handle<v8::String> strict_source = v8::String::Concat(
+        v8::String::NewFromUtf8(isolate, use_strict_prefix),
+        v8::String::NewFromUtf8(isolate, test_cases[i].source));
     VerifyPreParseAndParseErrorMessages(
         strict_source,
         test_cases[i].error_location_beg + prefix_length,
@@ -1486,4 +1595,48 @@ TEST(ErrorsEvalAndArguments) {
         test_cases[i].preparse_error_message,
         test_cases[i].parse_error_message);
   }
+
+  // Test cases which produce an error also in non-strict mode.
+  ParseErrorTestCase error_test_cases[] = {
+    // In this case we expect something completely different, "(", and get a
+    // strict reserved word. Note that this differs from the "eval or arguments"
+    // case; there we just report an unexpected identifier.
+    {"function foo interface", 13, 22,
+     unexpected_token_identifier_preparse,
+     unexpected_token_identifier_parse},
+    {"\"use strict\"; function foo interface", 27, 36,
+     unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {NULL, 0, 0, NULL, NULL}
+  };
+
+  for (int i = 0; error_test_cases[i].source; ++i) {
+    v8::Handle<v8::String> source =
+        v8::String::NewFromUtf8(isolate, error_test_cases[i].source);
+    VerifyPreParseAndParseErrorMessages(
+        source,
+        error_test_cases[i].error_location_beg,
+        error_test_cases[i].error_location_end,
+        error_test_cases[i].preparse_error_message,
+        error_test_cases[i].parse_error_message);
+  }
+
+  // Test cases where only a sub-scope is strict.
+  ParseErrorTestCase scoped_test_cases[] = {
+    {"var interface = 1; function foo() { \"use strict\"; var interface = 2; }",
+     54, 63, unexpected_strict_reserved_preparse,
+     unexpected_strict_reserved_parse},
+    {NULL, 0, 0, NULL, NULL}
+  };
+
+  for (int i = 0; scoped_test_cases[i].source; ++i) {
+    v8::Handle<v8::String> source =
+        v8::String::NewFromUtf8(isolate, scoped_test_cases[i].source);
+    VerifyPreParseAndParseErrorMessages(
+        source,
+        scoped_test_cases[i].error_location_beg,
+        scoped_test_cases[i].error_location_end,
+        scoped_test_cases[i].preparse_error_message,
+        scoped_test_cases[i].parse_error_message);
+  }
 }