Better tests for the Parser / Preparser unification.
authormarja@chromium.org <marja@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 5 Feb 2014 15:30:01 +0000 (15:30 +0000)
committermarja@chromium.org <marja@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 5 Feb 2014 15:30:01 +0000 (15:30 +0000)
We need a way to assert that for a given source code snippet, an error *is*
produced or *is not* produced. Otherwise we might accidentally create new
errors or start accepting code which was previously not accepted. Just checking
that Parser and PreParser produce the same result doesn't cut it.

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

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

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

test/cctest/test-parsing.cc

index bdf99b5..303da3b 100644 (file)
@@ -1099,6 +1099,13 @@ enum ParserFlag {
 };
 
 
+enum ParserSyncTestResult {
+  kSuccessOrError,
+  kSuccess,
+  kError
+};
+
+
 void SetParserFlags(i::ParserBase* parser, i::EnumSet<ParserFlag> flags) {
   parser->set_allow_lazy(flags.Contains(kAllowLazy));
   parser->set_allow_natives_syntax(flags.Contains(kAllowNativesSyntax));
@@ -1112,7 +1119,8 @@ void SetParserFlags(i::ParserBase* parser, i::EnumSet<ParserFlag> flags) {
 
 
 void TestParserSyncWithFlags(i::Handle<i::String> source,
-                             i::EnumSet<ParserFlag> flags) {
+                             i::EnumSet<ParserFlag> flags,
+                             ParserSyncTestResult result) {
   i::Isolate* isolate = CcTest::i_isolate();
   i::Factory* factory = isolate->factory();
 
@@ -1154,6 +1162,17 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
     i::Handle<i::String> message_string =
         i::Handle<i::String>::cast(i::GetProperty(exception_handle, "message"));
 
+    if (result == kSuccess) {
+      i::OS::Print(
+          "Parser failed on:\n"
+          "\t%s\n"
+          "with error:\n"
+          "\t%s\n"
+          "However, we expected no error.",
+          source->ToCString().get(), message_string->ToCString().get());
+      CHECK(false);
+    }
+
     if (!data.has_error()) {
       i::OS::Print(
           "Parser failed on:\n"
@@ -1187,13 +1206,21 @@ void TestParserSyncWithFlags(i::Handle<i::String> source,
         "However, the parser succeeded",
         source->ToCString().get(), FormatMessage(&data)->ToCString().get());
     CHECK(false);
+  } else if (result == kError) {
+    i::OS::Print(
+        "Expected error on:\n"
+        "\t%s\n"
+        "However, parser and preparser succeeded",
+        source->ToCString().get());
+    CHECK(false);
   }
 }
 
 
 void TestParserSync(const char* source,
                     const ParserFlag* flag_list,
-                    size_t flag_list_length) {
+                    size_t flag_list_length,
+                    ParserSyncTestResult result = kSuccessOrError) {
   i::Handle<i::String> str =
       CcTest::i_isolate()->factory()->NewStringFromAscii(i::CStrVector(source));
   for (int bits = 0; bits < (1 << flag_list_length); bits++) {
@@ -1201,7 +1228,7 @@ void TestParserSync(const char* source,
     for (size_t flag_index = 0; flag_index < flag_list_length; flag_index++) {
       if ((bits & (1 << flag_index)) != 0) flags.Add(flag_list[flag_index]);
     }
-    TestParserSyncWithFlags(str, flags);
+    TestParserSyncWithFlags(str, flags, result);
   }
 }
 
@@ -1347,15 +1374,52 @@ TEST(PreparserStrictOctal) {
 }
 
 
+void RunParserSyncTest(const char* context_data[][2],
+                       const char* statement_data[],
+                       ParserSyncTestResult result) {
+  v8::HandleScope handles(CcTest::isolate());
+  v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
+  v8::Context::Scope context_scope(context);
+
+  int marker;
+  CcTest::i_isolate()->stack_guard()->SetStackLimit(
+      reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+
+  static const ParserFlag flags[] = {
+    kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators,
+    kAllowForOf
+  };
+  for (int i = 0; context_data[i][0] != NULL; ++i) {
+    for (int j = 0; statement_data[j] != NULL; ++j) {
+      int kPrefixLen = i::StrLength(context_data[i][0]);
+      int kStatementLen = i::StrLength(statement_data[j]);
+      int kSuffixLen = i::StrLength(context_data[i][1]);
+      int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen;
+
+      // Plug the source code pieces together.
+      i::ScopedVector<char> program(kProgramSize + 1);
+      int length = i::OS::SNPrintF(program,
+                                   "%s%s%s",
+                                   context_data[i][0],
+                                   statement_data[j],
+                                   context_data[i][1]);
+      CHECK(length == kProgramSize);
+      TestParserSync(program.start(),
+                     flags,
+                     ARRAY_SIZE(flags),
+                     result);
+    }
+  }
+}
+
+
 TEST(ErrorsEvalAndArguments) {
   // Tests that both preparsing and parsing produce the right kind of errors for
   // using "eval" and "arguments" as identifiers. Without the strict mode, it's
   // ok to use "eval" or "arguments" as identifiers. With the strict mode, it
   // isn't.
   const char* context_data[][2] = {
-    { "", "" },
-    { "\"use strict\";", "}" },
-    { "var eval; function test_func() {", "}"},
+    { "\"use strict\";", "" },
     { "var eval; function test_func() {\"use strict\"; ", "}"},
     { NULL, NULL }
   };
@@ -1384,36 +1448,67 @@ TEST(ErrorsEvalAndArguments) {
     NULL
   };
 
-  v8::HandleScope handles(CcTest::isolate());
-  v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
-  v8::Context::Scope context_scope(context);
+  RunParserSyncTest(context_data, statement_data, kError);
+}
 
-  int marker;
-  CcTest::i_isolate()->stack_guard()->SetStackLimit(
-      reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
 
-  static const ParserFlag flags[] = {
-    kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators,
-    kAllowForOf
+TEST(NoErrorsEvalAndArgumentsClassic) {
+  // Tests that both preparsing and parsing accept "eval" and "arguments" as
+  // identifiers when needed.
+  const char* context_data[][2] = {
+    { "", "" },
+    { "function test_func() {", "}"},
+    { NULL, NULL }
   };
-  for (int i = 0; context_data[i][0] != NULL; ++i) {
-    for (int j = 0; statement_data[j] != NULL; ++j) {
-      int kPrefixLen = i::StrLength(context_data[i][0]);
-      int kStatementLen = i::StrLength(statement_data[j]);
-      int kSuffixLen = i::StrLength(context_data[i][1]);
-      int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen;
 
-      // Plug the source code pieces together.
-      i::ScopedVector<char> program(kProgramSize + 1);
-      int length = i::OS::SNPrintF(program,
-                                   "%s%s%s",
-                                   context_data[i][0],
-                                   statement_data[j],
-                                   context_data[i][1]);
-      CHECK(length == kProgramSize);
-      TestParserSync(program.start(), flags, ARRAY_SIZE(flags));
-    }
-  }
+  const char* statement_data[] = {
+    "var eval;",
+    "var arguments",
+    "var foo, eval;",
+    "var foo, arguments;",
+    "try { } catch (eval) { }",
+    "try { } catch (arguments) { }",
+    "function eval() { }",
+    "function arguments() { }",
+    "function foo(eval) { }",
+    "function foo(arguments) { }",
+    "function foo(bar, eval) { }",
+    "function foo(bar, arguments) { }",
+    "eval = 1;",
+    "arguments = 1;",
+    "var foo = eval = 1;",
+    "var foo = arguments = 1;",
+    "++eval;",
+    "++arguments;",
+    "eval++;",
+    "arguments++;",
+    NULL
+  };
+
+  RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+TEST(NoErrorsEvalAndArgumentsStrict) {
+  const char* context_data[][2] = {
+    { "\"use strict\";", "" },
+    { "function test_func() { \"use strict\";", "}" },
+    { NULL, NULL }
+  };
+
+  const char* statement_data[] = {
+    "eval;",
+    "arguments;",
+    "var foo = eval;",
+    "var foo = arguments;",
+    "var foo = { eval: 1 };",
+    "var foo = { arguments: 1 };",
+    "var foo = { }; foo.eval = {};",
+    "var foo = { }; foo.arguments = {};",
+    NULL
+  };
+
+  RunParserSyncTest(context_data, statement_data, kSuccess);
 }
 
 
@@ -1423,10 +1518,8 @@ TEST(ErrorsFutureStrictReservedWords) {
   // it's ok to use future strict reserved words as identifiers. With the strict
   // mode, it isn't.
   const char* context_data[][2] = {
-    { "", "" },
-    { "\"use strict\";", "}" },
-    { "var eval; function test_func() {", "}"},
-    { "var eval; function test_func() {\"use strict\"; ", "}"},
+    { "\"use strict\";", "" },
+    { "function test_func() {\"use strict\"; ", "}"},
     { NULL, NULL }
   };
 
@@ -1444,36 +1537,32 @@ TEST(ErrorsFutureStrictReservedWords) {
     NULL
   };
 
-  v8::HandleScope handles(CcTest::isolate());
-  v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
-  v8::Context::Scope context_scope(context);
+  RunParserSyncTest(context_data, statement_data, kError);
+}
 
-  int marker;
-  CcTest::i_isolate()->stack_guard()->SetStackLimit(
-      reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
 
-  static const ParserFlag flags[] = {
-    kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators,
-    kAllowForOf
+TEST(NoErrorsFutureStrictReservedWords) {
+  const char* context_data[][2] = {
+    { "", "" },
+    { "function test_func() {", "}"},
+    { NULL, NULL }
   };
-  for (int i = 0; context_data[i][0] != NULL; ++i) {
-    for (int j = 0; statement_data[j] != NULL; ++j) {
-      int kPrefixLen = i::StrLength(context_data[i][0]);
-      int kStatementLen = i::StrLength(statement_data[j]);
-      int kSuffixLen = i::StrLength(context_data[i][1]);
-      int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen;
 
-      // Plug the source code pieces together.
-      i::ScopedVector<char> program(kProgramSize + 1);
-      int length = i::OS::SNPrintF(program,
-                                   "%s%s%s",
-                                   context_data[i][0],
-                                   statement_data[j],
-                                   context_data[i][1]);
-      CHECK(length == kProgramSize);
-      TestParserSync(program.start(), flags, ARRAY_SIZE(flags));
-    }
-  }
+  const char* statement_data[] = {
+    "var interface;",
+    "var foo, interface;",
+    "try { } catch (interface) { }",
+    "function interface() { }",
+    "function foo(interface) { }",
+    "function foo(bar, interface) { }",
+    "interface = 1;",
+    "var foo = interface = 1;",
+    "++interface;",
+    "interface++;",
+    NULL
+  };
+
+  RunParserSyncTest(context_data, statement_data, kSuccess);
 }
 
 
@@ -1483,7 +1572,7 @@ TEST(ErrorsReservedWords) {
   // strict mode.
   const char* context_data[][2] = {
     { "", "" },
-    { "\"use strict\";", "}" },
+    { "\"use strict\";", "" },
     { "var eval; function test_func() {", "}"},
     { "var eval; function test_func() {\"use strict\"; ", "}"},
     { NULL, NULL }
@@ -1504,53 +1593,70 @@ TEST(ErrorsReservedWords) {
     NULL
   };
 
-  v8::HandleScope handles(CcTest::isolate());
-  v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
-  v8::Context::Scope context_scope(context);
+  RunParserSyncTest(context_data, statement_data, kError);
+}
 
-  int marker;
-  CcTest::i_isolate()->stack_guard()->SetStackLimit(
-      reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
 
-  static const ParserFlag flags[] = {
-    kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators,
-    kAllowForOf
+TEST(NoErrorsYieldClassic) {
+  // In classic mode, it's okay to use "yield" as identifier, *except* inside a
+  // generator (see next test).
+  const char* context_data[][2] = {
+    { "", "" },
+    { "function is_not_gen() {", "}" },
+    { NULL, NULL }
   };
-  for (int i = 0; context_data[i][0] != NULL; ++i) {
-    for (int j = 0; statement_data[j] != NULL; ++j) {
-      int kPrefixLen = i::StrLength(context_data[i][0]);
-      int kStatementLen = i::StrLength(statement_data[j]);
-      int kSuffixLen = i::StrLength(context_data[i][1]);
-      int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen;
 
-      // Plug the source code pieces together.
-      i::ScopedVector<char> program(kProgramSize + 1);
-      int length = i::OS::SNPrintF(program,
-                                   "%s%s%s",
-                                   context_data[i][0],
-                                   statement_data[j],
-                                   context_data[i][1]);
-      CHECK(length == kProgramSize);
-      TestParserSync(program.start(), flags, ARRAY_SIZE(flags));
-    }
-  }
+  const char* statement_data[] = {
+    "var yield;",
+    "var foo, yield;",
+    "try { } catch (yield) { }",
+    "function yield() { }",
+    "function foo(yield) { }",
+    "function foo(bar, yield) { }",
+    "yield = 1;",
+    "var foo = yield = 1;",
+    "++yield;",
+    "yield++;",
+    NULL
+  };
+
+  RunParserSyncTest(context_data, statement_data, kSuccess);
 }
 
 
-TEST(ErrorsYield) {
-  // Tests that both preparsing and parsing produce the right kind of errors for
-  // using yield as identifier. In non-strict mode, it's okay to use "yield" as
-  // an identifier, *except* inside a generator function. In strict mode, it's
-  // never okay.
+TEST(ErrorsYieldClassicGenerator) {
   const char* context_data[][2] = {
-    { "", "" },
-    { "\"use strict\";", "}" },
-    { "var eval; function test_func() {", "}"},
-    { "var eval; function test_func() {\"use strict\"; ", "}"},
-    { "function is_not_gen() {", "}" },
     { "function * is_gen() {", "}" },
+    { NULL, NULL }
+  };
+
+  const char* statement_data[] = {
+    "var yield;",
+    "var foo, yield;",
+    "try { } catch (yield) { }",
+    "function yield() { }",
+    // BUG: These should not be allowed, but they are (if kAllowGenerators is
+    // set)
+    // "function foo(yield) { }",
+    // "function foo(bar, yield) { }",
+    "yield = 1;",
+    "var foo = yield = 1;",
+    "++yield;",
+    "yield++;",
+    NULL
+  };
+
+  // If generators are not allowed, the error will be produced at the '*' token,
+  // so this test works both with and without the kAllowGenerators flag.
+  RunParserSyncTest(context_data, statement_data, kError);
+}
+
+
+TEST(ErrorsYieldStrict) {
+  const char* context_data[][2] = {
+    { "\"use strict\";", "" },
     { "\"use strict\"; function is_not_gen() {", "}" },
-    { "\"use strict\"; function * is_gen() {", "}" },
+    { "function test_func() {\"use strict\"; ", "}"},
     { NULL, NULL }
   };
 
@@ -1565,126 +1671,127 @@ TEST(ErrorsYield) {
     "var foo = yield = 1;",
     "++yield;",
     "yield++;",
-    "yield 2;",  // this is legal inside generator
-    "yield * 2;",  // this is legal inside generator
     NULL
   };
 
-  v8::HandleScope handles(CcTest::isolate());
-  v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
-  v8::Context::Scope context_scope(context);
+  RunParserSyncTest(context_data, statement_data, kError);
+}
 
-  int marker;
-  CcTest::i_isolate()->stack_guard()->SetStackLimit(
-      reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
 
-  static const ParserFlag flags[] = {
-    kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators,
-    kAllowForOf
+TEST(ErrorsYield) {
+  const char* context_data[][2] = {
+    { "function * is_gen() {", "}" },
+    { NULL, NULL }
   };
-  for (int i = 0; context_data[i][0] != NULL; ++i) {
-    for (int j = 0; statement_data[j] != NULL; ++j) {
-      int kPrefixLen = i::StrLength(context_data[i][0]);
-      int kStatementLen = i::StrLength(statement_data[j]);
-      int kSuffixLen = i::StrLength(context_data[i][1]);
-      int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen;
 
-      // Plug the source code pieces together.
-      i::ScopedVector<char> program(kProgramSize + 1);
-      int length = i::OS::SNPrintF(program,
-                                   "%s%s%s",
-                                   context_data[i][0],
-                                   statement_data[j],
-                                   context_data[i][1]);
-      CHECK(length == kProgramSize);
-      TestParserSync(program.start(), flags, ARRAY_SIZE(flags));
-    }
-  }
+  const char* statement_data[] = {
+    "yield 2;",  // this is legal inside generator
+    "yield * 2;",  // this is legal inside generator
+    NULL
+  };
+
+  // Here we cannot assert that there is no error, since there will be without
+  // the kAllowGenerators flag. However, we test that Parser and PreParser
+  // produce the same errors.
+  RunParserSyncTest(context_data, statement_data, kSuccessOrError);
 }
 
 
 TEST(ErrorsNameOfStrictFunction) {
   // Tests that illegal tokens as names of a strict function produce the correct
   // errors.
+  const char* context_data[][2] = {
+    { "", ""},
+    { "\"use strict\";", ""},
+    { NULL, NULL }
+  };
+
   const char* statement_data[] = {
-    "function eval() { }",  // legal
-    "function eval() {\"use strict\";}",  // illegal
-    "function arguments() { }",  // legal
-    "function arguments() {\"use strict\";}",  // illegal
+    "function eval() {\"use strict\";}",
+    "function arguments() {\"use strict\";}",
+    "function interface() {\"use strict\";}",
+    "function yield() {\"use strict\";}",
     // Future reserved words are always illegal
-    "function super() { }",  // illegal
-    "function super() {\"use strict\";}",  // illegal
-    "function interface() { }",  // legal
-    "function interface() {\"use strict\";}",  // illegal
-    "function yield() { }",  // legal
-    "function yield() {\"use strict\";}",  // illegal
+    "function super() { }",
+    "function super() {\"use strict\";}",
     NULL
   };
 
-  v8::HandleScope handles(CcTest::isolate());
-  v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
-  v8::Context::Scope context_scope(context);
+  RunParserSyncTest(context_data, statement_data, kError);
+}
 
-  int marker;
-  CcTest::i_isolate()->stack_guard()->SetStackLimit(
-      reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
 
-  static const ParserFlag flags[] = {
-    kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators,
-    kAllowForOf
+TEST(NoErrorsNameOfStrictFunction) {
+  const char* context_data[][2] = {
+    { "", ""},
+    { NULL, NULL }
   };
-  for (int j = 0; statement_data[j] != NULL; ++j) {
-    TestParserSync(statement_data[j], flags, ARRAY_SIZE(flags));
-  }
+
+  const char* statement_data[] = {
+    "function eval() { }",
+    "function arguments() { }",
+    "function interface() { }",
+    "function yield() { }",
+    NULL
+  };
+
+  RunParserSyncTest(context_data, statement_data, kSuccess);
+}
+
+
+
+TEST(ErrorsIllegalWordsAsLabelsClassic) {
+  // Using future reserved words as labels is always an error.
+  const char* context_data[][2] = {
+    { "", ""},
+    { "function test_func() {", "}" },
+    { NULL, NULL }
+  };
+
+  const char* statement_data[] = {
+    "super: while(true) { break super; }",
+    NULL
+  };
+
+  RunParserSyncTest(context_data, statement_data, kError);
 }
 
 
-TEST(ErrorsIllegalWordsAsLabels) {
+TEST(ErrorsIllegalWordsAsLabelsStrict) {
   // Tests that illegal tokens as labels produce the correct errors.
   const char* context_data[][2] = {
-    { "", "" },
-    { "\"use strict\";", "}" },
+    { "\"use strict\";", "" },
+    { "function test_func() {\"use strict\"; ", "}"},
     { NULL, NULL }
   };
 
   const char* statement_data[] = {
-    "mylabel: while(true) { break mylabel; }",
-    "eval: while(true) { break eval; }",
-    "arguments: while(true) { break arguments; }",
-    "super: while(true) { break super; }",  // always illegal
+    "super: while(true) { break super; }",
     "interface: while(true) { break interface; }",
     "yield: while(true) { break yield; }",
     NULL
   };
 
-  v8::HandleScope handles(CcTest::isolate());
-  v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
-  v8::Context::Scope context_scope(context);
+  RunParserSyncTest(context_data, statement_data, kError);
+}
 
-  int marker;
-  CcTest::i_isolate()->stack_guard()->SetStackLimit(
-      reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
 
-  static const ParserFlag flags[] = {
-    kAllowLazy, kAllowHarmonyScoping, kAllowModules, kAllowGenerators,
-    kAllowForOf
+TEST(NoErrorsIllegalWordsAsLabels) {
+  // Using eval and arguments as labels is legal even in strict mode.
+  const char* context_data[][2] = {
+    { "", ""},
+    { "function test_func() {", "}" },
+    { "\"use strict\";", "" },
+    { "\"use strict\"; function test_func() {", "}" },
+    { NULL, NULL }
   };
-  for (int i = 0; context_data[i][0] != NULL; ++i) {
-    for (int j = 0; statement_data[j] != NULL; ++j) {
-      int kPrefixLen = i::StrLength(context_data[i][0]);
-      int kStatementLen = i::StrLength(statement_data[j]);
-      int kSuffixLen = i::StrLength(context_data[i][1]);
-      int kProgramSize = kPrefixLen + kStatementLen + kSuffixLen;
 
-      // Plug the source code pieces together.
-      i::ScopedVector<char> program(kProgramSize + 1);
-      int length = i::OS::SNPrintF(program,
-                                   "%s%s%s",
-                                   context_data[i][0],
-                                   statement_data[j],
-                                   context_data[i][1]);
-      CHECK(length == kProgramSize);
-      TestParserSync(program.start(), flags, ARRAY_SIZE(flags));
-    }
-  }
+  const char* statement_data[] = {
+    "mylabel: while(true) { break mylabel; }",
+    "eval: while(true) { break eval; }",
+    "arguments: while(true) { break arguments; }",
+    NULL
+  };
+
+  RunParserSyncTest(context_data, statement_data, kSuccess);
 }