LCOV - code coverage report
Current view: top level - src/parsing - scanner-inl.h (source / functions) Hit Total Coverage
Test: app.info Lines: 77 78 98.7 %
Date: 2019-04-19 Functions: 1 1 100.0 %

          Line data    Source code
       1             : // Copyright 2018 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #ifndef V8_PARSING_SCANNER_INL_H_
       6             : #define V8_PARSING_SCANNER_INL_H_
       7             : 
       8             : #include "src/char-predicates-inl.h"
       9             : #include "src/parsing/keywords-gen.h"
      10             : #include "src/parsing/scanner.h"
      11             : 
      12             : namespace v8 {
      13             : namespace internal {
      14             : 
      15             : // ----------------------------------------------------------------------------
      16             : // Keyword Matcher
      17             : 
      18             : #define KEYWORDS(KEYWORD_GROUP, KEYWORD)                    \
      19             :   KEYWORD_GROUP('a')                                        \
      20             :   KEYWORD("async", Token::ASYNC)                            \
      21             :   KEYWORD("await", Token::AWAIT)                            \
      22             :   KEYWORD_GROUP('b')                                        \
      23             :   KEYWORD("break", Token::BREAK)                            \
      24             :   KEYWORD_GROUP('c')                                        \
      25             :   KEYWORD("case", Token::CASE)                              \
      26             :   KEYWORD("catch", Token::CATCH)                            \
      27             :   KEYWORD("class", Token::CLASS)                            \
      28             :   KEYWORD("const", Token::CONST)                            \
      29             :   KEYWORD("continue", Token::CONTINUE)                      \
      30             :   KEYWORD_GROUP('d')                                        \
      31             :   KEYWORD("debugger", Token::DEBUGGER)                      \
      32             :   KEYWORD("default", Token::DEFAULT)                        \
      33             :   KEYWORD("delete", Token::DELETE)                          \
      34             :   KEYWORD("do", Token::DO)                                  \
      35             :   KEYWORD_GROUP('e')                                        \
      36             :   KEYWORD("else", Token::ELSE)                              \
      37             :   KEYWORD("enum", Token::ENUM)                              \
      38             :   KEYWORD("export", Token::EXPORT)                          \
      39             :   KEYWORD("extends", Token::EXTENDS)                        \
      40             :   KEYWORD_GROUP('f')                                        \
      41             :   KEYWORD("false", Token::FALSE_LITERAL)                    \
      42             :   KEYWORD("finally", Token::FINALLY)                        \
      43             :   KEYWORD("for", Token::FOR)                                \
      44             :   KEYWORD("function", Token::FUNCTION)                      \
      45             :   KEYWORD_GROUP('g')                                        \
      46             :   KEYWORD("get", Token::GET)                                \
      47             :   KEYWORD_GROUP('i')                                        \
      48             :   KEYWORD("if", Token::IF)                                  \
      49             :   KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \
      50             :   KEYWORD("import", Token::IMPORT)                          \
      51             :   KEYWORD("in", Token::IN)                                  \
      52             :   KEYWORD("instanceof", Token::INSTANCEOF)                  \
      53             :   KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD)  \
      54             :   KEYWORD_GROUP('l')                                        \
      55             :   KEYWORD("let", Token::LET)                                \
      56             :   KEYWORD_GROUP('n')                                        \
      57             :   KEYWORD("new", Token::NEW)                                \
      58             :   KEYWORD("null", Token::NULL_LITERAL)                      \
      59             :   KEYWORD_GROUP('p')                                        \
      60             :   KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD)    \
      61             :   KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD)    \
      62             :   KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD)  \
      63             :   KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD)     \
      64             :   KEYWORD_GROUP('r')                                        \
      65             :   KEYWORD("return", Token::RETURN)                          \
      66             :   KEYWORD_GROUP('s')                                        \
      67             :   KEYWORD("set", Token::SET)                                \
      68             :   KEYWORD("static", Token::STATIC)                          \
      69             :   KEYWORD("super", Token::SUPER)                            \
      70             :   KEYWORD("switch", Token::SWITCH)                          \
      71             :   KEYWORD_GROUP('t')                                        \
      72             :   KEYWORD("this", Token::THIS)                              \
      73             :   KEYWORD("throw", Token::THROW)                            \
      74             :   KEYWORD("true", Token::TRUE_LITERAL)                      \
      75             :   KEYWORD("try", Token::TRY)                                \
      76             :   KEYWORD("typeof", Token::TYPEOF)                          \
      77             :   KEYWORD_GROUP('v')                                        \
      78             :   KEYWORD("var", Token::VAR)                                \
      79             :   KEYWORD("void", Token::VOID)                              \
      80             :   KEYWORD_GROUP('w')                                        \
      81             :   KEYWORD("while", Token::WHILE)                            \
      82             :   KEYWORD("with", Token::WITH)                              \
      83             :   KEYWORD_GROUP('y')                                        \
      84             :   KEYWORD("yield", Token::YIELD)
      85             : 
      86             : constexpr bool IsKeywordStart(char c) {
      87             : #define KEYWORD_GROUP_CHECK(ch) c == ch ||
      88             : #define KEYWORD_CHECK(keyword, token)
      89             :   return KEYWORDS(KEYWORD_GROUP_CHECK, KEYWORD_CHECK) /* || */ false;
      90             : #undef KEYWORD_GROUP_CHECK
      91             : #undef KEYWORD_CHECK
      92             : }
      93             : 
      94             : V8_INLINE Token::Value KeywordOrIdentifierToken(const uint8_t* input,
      95             :                                                 int input_length) {
      96             :   DCHECK_GE(input_length, 1);
      97             :   return PerfectKeywordHash::GetToken(reinterpret_cast<const char*>(input),
      98    78538397 :                                       input_length);
      99             : }
     100             : 
     101             : // Recursive constexpr template magic to check if a character is in a given
     102             : // string.
     103             : template <int N>
     104             : constexpr bool IsInString(const char (&s)[N], char c, size_t i = 0) {
     105             :   return i >= N ? false : s[i] == c ? true : IsInString(s, c, i + 1);
     106             : }
     107             : 
     108             : inline constexpr bool CanBeKeywordCharacter(char c) {
     109             :   return IsInString(
     110             : #define KEYWORD_GROUP_CASE(ch)  // Nothing
     111             : #define KEYWORD(keyword, token) keyword
     112             :       // Use C string literal concatenation ("a" "b" becomes "ab") to build one
     113             :       // giant string containing all the keywords.
     114             :       KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
     115             : #undef KEYWORD
     116             : #undef KEYWORD_GROUP_CASE
     117             :           ,
     118             :       c);
     119             : }
     120             : 
     121             : // Make sure tokens are stored as a single byte.
     122             : STATIC_ASSERT(sizeof(Token::Value) == 1);
     123             : 
     124             : // Get the shortest token that this character starts, the token may change
     125             : // depending on subsequent characters.
     126             : constexpr Token::Value GetOneCharToken(char c) {
     127             :   // clang-format off
     128             :   return
     129             :       c == '(' ? Token::LPAREN :
     130             :       c == ')' ? Token::RPAREN :
     131             :       c == '{' ? Token::LBRACE :
     132             :       c == '}' ? Token::RBRACE :
     133             :       c == '[' ? Token::LBRACK :
     134             :       c == ']' ? Token::RBRACK :
     135             :       c == '?' ? Token::CONDITIONAL :
     136             :       c == ':' ? Token::COLON :
     137             :       c == ';' ? Token::SEMICOLON :
     138             :       c == ',' ? Token::COMMA :
     139             :       c == '.' ? Token::PERIOD :
     140             :       c == '|' ? Token::BIT_OR :
     141             :       c == '&' ? Token::BIT_AND :
     142             :       c == '^' ? Token::BIT_XOR :
     143             :       c == '~' ? Token::BIT_NOT :
     144             :       c == '!' ? Token::NOT :
     145             :       c == '<' ? Token::LT :
     146             :       c == '>' ? Token::GT :
     147             :       c == '%' ? Token::MOD :
     148             :       c == '=' ? Token::ASSIGN :
     149             :       c == '+' ? Token::ADD :
     150             :       c == '-' ? Token::SUB :
     151             :       c == '*' ? Token::MUL :
     152             :       c == '/' ? Token::DIV :
     153             :       c == '#' ? Token::PRIVATE_NAME :
     154             :       c == '"' ? Token::STRING :
     155             :       c == '\'' ? Token::STRING :
     156             :       c == '`' ? Token::TEMPLATE_SPAN :
     157             :       c == '\\' ? Token::IDENTIFIER :
     158             :       // Whitespace or line terminator
     159             :       c == ' ' ? Token::WHITESPACE :
     160             :       c == '\t' ? Token::WHITESPACE :
     161             :       c == '\v' ? Token::WHITESPACE :
     162             :       c == '\f' ? Token::WHITESPACE :
     163             :       c == '\r' ? Token::WHITESPACE :
     164             :       c == '\n' ? Token::WHITESPACE :
     165             :       // IsDecimalDigit must be tested before IsAsciiIdentifier
     166             :       IsDecimalDigit(c) ? Token::NUMBER :
     167             :       IsAsciiIdentifier(c) ? Token::IDENTIFIER :
     168             :       Token::ILLEGAL;
     169             :   // clang-format on
     170             : }
     171             : 
     172             : // Table of one-character tokens, by character (0x00..0x7F only).
     173             : static const constexpr Token::Value one_char_tokens[128] = {
     174             : #define CALL_GET_SCAN_FLAGS(N) GetOneCharToken(N),
     175             :     INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
     176             : #undef CALL_GET_SCAN_FLAGS
     177             : };
     178             : 
     179             : #undef KEYWORDS
     180             : 
     181             : V8_INLINE Token::Value Scanner::ScanIdentifierOrKeyword() {
     182             :   next().literal_chars.Start();
     183             :   return ScanIdentifierOrKeywordInner();
     184             : }
     185             : 
     186             : // Character flags for the fast path of scanning a keyword or identifier token.
     187             : enum class ScanFlags : uint8_t {
     188             :   kTerminatesLiteral = 1 << 0,
     189             :   // "Cannot" rather than "can" so that this flag can be ORed together across
     190             :   // multiple characters.
     191             :   kCannotBeKeyword = 1 << 1,
     192             :   kCannotBeKeywordStart = 1 << 2,
     193             :   kStringTerminator = 1 << 3,
     194             :   kIdentifierNeedsSlowPath = 1 << 4,
     195             :   kMultilineCommentCharacterNeedsSlowPath = 1 << 5,
     196             : };
     197             : constexpr uint8_t GetScanFlags(char c) {
     198             :   return
     199             :       // Keywords are all lowercase and only contain letters.
     200             :       // Note that non-identifier characters do not set this flag, so
     201             :       // that it plays well with kTerminatesLiteral.
     202             :       (IsAsciiIdentifier(c) && !CanBeKeywordCharacter(c)
     203             :            ? static_cast<uint8_t>(ScanFlags::kCannotBeKeyword)
     204             :            : 0) |
     205             :       (IsKeywordStart(c)
     206             :            ? 0
     207             :            : static_cast<uint8_t>(ScanFlags::kCannotBeKeywordStart)) |
     208             :       // Anything that isn't an identifier character will terminate the
     209             :       // literal, or at least terminates the literal fast path processing
     210             :       // (like an escape).
     211             :       (!IsAsciiIdentifier(c)
     212             :            ? static_cast<uint8_t>(ScanFlags::kTerminatesLiteral)
     213             :            : 0) |
     214             :       // Possible string termination characters.
     215             :       ((c == '\'' || c == '"' || c == '\n' || c == '\r' || c == '\\')
     216             :            ? static_cast<uint8_t>(ScanFlags::kStringTerminator)
     217             :            : 0) |
     218             :       // Escapes are processed on the slow path.
     219             :       (c == '\\' ? static_cast<uint8_t>(ScanFlags::kIdentifierNeedsSlowPath)
     220             :                  : 0) |
     221             :       // Newlines and * are interesting characters for multiline comment
     222             :       // scanning.
     223             :       (c == '\n' || c == '\r' || c == '*'
     224             :            ? static_cast<uint8_t>(
     225             :                  ScanFlags::kMultilineCommentCharacterNeedsSlowPath)
     226             :            : 0);
     227             : }
     228             : inline bool TerminatesLiteral(uint8_t scan_flags) {
     229             :   return (scan_flags & static_cast<uint8_t>(ScanFlags::kTerminatesLiteral));
     230             : }
     231             : inline bool CanBeKeyword(uint8_t scan_flags) {
     232       13488 :   return !(scan_flags & static_cast<uint8_t>(ScanFlags::kCannotBeKeyword));
     233             : }
     234             : inline bool IdentifierNeedsSlowPath(uint8_t scan_flags) {
     235             :   return (scan_flags &
     236             :           static_cast<uint8_t>(ScanFlags::kIdentifierNeedsSlowPath));
     237             : }
     238             : inline bool MultilineCommentCharacterNeedsSlowPath(uint8_t scan_flags) {
     239             :   return (scan_flags & static_cast<uint8_t>(
     240       66627 :                            ScanFlags::kMultilineCommentCharacterNeedsSlowPath));
     241             : }
     242             : inline bool MayTerminateString(uint8_t scan_flags) {
     243             :   return (scan_flags & static_cast<uint8_t>(ScanFlags::kStringTerminator));
     244             : }
     245             : // Table of precomputed scan flags for the 128 ASCII characters, for branchless
     246             : // flag calculation during the scan.
     247             : static constexpr const uint8_t character_scan_flags[128] = {
     248             : #define CALL_GET_SCAN_FLAGS(N) GetScanFlags(N),
     249             :     INT_0_TO_127_LIST(CALL_GET_SCAN_FLAGS)
     250             : #undef CALL_GET_SCAN_FLAGS
     251             : };
     252             : 
     253             : inline bool CharCanBeKeyword(uc32 c) {
     254      112376 :   return static_cast<uint32_t>(c) < arraysize(character_scan_flags) &&
     255       55863 :          CanBeKeyword(character_scan_flags[c]);
     256             : }
     257             : 
     258             : V8_INLINE Token::Value Scanner::ScanIdentifierOrKeywordInner() {
     259             :   DCHECK(IsIdentifierStart(c0_));
     260             :   bool escaped = false;
     261             :   bool can_be_keyword = true;
     262             : 
     263             :   STATIC_ASSERT(arraysize(character_scan_flags) == kMaxAscii + 1);
     264   133970429 :   if (V8_LIKELY(static_cast<uint32_t>(c0_) <= kMaxAscii)) {
     265   133968427 :     if (V8_LIKELY(c0_ != '\\')) {
     266   133943814 :       uint8_t scan_flags = character_scan_flags[c0_];
     267             :       DCHECK(!TerminatesLiteral(scan_flags));
     268             :       STATIC_ASSERT(static_cast<uint8_t>(ScanFlags::kCannotBeKeywordStart) ==
     269             :                     static_cast<uint8_t>(ScanFlags::kCannotBeKeyword) << 1);
     270   133943814 :       scan_flags >>= 1;
     271             :       // Make sure the shifting above doesn't set IdentifierNeedsSlowPath.
     272             :       // Otherwise we'll fall into the slow path after scanning the identifier.
     273             :       DCHECK(!IdentifierNeedsSlowPath(scan_flags));
     274             :       AddLiteralChar(static_cast<char>(c0_));
     275  2285251750 :       AdvanceUntil([this, &scan_flags](uc32 c0) {
     276   806331330 :         if (V8_UNLIKELY(static_cast<uint32_t>(c0) > kMaxAscii)) {
     277             :           // A non-ascii character means we need to drop through to the slow
     278             :           // path.
     279             :           // TODO(leszeks): This would be most efficient as a goto to the slow
     280             :           // path, check codegen and maybe use a bool instead.
     281             :           scan_flags |=
     282         347 :               static_cast<uint8_t>(ScanFlags::kIdentifierNeedsSlowPath);
     283         347 :           return true;
     284             :         }
     285   806330983 :         uint8_t char_flags = character_scan_flags[c0];
     286   806330983 :         scan_flags |= char_flags;
     287   806330983 :         if (TerminatesLiteral(char_flags)) {
     288             :           return true;
     289             :         } else {
     290             :           AddLiteralChar(static_cast<char>(c0));
     291   672341183 :           return false;
     292             :         }
     293             :       });
     294             : 
     295   133952087 :       if (V8_LIKELY(!IdentifierNeedsSlowPath(scan_flags))) {
     296   133938599 :         if (!CanBeKeyword(scan_flags)) return Token::IDENTIFIER;
     297             :         // Could be a keyword or identifier.
     298             :         Vector<const uint8_t> chars = next().literal_chars.one_byte_literal();
     299             :         return KeywordOrIdentifierToken(chars.start(), chars.length());
     300             :       }
     301             : 
     302             :       can_be_keyword = CanBeKeyword(scan_flags);
     303             :     } else {
     304             :       // Special case for escapes at the start of an identifier.
     305             :       escaped = true;
     306       24613 :       uc32 c = ScanIdentifierUnicodeEscape();
     307             :       DCHECK(!IsIdentifierStart(-1));
     308       49226 :       if (c == '\\' || !IsIdentifierStart(c)) {
     309             :         return Token::ILLEGAL;
     310             :       }
     311             :       AddLiteralChar(c);
     312             :       can_be_keyword = CharCanBeKeyword(c);
     313             :     }
     314             :   }
     315             : 
     316       18328 :   return ScanIdentifierOrKeywordInnerSlow(escaped, can_be_keyword);
     317             : }
     318             : 
     319             : V8_INLINE Token::Value Scanner::SkipWhiteSpace() {
     320             :   int start_position = source_pos();
     321             : 
     322             :   // We won't skip behind the end of input.
     323             :   DCHECK(!IsWhiteSpaceOrLineTerminator(kEndOfInput));
     324             : 
     325             :   // Advance as long as character is a WhiteSpace or LineTerminator.
     326  1067621328 :   while (IsWhiteSpaceOrLineTerminator(c0_)) {
     327   536187797 :     if (!next().after_line_terminator && unibrow::IsLineTerminator(c0_)) {
     328    38129513 :       next().after_line_terminator = true;
     329             :     }
     330             :     Advance();
     331             :   }
     332             : 
     333             :   // Return whether or not we skipped any characters.
     334   173689712 :   if (source_pos() == start_position) {
     335             :     DCHECK_NE('0', c0_);
     336             :     return Token::ILLEGAL;
     337             :   }
     338             : 
     339             :   return Token::WHITESPACE;
     340             : }
     341             : 
     342             : V8_INLINE Token::Value Scanner::ScanSingleToken() {
     343             :   Token::Value token;
     344             :   do {
     345   587310671 :     next().location.beg_pos = source_pos();
     346             : 
     347   587310671 :     if (V8_LIKELY(static_cast<unsigned>(c0_) <= kMaxAscii)) {
     348   582431800 :       token = one_char_tokens[c0_];
     349             : 
     350   582431800 :       switch (token) {
     351             :         case Token::LPAREN:
     352             :         case Token::RPAREN:
     353             :         case Token::LBRACE:
     354             :         case Token::RBRACE:
     355             :         case Token::LBRACK:
     356             :         case Token::RBRACK:
     357             :         case Token::CONDITIONAL:
     358             :         case Token::COLON:
     359             :         case Token::SEMICOLON:
     360             :         case Token::COMMA:
     361             :         case Token::BIT_NOT:
     362             :         case Token::ILLEGAL:
     363             :           // One character tokens.
     364             :           return Select(token);
     365             : 
     366             :         case Token::STRING:
     367    10482766 :           return ScanString();
     368             : 
     369             :         case Token::LT:
     370             :           // < <= << <<= <!--
     371             :           Advance();
     372     1063261 :           if (c0_ == '=') return Select(Token::LTE);
     373      979739 :           if (c0_ == '<') return Select('=', Token::ASSIGN_SHL, Token::SHL);
     374      537398 :           if (c0_ == '!') {
     375          73 :             token = ScanHtmlComment();
     376             :             continue;
     377             :           }
     378             :           return Token::LT;
     379             : 
     380             :         case Token::GT:
     381             :           // > >= >> >>= >>> >>>=
     382             :           Advance();
     383      537813 :           if (c0_ == '=') return Select(Token::GTE);
     384      505469 :           if (c0_ == '>') {
     385             :             // >> >>= >>> >>>=
     386             :             Advance();
     387      260905 :             if (c0_ == '=') return Select(Token::ASSIGN_SAR);
     388      255772 :             if (c0_ == '>') return Select('=', Token::ASSIGN_SHR, Token::SHR);
     389             :             return Token::SAR;
     390             :           }
     391             :           return Token::GT;
     392             : 
     393             :         case Token::ASSIGN:
     394             :           // = == === =>
     395             :           Advance();
     396    25659484 :           if (c0_ == '=') return Select('=', Token::EQ_STRICT, Token::EQ);
     397    23982082 :           if (c0_ == '>') return Select(Token::ARROW);
     398             :           return Token::ASSIGN;
     399             : 
     400             :         case Token::NOT:
     401             :           // ! != !==
     402             :           Advance();
     403     2389459 :           if (c0_ == '=') return Select('=', Token::NE_STRICT, Token::NE);
     404             :           return Token::NOT;
     405             : 
     406             :         case Token::ADD:
     407             :           // + ++ +=
     408             :           Advance();
     409     4799728 :           if (c0_ == '+') return Select(Token::INC);
     410     3830922 :           if (c0_ == '=') return Select(Token::ASSIGN_ADD);
     411             :           return Token::ADD;
     412             : 
     413             :         case Token::SUB:
     414             :           // - -- --> -=
     415             :           Advance();
     416      821209 :           if (c0_ == '-') {
     417             :             Advance();
     418      103534 :             if (c0_ == '>' && next().after_line_terminator) {
     419             :               // For compatibility with SpiderMonkey, we skip lines that
     420             :               // start with an HTML comment end '-->'.
     421         154 :               token = SkipSingleHTMLComment();
     422             :               continue;
     423             :             }
     424             :             return Token::DEC;
     425             :           }
     426      717675 :           if (c0_ == '=') return Select(Token::ASSIGN_SUB);
     427             :           return Token::SUB;
     428             : 
     429             :         case Token::MUL:
     430             :           // * *=
     431             :           Advance();
     432      520609 :           if (c0_ == '*') return Select('=', Token::ASSIGN_EXP, Token::EXP);
     433      511225 :           if (c0_ == '=') return Select(Token::ASSIGN_MUL);
     434             :           return Token::MUL;
     435             : 
     436             :         case Token::MOD:
     437             :           // % %=
     438      167266 :           return Select('=', Token::ASSIGN_MOD, Token::MOD);
     439             : 
     440             :         case Token::DIV:
     441             :           // /  // /* /=
     442             :           Advance();
     443     7728970 :           if (c0_ == '/') {
     444             :             uc32 c = Peek();
     445     7270882 :             if (c == '#' || c == '@') {
     446             :               Advance();
     447             :               Advance();
     448        4029 :               token = SkipSourceURLComment();
     449             :               continue;
     450             :             }
     451     7266853 :             token = SkipSingleLineComment();
     452             :             continue;
     453             :           }
     454      457751 :           if (c0_ == '*') {
     455       58598 :             token = SkipMultiLineComment();
     456             :             continue;
     457             :           }
     458      399153 :           if (c0_ == '=') return Select(Token::ASSIGN_DIV);
     459             :           return Token::DIV;
     460             : 
     461             :         case Token::BIT_AND:
     462             :           // & && &=
     463             :           Advance();
     464     1268662 :           if (c0_ == '&') return Select(Token::AND);
     465      662213 :           if (c0_ == '=') return Select(Token::ASSIGN_BIT_AND);
     466             :           return Token::BIT_AND;
     467             : 
     468             :         case Token::BIT_OR:
     469             :           // | || |=
     470             :           Advance();
     471      878480 :           if (c0_ == '|') return Select(Token::OR);
     472      387693 :           if (c0_ == '=') return Select(Token::ASSIGN_BIT_OR);
     473             :           return Token::BIT_OR;
     474             : 
     475             :         case Token::BIT_XOR:
     476             :           // ^ ^=
     477       18420 :           return Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
     478             : 
     479             :         case Token::PERIOD:
     480             :           // . Number
     481             :           Advance();
     482    15083423 :           if (IsDecimalDigit(c0_)) return ScanNumber(true);
     483    15080356 :           if (c0_ == '.') {
     484      106445 :             if (Peek() == '.') {
     485             :               Advance();
     486             :               Advance();
     487             :               return Token::ELLIPSIS;
     488             :             }
     489             :           }
     490             :           return Token::PERIOD;
     491             : 
     492             :         case Token::TEMPLATE_SPAN:
     493             :           Advance();
     494       77088 :           return ScanTemplateSpan();
     495             : 
     496             :         case Token::PRIVATE_NAME:
     497      288776 :           return ScanPrivateName();
     498             : 
     499             :         case Token::WHITESPACE:
     500             :           token = SkipWhiteSpace();
     501             :           continue;
     502             : 
     503             :         case Token::NUMBER:
     504    38361262 :           return ScanNumber(false);
     505             : 
     506             :         case Token::IDENTIFIER:
     507             :           return ScanIdentifierOrKeyword();
     508             : 
     509             :         default:
     510           0 :           UNREACHABLE();
     511             :       }
     512             :     }
     513             : 
     514    14636543 :     if (IsIdentifierStart(c0_) ||
     515     4878945 :         (CombineSurrogatePair() && IsIdentifierStart(c0_))) {
     516             :       return ScanIdentifierOrKeyword();
     517             :     }
     518     4878761 :     if (c0_ == kEndOfInput) {
     519     9755512 :       return source_->has_parser_error() ? Token::ILLEGAL : Token::EOS;
     520             :     }
     521             :     token = SkipWhiteSpace();
     522             : 
     523             :     // Continue scanning for tokens as long as we're just skipping whitespace.
     524   181181924 :   } while (token == Token::WHITESPACE);
     525             : 
     526             :   return token;
     527             : }
     528             : 
     529             : void Scanner::Scan(TokenDesc* next_desc) {
     530             :   DCHECK_EQ(next_desc, &next());
     531             : 
     532   406115421 :   next_desc->token = ScanSingleToken();
     533             :   DCHECK_IMPLIES(has_parser_error(), next_desc->token == Token::ILLEGAL);
     534   406115421 :   next_desc->location.end_pos = source_pos();
     535             : 
     536             : #ifdef DEBUG
     537             :   SanityCheckTokenDesc(current());
     538             :   SanityCheckTokenDesc(next());
     539             :   SanityCheckTokenDesc(next_next());
     540             : #endif
     541             : }
     542             : 
     543     5142799 : void Scanner::Scan() { Scan(next_); }
     544             : 
     545             : }  // namespace internal
     546             : }  // namespace v8
     547             : 
     548             : #endif  // V8_PARSING_SCANNER_INL_H_

Generated by: LCOV version 1.10