Tests for (pre)parse errors when "yield" is found in inappropriate places.
authormarja@chromium.org <marja@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 4 Feb 2014 18:16:45 +0000 (18:16 +0000)
committermarja@chromium.org <marja@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 4 Feb 2014 18:16:45 +0000 (18:16 +0000)
In addition:
- Fix: PreParser used to report an unexpected token one token too late when
ParsePrimaryExpression failed.
- Unified identifier handling (PreParser::GetIdentifier is now like Parser::GetIdentifier).
- Fix: PreParser used to produce "unexpected_token YIELD" errors when Parser
produced "unexpected_token_identifier"; fixed PreParser to match Parser.

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

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

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

src/preparser.cc
test/cctest/test-parsing.cc

index b3a271a..c14d141 100644 (file)
@@ -119,6 +119,7 @@ void PreParser::ReportUnexpectedToken(Token::Value token) {
                            "unexpected_token_identifier", NULL);
   case Token::FUTURE_RESERVED_WORD:
     return ReportMessageAt(source_location, "unexpected_reserved", NULL);
+  case Token::YIELD:
   case Token::FUTURE_STRICT_RESERVED_WORD:
     return ReportMessageAt(source_location,
                            is_classic_mode() ? "unexpected_token_identifier"
@@ -1183,7 +1184,8 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) {
       break;
 
     default: {
-      Next();
+      Token::Value next = Next();
+      ReportUnexpectedToken(next);
       *ok = false;
       return Expression::Default();
     }
@@ -1493,35 +1495,15 @@ PreParser::Identifier PreParser::GetIdentifierSymbol() {
 
 PreParser::Identifier PreParser::ParseIdentifier(bool* ok) {
   Token::Value next = Next();
-  switch (next) {
-    case Token::FUTURE_RESERVED_WORD: {
-      Scanner::Location location = scanner()->location();
-      ReportMessageAt(location.beg_pos, location.end_pos,
-                      "unexpected_reserved", NULL);
-      *ok = false;
-      return GetIdentifierSymbol();
-    }
-    case Token::YIELD:
-      if (scope_->is_generator()) {
-        // 'yield' in a generator is only valid as part of a YieldExpression.
-        ReportMessageAt(scanner()->location(), "unexpected_token", "yield");
-        *ok = false;
-        return Identifier::Yield();
-      }
-      // FALLTHROUGH
-    case Token::FUTURE_STRICT_RESERVED_WORD:
-      if (!is_classic_mode()) {
-        Scanner::Location location = scanner()->location();
-        ReportMessageAt(location.beg_pos, location.end_pos,
-                        "unexpected_strict_reserved", NULL);
-        *ok = false;
-      }
-      // FALLTHROUGH
-    case Token::IDENTIFIER:
-      return GetIdentifierSymbol();
-    default:
-      *ok = false;
-      return Identifier::Default();
+  if (next == Token::IDENTIFIER ||
+      (is_classic_mode() &&
+       (next == Token::FUTURE_STRICT_RESERVED_WORD ||
+        (next == Token::YIELD && !scope_->is_generator())))) {
+    return GetIdentifierSymbol();
+  } else {
+    ReportUnexpectedToken(next);
+    *ok = false;
+    return Identifier::Default();
   }
 }
 
index acd0771..ad8b8f9 100644 (file)
@@ -1531,3 +1531,68 @@ TEST(ErrorsReservedWords) {
     }
   }
 }
+
+
+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.
+  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() {", "}" },
+    { "\"use strict\"; function is_not_gen() {", "}" },
+    { "\"use strict\"; function * is_gen() {", "}" },
+    { NULL, NULL }
+  };
+
+  const char* statement_data[] = {
+    "var yield;",
+    "var foo, yield;",
+    "try { } catch (yield) { }",
+    "function yield() { }",
+    "function foo(yield) { }",
+    "function foo(bar, yield) { }",
+    "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);
+
+  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));
+    }
+  }
+}