[parser] Restructure identifier classifying

- Rely more heavily on Token::IsValidIdentifier.
- Deal with IsLet() when it's possibly a lexical declaration.
- Remove ENUM from the default IsAnyIdentifier range.
- Always pre-check whether IsAnyIdentifier before classifying identifiers.

Change-Id: I55eae6ff65dc306b466fa29d233c715e85bc3854
Reviewed-on: https://chromium-review.googlesource.com/c/1356514
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57977}
This commit is contained in:
Toon Verwaest 2018-11-30 15:26:42 +01:00 committed by Commit Bot
parent 8fd93c827d
commit 298aefa600
8 changed files with 145 additions and 87 deletions

View File

@ -1039,13 +1039,12 @@ class ParserBase {
// for the case of parsing the identifier in a function expression, where the
// relevant "function_kind" bit is of the function being parsed, not the
// containing function.
IdentifierT ParseIdentifierOrStrictReservedWord(FunctionKind function_kind,
bool* is_strict_reserved,
bool* is_await);
IdentifierT ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved,
bool* is_await) {
V8_INLINE IdentifierT ParseIdentifierOrStrictReservedWord(
FunctionKind function_kind, bool* is_strict_reserved);
V8_INLINE IdentifierT
ParseIdentifierOrStrictReservedWord(bool* is_strict_reserved) {
return ParseIdentifierOrStrictReservedWord(function_state_->kind(),
is_strict_reserved, is_await);
is_strict_reserved);
}
V8_INLINE IdentifierT ParseIdentifierName();
@ -1540,52 +1539,47 @@ template <typename Impl>
typename ParserBase<Impl>::IdentifierT
ParserBase<Impl>::ParseAndClassifyIdentifier() {
Token::Value next = Next();
STATIC_ASSERT(Token::IDENTIFIER + 1 == Token::ASYNC);
if (IsInRange(next, Token::IDENTIFIER, Token::ASYNC)) {
IdentifierT name = impl()->GetSymbol();
STATIC_ASSERT(Token::IDENTIFIER + 1 == Token::ASYNC);
if (V8_LIKELY(IsInRange(next, Token::IDENTIFIER, Token::ASYNC))) {
IdentifierT name = impl()->GetSymbol();
if (V8_UNLIKELY(impl()->IsArguments(name) &&
scope()->ShouldBanArguments())) {
ReportMessage(MessageTemplate::kArgumentsDisallowedInInitializer);
return impl()->EmptyIdentifierString();
}
return name;
} else if (next == Token::AWAIT && !parsing_module_ && !is_async_function()) {
classifier()->RecordAsyncArrowFormalParametersError(
scanner()->location(), MessageTemplate::kAwaitBindingIdentifier);
return impl()->GetSymbol();
} else if (is_sloppy(language_mode()) &&
(Token::IsStrictReservedWord(next) ||
(next == Token::YIELD && !is_generator()))) {
IdentifierT name = impl()->GetSymbol();
classifier()->RecordStrictModeFormalParameterError(
scanner()->location(), MessageTemplate::kUnexpectedStrictReserved);
if (impl()->IdentifierEquals(name, ast_value_factory()->let_string())) {
classifier()->RecordLetPatternError(
scanner()->location(), MessageTemplate::kLetInLexicalBinding);
}
return name;
} else {
if (!Token::IsValidIdentifier(next, language_mode(), this->is_generator(),
parsing_module_ || is_async_function())) {
ReportUnexpectedToken(next);
return impl()->EmptyIdentifierString();
}
if (next == Token::AWAIT) {
classifier()->RecordAsyncArrowFormalParametersError(
scanner()->location(), MessageTemplate::kAwaitBindingIdentifier);
return impl()->GetSymbol();
}
DCHECK(Token::IsStrictReservedWord(next));
classifier()->RecordStrictModeFormalParameterError(
scanner()->location(), MessageTemplate::kUnexpectedStrictReserved);
return impl()->GetSymbol();
}
template <class Impl>
typename ParserBase<Impl>::IdentifierT
ParserBase<Impl>::ParseIdentifierOrStrictReservedWord(
FunctionKind function_kind, bool* is_strict_reserved, bool* is_await) {
FunctionKind function_kind, bool* is_strict_reserved) {
Token::Value next = Next();
if (next == Token::IDENTIFIER || (next == Token::AWAIT && !parsing_module_ &&
!IsAsyncFunction(function_kind)) ||
next == Token::ASYNC) {
*is_strict_reserved = false;
*is_await = next == Token::AWAIT;
} else if (Token::IsStrictReservedWord(next) ||
(next == Token::YIELD && !IsGeneratorFunction(function_kind))) {
*is_strict_reserved = true;
} else {
*is_strict_reserved = Token::IsStrictReservedWord(next);
if (!Token::IsValidIdentifier(
next, language_mode(), IsGeneratorFunction(function_kind),
parsing_module_ || IsAsyncFunction(function_kind))) {
ReportUnexpectedToken(next);
return impl()->EmptyIdentifierString();
}
@ -1596,7 +1590,7 @@ ParserBase<Impl>::ParseIdentifierOrStrictReservedWord(
template <typename Impl>
typename ParserBase<Impl>::IdentifierT ParserBase<Impl>::ParseIdentifierName() {
Token::Value next = Next();
if (!Token::IsAnyIdentifier(next) && next != Token::ESCAPED_KEYWORD &&
if (!Token::IsAnyIdentifierOrEnum(next) && next != Token::ESCAPED_KEYWORD &&
!Token::IsKeyword(next)) {
ReportUnexpectedToken(next);
return impl()->EmptyIdentifierString();
@ -1709,8 +1703,7 @@ ParserBase<Impl>::ParsePrimaryExpression() {
int beg_pos = peek_position();
Token::Value token = peek();
if (IsInRange(token, Token::IDENTIFIER,
Token::ESCAPED_STRICT_RESERVED_WORD)) {
if (Token::IsAnyIdentifier(token)) {
// Using eval or arguments in this context is OK even in strict mode.
IdentifierT name = ParseAndClassifyIdentifier();
InferName infer = InferName::kYes;
@ -1743,7 +1736,6 @@ ParserBase<Impl>::ParsePrimaryExpression() {
}
return impl()->ExpressionFromIdentifier(name, beg_pos, infer);
}
DCHECK_IMPLIES(Token::IsAnyIdentifier(token), token == Token::ENUM);
if (Token::IsLiteral(token)) {
return impl()->ExpressionFromLiteral(Next(), beg_pos);
@ -1804,14 +1796,10 @@ ParserBase<Impl>::ParsePrimaryExpression() {
bool is_strict_reserved_name = false;
Scanner::Location class_name_location = Scanner::Location::invalid();
if (peek_any_identifier()) {
bool is_await = false;
name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved_name,
&is_await);
name = ParseAndClassifyIdentifier();
class_name_location = scanner()->location();
if (is_await) {
classifier()->RecordAsyncArrowFormalParametersError(
scanner()->location(), MessageTemplate::kAwaitBindingIdentifier);
}
is_strict_reserved_name =
Token::IsStrictReservedWord(scanner()->current_token());
}
return ParseClassLiteral(name, class_name_location,
is_strict_reserved_name, class_token_pos);
@ -2397,7 +2385,7 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ParsePropertyInfo* prop_info,
// IdentifierReference Initializer?
DCHECK_EQ(function_flags, ParseFunctionFlag::kIsNormal);
if (!Token::IsIdentifier(name_token, language_mode(),
if (!Token::IsValidIdentifier(name_token, language_mode(),
this->is_generator(),
parsing_module_ || is_async_function())) {
ReportUnexpectedToken(Next());
@ -3282,9 +3270,8 @@ ParserBase<Impl>::ParseFunctionExpression() {
scanner()->CurrentSymbol(ast_value_factory()) ==
ast_value_factory()->anonymous_string());
} else if (peek_any_identifier()) {
bool is_await = false;
name = ParseIdentifierOrStrictReservedWord(
function_kind, &is_strict_reserved_name, &is_await);
name = ParseIdentifierOrStrictReservedWord(function_kind,
&is_strict_reserved_name);
function_name_location = scanner()->location();
function_type = FunctionLiteral::kNamedExpression;
}
@ -3600,9 +3587,17 @@ typename ParserBase<Impl>::BlockT ParserBase<Impl>::ParseVariableDeclarations(
pattern = ParseBindingPattern();
if (IsLexicalVariableMode(parsing_result->descriptor.mode)) {
if (impl()->IsIdentifier(pattern)) {
if (impl()->IsLet(impl()->AsIdentifier(pattern))) {
impl()->ReportMessageAt(
Scanner::Location(bindings_start, end_position()),
MessageTemplate::kLetInLexicalBinding);
}
} else {
ValidateLetPattern();
}
}
}
Scanner::Location variable_loc = scanner()->location();
bool single_name = impl()->IsIdentifier(pattern);
@ -3746,8 +3741,7 @@ ParserBase<Impl>::ParseHoistableDeclaration(
name_validity = kSkipFunctionNameCheck;
} else {
bool is_strict_reserved = false;
bool is_await = false;
name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, &is_await);
name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved);
name_validity = is_strict_reserved ? kFunctionNameIsStrictReserved
: kFunctionNameValidityUnknown;
variable_name = name;
@ -3809,8 +3803,7 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseClassDeclaration(
if (default_export && (peek() == Token::EXTENDS || peek() == Token::LBRACE)) {
impl()->GetDefaultStrings(&name, &variable_name);
} else {
bool is_await = false;
name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved, &is_await);
name = ParseIdentifierOrStrictReservedWord(&is_strict_reserved);
variable_name = name;
}
@ -4305,12 +4298,7 @@ ParserBase<Impl>::ParseAsyncFunctionLiteral() {
ast_value_factory()->anonymous_string());
} else if (peek_any_identifier()) {
type = FunctionLiteral::kNamedExpression;
bool is_await = false;
name = ParseIdentifierOrStrictReservedWord(kind, &is_strict_reserved,
&is_await);
// If the function name is "await", ParseIdentifierOrStrictReservedWord
// recognized the error.
DCHECK(!is_await);
name = ParseIdentifierOrStrictReservedWord(kind, &is_strict_reserved);
}
FunctionLiteralT result = impl()->ParseFunctionLiteral(
name, scanner()->location(),
@ -4478,10 +4466,14 @@ void ParserBase<Impl>::CheckDestructuringElement(ExpressionT expression,
classifier()->RecordBindingPatternError(
Scanner::Location(begin, end),
MessageTemplate::kInvalidPropertyBindingPattern);
} else if (is_strict(language_mode()) && impl()->IsIdentifier(expression)) {
// Only classify if we are already in strict mode since the language mode
// cannot change in the presence of non-simple parameters.
ClassifyFormalParameter(impl()->AsIdentifier(expression), begin, end);
} else if (impl()->IsIdentifier(expression)) {
IdentifierT identifier = impl()->AsIdentifier(expression);
ClassifyFormalParameter(identifier, begin, end);
if (impl()->IsLet(identifier)) {
classifier()->RecordLetPatternError(
Scanner::Location(begin, end),
MessageTemplate::kLetInLexicalBinding);
}
}
return;
}

View File

@ -916,7 +916,7 @@ ZoneChunkList<Parser::ExportClauseData>* Parser::ParseExportClause(
// Keep track of the first reserved word encountered in case our
// caller needs to report an error.
if (!reserved_loc->IsValid() &&
!Token::IsIdentifier(name_tok, LanguageMode::kStrict, false,
!Token::IsValidIdentifier(name_tok, LanguageMode::kStrict, false,
parsing_module_)) {
*reserved_loc = scanner()->location();
}
@ -971,8 +971,9 @@ ZonePtrList<const Parser::NamedImport>* Parser::ParseNamedImports(int pos) {
if (CheckContextualKeyword(ast_value_factory()->as_string())) {
local_name = ParseIdentifierName();
}
if (!Token::IsIdentifier(scanner()->current_token(), LanguageMode::kStrict,
false, parsing_module_)) {
if (!Token::IsValidIdentifier(scanner()->current_token(),
LanguageMode::kStrict, false,
parsing_module_)) {
ReportMessage(MessageTemplate::kUnexpectedReserved);
return nullptr;
} else if (IsEvalOrArguments(local_name)) {

View File

@ -595,6 +595,10 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
return identifier == ast_value_factory()->arguments_string();
}
V8_INLINE bool IsLet(const AstRawString* identifier) const {
return identifier == ast_value_factory()->let_string();
}
V8_INLINE bool IsEvalOrArguments(const AstRawString* identifier) const {
return IsEval(identifier) || IsArguments(identifier);
}

View File

@ -28,6 +28,8 @@ PreParserIdentifier GetSymbolHelper(Scanner* scanner,
// - 'contextual' keywords (and may contain escaped; treated in 2nd switch.)
// - 'contextual' keywords, but may not be escaped (3rd switch).
switch (scanner->current_token()) {
case Token::LET:
return PreParserIdentifier::Let();
case Token::AWAIT:
return PreParserIdentifier::Await();
case Token::ASYNC:
@ -44,6 +46,9 @@ PreParserIdentifier GetSymbolHelper(Scanner* scanner,
return PreParserIdentifier::Name();
}
if (scanner->literal_contains_escapes()) {
if (string == avf->let_string()) {
return PreParserIdentifier::Let();
}
return PreParserIdentifier::Default();
}
if (string == avf->eval_string()) {

View File

@ -45,6 +45,9 @@ class PreParserIdentifier {
static PreParserIdentifier Await() {
return PreParserIdentifier(kAwaitIdentifier);
}
static PreParserIdentifier Let() {
return PreParserIdentifier(kLetIdentifier);
}
static PreParserIdentifier Async() {
return PreParserIdentifier(kAsyncIdentifier);
}
@ -64,6 +67,7 @@ class PreParserIdentifier {
}
bool IsConstructor() const { return type_ == kConstructorIdentifier; }
bool IsAwait() const { return type_ == kAwaitIdentifier; }
bool IsLet() const { return type_ == kLetIdentifier; }
bool IsName() const { return type_ == kNameIdentifier; }
bool IsPrivateName() const { return type_ == kPrivateNameIdentifier; }
@ -75,6 +79,7 @@ class PreParserIdentifier {
kArgumentsIdentifier,
kConstructorIdentifier,
kAwaitIdentifier,
kLetIdentifier,
kAsyncIdentifier,
kNameIdentifier,
kPrivateNameIdentifier
@ -1301,6 +1306,10 @@ class PreParser : public ParserBase<PreParser> {
return identifier.IsAwait();
}
V8_INLINE bool IsLet(const PreParserIdentifier& identifier) const {
return identifier.IsLet();
}
// Returns true if the expression is of type "this.foo".
V8_INLINE static bool IsThisProperty(const PreParserExpression& expression) {
return expression.IsThisProperty();

View File

@ -518,7 +518,8 @@ class Scanner {
token == Token::UNINITIALIZED || token == Token::REGEXP_LITERAL ||
token == Token::ESCAPED_KEYWORD ||
IsInRange(token, Token::NUMBER, Token::STRING) ||
(Token::IsAnyIdentifier(token) && !Token::IsKeyword(token)) ||
(Token::IsAnyIdentifierOrEnum(token) &&
!Token::IsKeyword(token)) ||
IsInRange(token, Token::TEMPLATE_SPAN, Token::TEMPLATE_TAIL);
}
bool CanAccessRawLiteral() const {

View File

@ -173,8 +173,10 @@ namespace internal {
T(IDENTIFIER, nullptr, 0) \
K(ASYNC, "async", 0) \
/* `await` is a reserved word in module code only */ \
/* BEGIN AwaitOrYield */ \
K(AWAIT, "await", 0) \
K(YIELD, "yield", 0) \
/* END AwaitOrYield */ \
K(LET, "let", 0) \
K(STATIC, "static", 0) \
/* Future reserved words (ECMA-262, section 7.6.1.2). */ \
@ -218,15 +220,14 @@ class Token {
// Predicates
static bool IsKeyword(Value token) { return token_type[token] == 'K'; }
static bool IsIdentifier(Value token, LanguageMode language_mode,
bool is_generator, bool disallow_await) {
if (IsInRange(token, IDENTIFIER, ASYNC)) return true;
if (IsInRange(token, LET, ESCAPED_STRICT_RESERVED_WORD)) {
return is_sloppy(language_mode);
}
V8_INLINE static bool IsValidIdentifier(Value token,
LanguageMode language_mode,
bool is_generator,
bool disallow_await) {
if (V8_LIKELY(IsInRange(token, IDENTIFIER, ASYNC))) return true;
if (token == AWAIT) return !disallow_await;
if (token == YIELD) return !is_generator && is_sloppy(language_mode);
return false;
return IsStrictReservedWord(token) && is_sloppy(language_mode);
}
static bool IsCallable(Value token) { return IsInRange(token, SUPER, ENUM); }
@ -236,11 +237,19 @@ class Token {
}
static bool IsAnyIdentifier(Value token) {
return IsInRange(token, IDENTIFIER, ESCAPED_STRICT_RESERVED_WORD);
}
static bool IsAnyIdentifierOrEnum(Value token) {
return IsInRange(token, IDENTIFIER, ENUM);
}
static bool IsAwaitOrYield(Value token) {
return IsInRange(token, AWAIT, YIELD);
}
static bool IsStrictReservedWord(Value token) {
return IsInRange(token, LET, ESCAPED_STRICT_RESERVED_WORD);
return IsInRange(token, YIELD, ESCAPED_STRICT_RESERVED_WORD);
}
static bool IsLiteral(Value token) {

View File

@ -98,7 +98,6 @@ bool TokenIsAnyIdentifier(Token::Value token) {
case Token::STATIC:
case Token::FUTURE_STRICT_RESERVED_WORD:
case Token::ESCAPED_STRICT_RESERVED_WORD:
case Token::ENUM:
return true;
default:
return false;
@ -112,6 +111,31 @@ TEST(AnyIdentifierToken) {
}
}
bool TokenIsAnyIdentifierOrEnum(Token::Value token) {
switch (token) {
case Token::IDENTIFIER:
case Token::ASYNC:
case Token::AWAIT:
case Token::YIELD:
case Token::LET:
case Token::STATIC:
case Token::FUTURE_STRICT_RESERVED_WORD:
case Token::ESCAPED_STRICT_RESERVED_WORD:
case Token::ENUM:
return true;
default:
return false;
}
}
TEST(AnyIdentifierOrEnumToken) {
for (int i = 0; i < Token::NUM_TOKENS; i++) {
Token::Value token = static_cast<Token::Value>(i);
CHECK_EQ(TokenIsAnyIdentifierOrEnum(token),
Token::IsAnyIdentifierOrEnum(token));
}
}
bool TokenIsCallable(Token::Value token) {
switch (token) {
case Token::SUPER:
@ -137,7 +161,7 @@ TEST(CallableToken) {
}
}
bool TokenIsIdentifier(Token::Value token, LanguageMode language_mode,
bool TokenIsValidIdentifier(Token::Value token, LanguageMode language_mode,
bool is_generator, bool disallow_await) {
switch (token) {
case Token::IDENTIFIER:
@ -158,7 +182,7 @@ bool TokenIsIdentifier(Token::Value token, LanguageMode language_mode,
UNREACHABLE();
}
TEST(IsIdentifierToken) {
TEST(IsValidIdentifierToken) {
for (int i = 0; i < Token::NUM_TOKENS; i++) {
Token::Value token = static_cast<Token::Value>(i);
for (size_t raw_language_mode = 0; raw_language_mode < LanguageModeSize;
@ -167,17 +191,30 @@ TEST(IsIdentifierToken) {
for (int is_generator = 0; is_generator < 2; is_generator++) {
for (int disallow_await = 0; disallow_await < 2; disallow_await++) {
CHECK_EQ(
TokenIsIdentifier(token, mode, is_generator, disallow_await),
Token::IsIdentifier(token, mode, is_generator, disallow_await));
TokenIsValidIdentifier(token, mode, is_generator, disallow_await),
Token::IsValidIdentifier(token, mode, is_generator,
disallow_await));
}
}
}
}
}
bool TokenIsAwaitOrYield(Token::Value token) {
return token == Token::AWAIT || token == Token::YIELD;
}
TEST(IsAwaitOrYield) {
for (int i = 0; i < Token::NUM_TOKENS; i++) {
Token::Value token = static_cast<Token::Value>(i);
CHECK_EQ(TokenIsAwaitOrYield(token), Token::IsAwaitOrYield(token));
}
}
bool TokenIsStrictReservedWord(Token::Value token) {
switch (token) {
case Token::LET:
case Token::YIELD:
case Token::STATIC:
case Token::FUTURE_STRICT_RESERVED_WORD:
case Token::ESCAPED_STRICT_RESERVED_WORD: