diff --git a/src/parser.h b/src/parser.h index 74f3944718..db9071a98b 100644 --- a/src/parser.h +++ b/src/parser.h @@ -853,8 +853,7 @@ class Parser : public ParserBase { bool ParserTraits::IsFutureStrictReserved( const AstRawString* identifier) const { - return identifier->IsOneByteEqualTo("yield") || - parser_->scanner()->IdentifierIsFutureStrictReserved(identifier); + return parser_->scanner()->IdentifierIsFutureStrictReserved(identifier); } diff --git a/src/preparser.cc b/src/preparser.cc index 2978cdda98..9fd6e23d3a 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -50,6 +50,8 @@ PreParserIdentifier PreParserTraits::GetSymbol(Scanner* scanner) { return PreParserIdentifier::FutureStrictReserved(); } else if (scanner->current_token() == Token::LET) { return PreParserIdentifier::Let(); + } else if (scanner->current_token() == Token::STATIC) { + return PreParserIdentifier::Static(); } else if (scanner->current_token() == Token::YIELD) { return PreParserIdentifier::Yield(); } @@ -491,8 +493,7 @@ PreParser::Statement PreParser::ParseExpressionOrLabelledStatement(bool* ok) { // identifier. DCHECK(!expr.AsIdentifier().IsFutureReserved()); DCHECK(strict_mode() == SLOPPY || - (!expr.AsIdentifier().IsFutureStrictReserved() && - !expr.AsIdentifier().IsYield())); + !IsFutureStrictReserved(expr.AsIdentifier())); Consume(Token::COLON); return ParseStatement(ok); // Preparsing is disabled for extensions (because the extension details diff --git a/src/preparser.h b/src/preparser.h index 3a2e28fa5f..ddf9cecc66 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -336,11 +336,9 @@ class ParserBase : public Traits { bool peek_any_identifier() { Token::Value next = peek(); - return next == Token::IDENTIFIER || - next == Token::FUTURE_RESERVED_WORD || - next == Token::FUTURE_STRICT_RESERVED_WORD || - next == Token::LET || - next == Token::YIELD; + return next == Token::IDENTIFIER || next == Token::FUTURE_RESERVED_WORD || + next == Token::FUTURE_STRICT_RESERVED_WORD || next == Token::LET || + next == Token::STATIC || next == Token::YIELD; } bool CheckContextualKeyword(Vector keyword) { @@ -606,6 +604,9 @@ class PreParserIdentifier { static PreParserIdentifier Let() { return PreParserIdentifier(kLetIdentifier); } + static PreParserIdentifier Static() { + return PreParserIdentifier(kStaticIdentifier); + } static PreParserIdentifier Yield() { return PreParserIdentifier(kYieldIdentifier); } @@ -619,6 +620,8 @@ class PreParserIdentifier { bool IsArguments(const AstValueFactory* = NULL) const { return type_ == kArgumentsIdentifier; } + bool IsLet() const { return type_ == kLetIdentifier; } + bool IsStatic() const { return type_ == kStaticIdentifier; } bool IsYield() const { return type_ == kYieldIdentifier; } bool IsPrototype() const { return type_ == kPrototypeIdentifier; } bool IsConstructor() const { return type_ == kConstructorIdentifier; } @@ -627,14 +630,15 @@ class PreParserIdentifier { } bool IsFutureReserved() const { return type_ == kFutureReservedIdentifier; } bool IsFutureStrictReserved() const { - return type_ == kFutureStrictReservedIdentifier; + return type_ == kFutureStrictReservedIdentifier || + type_ == kLetIdentifier || type_ == kStaticIdentifier || + type_ == kYieldIdentifier; } bool IsValidStrictVariable() const { return type_ == kUnknownIdentifier; } V8_INLINE bool IsValidArrowParam() const { // A valid identifier can be an arrow function parameter // except for eval, arguments, yield, and reserved keywords. - return !(IsEval() || IsArguments() || IsYield() || - IsFutureStrictReserved()); + return !(IsEval() || IsArguments() || IsFutureStrictReserved()); } // Allow identifier->name()[->length()] to work. The preparser @@ -651,6 +655,7 @@ class PreParserIdentifier { kFutureReservedIdentifier, kFutureStrictReservedIdentifier, kLetIdentifier, + kStaticIdentifier, kYieldIdentifier, kEvalIdentifier, kArgumentsIdentifier, @@ -1172,7 +1177,7 @@ class PreParserTraits { } static bool IsFutureStrictReserved(PreParserIdentifier identifier) { - return identifier.IsYield() || identifier.IsFutureStrictReserved(); + return identifier.IsFutureStrictReserved(); } static bool IsBoilerplateProperty(PreParserExpression property) { @@ -1593,6 +1598,7 @@ void ParserBase::ReportUnexpectedToken(Token::Value token) { case Token::FUTURE_RESERVED_WORD: return ReportMessageAt(source_location, "unexpected_reserved"); case Token::LET: + case Token::STATIC: case Token::YIELD: case Token::FUTURE_STRICT_RESERVED_WORD: return ReportMessageAt(source_location, strict_mode() == SLOPPY @@ -1622,8 +1628,8 @@ typename ParserBase::IdentifierT ParserBase::ParseIdentifier( return name; } else if (strict_mode() == SLOPPY && (next == Token::FUTURE_STRICT_RESERVED_WORD || - (next == Token::LET) || - (next == Token::YIELD && !is_generator()))) { + next == Token::LET || next == Token::STATIC || + (next == Token::YIELD && !is_generator()))) { return this->GetSymbol(scanner()); } else { this->ReportUnexpectedToken(next); @@ -1640,8 +1646,8 @@ typename ParserBase::IdentifierT ParserBase< Token::Value next = Next(); if (next == Token::IDENTIFIER) { *is_strict_reserved = false; - } else if (next == Token::FUTURE_STRICT_RESERVED_WORD || - next == Token::LET || + } else if (next == Token::FUTURE_STRICT_RESERVED_WORD || next == Token::LET || + next == Token::STATIC || (next == Token::YIELD && !this->is_generator())) { *is_strict_reserved = true; } else { @@ -1662,7 +1668,7 @@ typename ParserBase::IdentifierT ParserBase::ParseIdentifierName(bool* ok) { Token::Value next = Next(); if (next != Token::IDENTIFIER && next != Token::FUTURE_RESERVED_WORD && - next != Token::LET && next != Token::YIELD && + next != Token::LET && next != Token::STATIC && next != Token::YIELD && next != Token::FUTURE_STRICT_RESERVED_WORD && !Token::IsKeyword(next)) { this->ReportUnexpectedToken(next); *ok = false; @@ -1765,6 +1771,7 @@ ParserBase::ParsePrimaryExpression(bool* ok) { case Token::IDENTIFIER: case Token::LET: + case Token::STATIC: case Token::YIELD: case Token::FUTURE_STRICT_RESERVED_WORD: { // Using eval or arguments in this context is OK even in strict mode. diff --git a/src/scanner.cc b/src/scanner.cc index dc15e37e9a..8062faa244 100644 --- a/src/scanner.cc +++ b/src/scanner.cc @@ -1006,11 +1006,15 @@ static Token::Value KeywordOrIdentifierToken(const uint8_t* input, bool Scanner::IdentifierIsFutureStrictReserved( const AstRawString* string) const { // Keywords are always 1-byte strings. - return string->is_one_byte() && - Token::FUTURE_STRICT_RESERVED_WORD == - KeywordOrIdentifierToken(string->raw_data(), string->length(), - harmony_scoping_, harmony_modules_, - harmony_classes_); + if (!string->is_one_byte()) return false; + if (string->IsOneByteEqualTo("let") || string->IsOneByteEqualTo("static") || + string->IsOneByteEqualTo("yield")) { + return true; + } + return Token::FUTURE_STRICT_RESERVED_WORD == + KeywordOrIdentifierToken(string->raw_data(), string->length(), + harmony_scoping_, harmony_modules_, + harmony_classes_); } diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index b9adb17bb9..1909b3e66b 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -1814,6 +1814,33 @@ TEST(NoErrorsEvalAndArgumentsStrict) { } +#define FUTURE_STRICT_RESERVED_WORDS(V) \ + V(implements) \ + V(interface) \ + V(let) \ + V(package) \ + V(private) \ + V(protected) \ + V(public) \ + V(static) \ + V(yield) + + +#define FUTURE_STRICT_RESERVED_STATEMENTS(NAME) \ + "var " #NAME ";", \ + "var foo, " #NAME ";", \ + "try { } catch (" #NAME ") { }", \ + "function " #NAME "() { }", \ + "(function " #NAME "() { })", \ + "function foo(" #NAME ") { }", \ + "function foo(bar, " #NAME ") { }", \ + #NAME " = 1;", \ + #NAME " += 1;", \ + "var foo = " #NAME " = 1;", \ + "++" #NAME ";", \ + #NAME " ++;", + + 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, @@ -1826,24 +1853,19 @@ TEST(ErrorsFutureStrictReservedWords) { { NULL, NULL } }; - 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++;", - "var yield = 13;", + const char* statement_data[] { + FUTURE_STRICT_RESERVED_WORDS(FUTURE_STRICT_RESERVED_STATEMENTS) NULL }; static const ParserFlag always_flags[] = {kAllowArrowFunctions}; RunParserSyncTest(context_data, statement_data, kError, NULL, 0, always_flags, arraysize(always_flags)); + + static const ParserFlag classes_flags[] = { + kAllowArrowFunctions, kAllowClasses, kAllowHarmonyScoping}; + RunParserSyncTest(context_data, statement_data, kError, NULL, 0, + classes_flags, arraysize(classes_flags)); } @@ -1856,23 +1878,18 @@ TEST(NoErrorsFutureStrictReservedWords) { }; 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++;", - "var yield = 13;", + FUTURE_STRICT_RESERVED_WORDS(FUTURE_STRICT_RESERVED_STATEMENTS) NULL }; static const ParserFlag always_flags[] = {kAllowArrowFunctions}; RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0, always_flags, arraysize(always_flags)); + + static const ParserFlag classes_flags[] = { + kAllowArrowFunctions, kAllowClasses, kAllowHarmonyScoping}; + RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0, + classes_flags, arraysize(classes_flags)); } @@ -2252,12 +2269,13 @@ TEST(ErrorsIllegalWordsAsLabelsStrict) { { NULL, NULL } }; +#define LABELLED_WHILE(NAME) #NAME ": while (true) { break " #NAME "; }", const char* statement_data[] = { "super: while(true) { break super; }", - "interface: while(true) { break interface; }", - "yield: while(true) { break yield; }", + FUTURE_STRICT_RESERVED_WORDS(LABELLED_WHILE) NULL }; +#undef LABELLED_WHILE RunParserSyncTest(context_data, statement_data, kError); } @@ -2288,6 +2306,27 @@ TEST(NoErrorsIllegalWordsAsLabels) { } +TEST(NoErrorsFutureStrictReservedAsLabelsSloppy) { + const char* context_data[][2] = { + { "", ""}, + { "function test_func() {", "}" }, + { "() => {", "}" }, + { NULL, NULL } + }; + +#define LABELLED_WHILE(NAME) #NAME ": while (true) { break " #NAME "; }", + const char* statement_data[] { + FUTURE_STRICT_RESERVED_WORDS(LABELLED_WHILE) + NULL + }; +#undef LABELLED_WHILE + + static const ParserFlag always_flags[] = {kAllowArrowFunctions}; + RunParserSyncTest(context_data, statement_data, kSuccess, NULL, 0, + always_flags, arraysize(always_flags)); +} + + TEST(ErrorsParenthesizedLabels) { // Parenthesized identifiers shouldn't be recognized as labels. const char* context_data[][2] = {