v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
scanner-inl.h
Go to the documentation of this file.
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
9// Include the non-inl header before the rest of the headers.
10
13#include "src/utils/utils.h"
14
15namespace v8 {
16namespace internal {
17
18// ----------------------------------------------------------------------------
19// Keyword Matcher
20
21#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
22 KEYWORD_GROUP('a') \
23 KEYWORD("async", Token::kAsync) \
24 KEYWORD("await", Token::kAwait) \
25 KEYWORD_GROUP('b') \
26 KEYWORD("break", Token::kBreak) \
27 KEYWORD_GROUP('c') \
28 KEYWORD("case", Token::kCase) \
29 KEYWORD("catch", Token::kCatch) \
30 KEYWORD("class", Token::kClass) \
31 KEYWORD("const", Token::kConst) \
32 KEYWORD("continue", Token::kContinue) \
33 KEYWORD_GROUP('d') \
34 KEYWORD("debugger", Token::kDebugger) \
35 KEYWORD("default", Token::kDefault) \
36 KEYWORD("delete", Token::kDelete) \
37 KEYWORD("do", Token::kDo) \
38 KEYWORD_GROUP('e') \
39 KEYWORD("else", Token::kElse) \
40 KEYWORD("enum", Token::kEnum) \
41 KEYWORD("export", Token::kExport) \
42 KEYWORD("extends", Token::kExtends) \
43 KEYWORD_GROUP('f') \
44 KEYWORD("false", Token::kFalseLiteral) \
45 KEYWORD("finally", Token::kFinally) \
46 KEYWORD("for", Token::kFor) \
47 KEYWORD("function", Token::kFunction) \
48 KEYWORD_GROUP('g') \
49 KEYWORD("get", Token::kGet) \
50 KEYWORD_GROUP('i') \
51 KEYWORD("if", Token::kIf) \
52 KEYWORD("implements", Token::kFutureStrictReservedWord) \
53 KEYWORD("import", Token::kImport) \
54 KEYWORD("in", Token::kIn) \
55 KEYWORD("instanceof", Token::kInstanceOf) \
56 KEYWORD("interface", Token::kFutureStrictReservedWord) \
57 KEYWORD_GROUP('l') \
58 KEYWORD("let", Token::kLet) \
59 KEYWORD_GROUP('n') \
60 KEYWORD("new", Token::kNew) \
61 KEYWORD("null", Token::kNullLiteral) \
62 KEYWORD_GROUP('o') \
63 KEYWORD("of", Token::kOf) \
64 KEYWORD_GROUP('p') \
65 KEYWORD("package", Token::kFutureStrictReservedWord) \
66 KEYWORD("private", Token::kFutureStrictReservedWord) \
67 KEYWORD("protected", Token::kFutureStrictReservedWord) \
68 KEYWORD("public", Token::kFutureStrictReservedWord) \
69 KEYWORD_GROUP('r') \
70 KEYWORD("return", Token::kReturn) \
71 KEYWORD_GROUP('s') \
72 KEYWORD("set", Token::kSet) \
73 KEYWORD("static", Token::kStatic) \
74 KEYWORD("super", Token::kSuper) \
75 KEYWORD("switch", Token::kSwitch) \
76 KEYWORD_GROUP('t') \
77 KEYWORD("this", Token::kThis) \
78 KEYWORD("throw", Token::kThrow) \
79 KEYWORD("true", Token::kTrueLiteral) \
80 KEYWORD("try", Token::kTry) \
81 KEYWORD("typeof", Token::kTypeOf) \
82 KEYWORD_GROUP('u') \
83 KEYWORD("using", Token::kUsing) \
84 KEYWORD_GROUP('v') \
85 KEYWORD("var", Token::kVar) \
86 KEYWORD("void", Token::kVoid) \
87 KEYWORD_GROUP('w') \
88 KEYWORD("while", Token::kWhile) \
89 KEYWORD("with", Token::kWith) \
90 KEYWORD_GROUP('y') \
91 KEYWORD("yield", Token::kYield)
92
93constexpr bool IsKeywordStart(char c) {
94#define KEYWORD_GROUP_CHECK(ch) c == ch ||
95#define KEYWORD_CHECK(keyword, token)
96 return KEYWORDS(KEYWORD_GROUP_CHECK, KEYWORD_CHECK) /* || */ false;
97#undef KEYWORD_GROUP_CHECK
98#undef KEYWORD_CHECK
99}
100
102 int input_length) {
103 DCHECK_GE(input_length, 1);
104 return PerfectKeywordHash::GetToken(reinterpret_cast<const char*>(input),
105 input_length);
106}
107
108// Recursive constexpr template magic to check if a character is in a given
109// string.
110template <int N>
111constexpr bool IsInString(const char (&s)[N], char c, size_t i = 0) {
112 return i >= N ? false : s[i] == c ? true : IsInString(s, c, i + 1);
113}
114
115inline constexpr bool CanBeKeywordCharacter(char c) {
116 return IsInString(
117#define KEYWORD_GROUP_CASE(ch) // Nothing
118#define KEYWORD(keyword, token) keyword
119 // Use C string literal concatenation ("a" "b" becomes "ab") to build one
120 // giant string containing all the keywords.
122#undef KEYWORD
124 ,
125 c);
126}
127
128// Make sure tokens are stored as a single byte.
129static_assert(sizeof(Token::Value) == 1);
130
131// Get the shortest token that this character starts, the token may change
132// depending on subsequent characters.
133constexpr Token::Value GetOneCharToken(char c) {
134 // clang-format off
135 return
136 c == '(' ? Token::kLeftParen :
137 c == ')' ? Token::kRightParen :
138 c == '{' ? Token::kLeftBrace :
139 c == '}' ? Token::kRightBrace :
140 c == '[' ? Token::kLeftBracket :
141 c == ']' ? Token::kRightBracket :
142 c == '?' ? Token::kConditional :
143 c == ':' ? Token::kColon :
144 c == ';' ? Token::kSemicolon :
145 c == ',' ? Token::kComma :
146 c == '.' ? Token::kPeriod :
147 c == '|' ? Token::kBitOr :
148 c == '&' ? Token::kBitAnd :
149 c == '^' ? Token::kBitXor :
150 c == '~' ? Token::kBitNot :
151 c == '!' ? Token::kNot :
152 c == '<' ? Token::kLessThan :
153 c == '>' ? Token::kGreaterThan :
154 c == '%' ? Token::kMod :
155 c == '=' ? Token::kAssign :
156 c == '+' ? Token::kAdd :
157 c == '-' ? Token::kSub :
158 c == '*' ? Token::kMul :
159 c == '/' ? Token::kDiv :
160 c == '#' ? Token::kPrivateName :
161 c == '"' ? Token::kString :
162 c == '\'' ? Token::kString :
163 c == '`' ? Token::kTemplateSpan :
164 c == '\\' ? Token::kIdentifier :
165 // Whitespace or line terminator
166 c == ' ' ? Token::kWhitespace :
167 c == '\t' ? Token::kWhitespace :
168 c == '\v' ? Token::kWhitespace :
169 c == '\f' ? Token::kWhitespace :
170 c == '\r' ? Token::kWhitespace :
171 c == '\n' ? Token::kWhitespace :
172 // IsDecimalDigit must be tested before IsAsciiIdentifier
173 IsDecimalDigit(c) ? Token::kNumber :
174 IsAsciiIdentifier(c) ? Token::kIdentifier :
175 Token::kIllegal;
176 // clang-format on
177}
178
179// Table of one-character tokens, by character (0x00..0x7F only).
180static const constexpr Token::Value one_char_tokens[128] = {
181#define CALL_GET_SCAN_FLAGS(N) GetOneCharToken(N),
183#undef CALL_GET_SCAN_FLAGS
184};
185
186#undef KEYWORDS
187
192
193// Character flags for the fast path of scanning a keyword or identifier token.
194enum class ScanFlags : uint8_t {
195 kTerminatesLiteral = 1 << 0,
196 // "Cannot" rather than "can" so that this flag can be ORed together across
197 // multiple characters.
198 kCannotBeKeyword = 1 << 1,
199 kCannotBeKeywordStart = 1 << 2,
200 kStringTerminator = 1 << 3,
203};
204constexpr uint8_t GetScanFlags(char c) {
205 return
206 // Keywords are all lowercase and only contain letters.
207 // Note that non-identifier characters do not set this flag, so
208 // that it plays well with kTerminatesLiteral.
210 ? static_cast<uint8_t>(ScanFlags::kCannotBeKeyword)
211 : 0) |
213 ? 0
214 : static_cast<uint8_t>(ScanFlags::kCannotBeKeywordStart)) |
215 // Anything that isn't an identifier character will terminate the
216 // literal, or at least terminates the literal fast path processing
217 // (like an escape).
219 ? static_cast<uint8_t>(ScanFlags::kTerminatesLiteral)
220 : 0) |
221 // Possible string termination characters.
222 ((c == '\'' || c == '"' || c == '\n' || c == '\r' || c == '\\')
223 ? static_cast<uint8_t>(ScanFlags::kStringTerminator)
224 : 0) |
225 // Escapes are processed on the slow path.
226 (c == '\\' ? static_cast<uint8_t>(ScanFlags::kIdentifierNeedsSlowPath)
227 : 0) |
228 // Newlines and * are interesting characters for multiline comment
229 // scanning.
230 (c == '\n' || c == '\r' || c == '*'
231 ? static_cast<uint8_t>(
233 : 0);
234}
235inline bool TerminatesLiteral(uint8_t scan_flags) {
236 return (scan_flags & static_cast<uint8_t>(ScanFlags::kTerminatesLiteral));
237}
238inline bool CanBeKeyword(uint8_t scan_flags) {
239 return !(scan_flags & static_cast<uint8_t>(ScanFlags::kCannotBeKeyword));
240}
241inline bool IdentifierNeedsSlowPath(uint8_t scan_flags) {
242 return (scan_flags &
243 static_cast<uint8_t>(ScanFlags::kIdentifierNeedsSlowPath));
244}
245inline bool MultilineCommentCharacterNeedsSlowPath(uint8_t scan_flags) {
246 return (scan_flags & static_cast<uint8_t>(
248}
249inline bool MayTerminateString(uint8_t scan_flags) {
250 return (scan_flags & static_cast<uint8_t>(ScanFlags::kStringTerminator));
251}
252// Table of precomputed scan flags for the 128 ASCII characters, for branchless
253// flag calculation during the scan.
254static constexpr const uint8_t character_scan_flags[128] = {
255#define CALL_GET_SCAN_FLAGS(N) GetScanFlags(N),
257#undef CALL_GET_SCAN_FLAGS
258};
259
261 return static_cast<uint32_t>(c) < arraysize(character_scan_flags) &&
263}
264
267 bool escaped = false;
268 bool can_be_keyword = true;
269
270 static_assert(arraysize(character_scan_flags) == kMaxAscii + 1);
271 if (V8_LIKELY(static_cast<uint32_t>(c0_) <= kMaxAscii)) {
272 if (V8_LIKELY(c0_ != '\\')) {
273 uint8_t scan_flags = character_scan_flags[c0_];
274 DCHECK(!TerminatesLiteral(scan_flags));
275 static_assert(static_cast<uint8_t>(ScanFlags::kCannotBeKeywordStart) ==
276 static_cast<uint8_t>(ScanFlags::kCannotBeKeyword) << 1);
277 scan_flags >>= 1;
278 // Make sure the shifting above doesn't set IdentifierNeedsSlowPath.
279 // Otherwise we'll fall into the slow path after scanning the identifier.
280 DCHECK(!IdentifierNeedsSlowPath(scan_flags));
281 AddLiteralChar(static_cast<char>(c0_));
282 AdvanceUntil([this, &scan_flags](base::uc32 c0) {
283 if (V8_UNLIKELY(static_cast<uint32_t>(c0) > kMaxAscii)) {
284 // A non-ascii character means we need to drop through to the slow
285 // path.
286 // TODO(leszeks): This would be most efficient as a goto to the slow
287 // path, check codegen and maybe use a bool instead.
288 scan_flags |=
289 static_cast<uint8_t>(ScanFlags::kIdentifierNeedsSlowPath);
290 return true;
291 }
292 uint8_t char_flags = character_scan_flags[c0];
293 scan_flags |= char_flags;
294 if (TerminatesLiteral(char_flags)) {
295 return true;
296 } else {
297 AddLiteralChar(static_cast<char>(c0));
298 return false;
299 }
300 });
301
302 if (V8_LIKELY(!IdentifierNeedsSlowPath(scan_flags))) {
303 if (!CanBeKeyword(scan_flags)) return Token::kIdentifier;
304 // Could be a keyword or identifier.
307 return KeywordOrIdentifierToken(chars.begin(), chars.length());
308 }
309
310 can_be_keyword = CanBeKeyword(scan_flags);
311 } else {
312 // Special case for escapes at the start of an identifier.
313 escaped = true;
316 if (c == '\\' || !IsIdentifierStart(c)) {
317 return Token::kIllegal;
318 }
320 can_be_keyword = CharCanBeKeyword(c);
321 }
322 }
323
324 return ScanIdentifierOrKeywordInnerSlow(escaped, can_be_keyword);
325}
326
328 if (!IsWhiteSpaceOrLineTerminator(c0_)) return Token::kIllegal;
329
330 if (!next().after_line_terminator && unibrow::IsLineTerminator(c0_)) {
332 }
333
334 // Advance as long as character is a WhiteSpace or LineTerminator.
335 base::uc32 hint = ' ';
336 AdvanceUntil([this, &hint](base::uc32 c0) {
337 if (V8_LIKELY(c0 == hint)) return false;
339 if (!next().after_line_terminator && unibrow::IsLineTerminator(c0)) {
341 }
342 hint = c0;
343 return false;
344 }
345 return true;
346 });
347
348 return Token::kWhitespace;
349}
350
352 bool old_saw_non_comment = saw_non_comment_;
353 // Assume the token we'll parse is not a comment; if it is, saw_non_comment_
354 // is restored.
355 saw_non_comment_ = true;
356 Token::Value token;
357 do {
359
360 if (V8_LIKELY(static_cast<unsigned>(c0_) <= kMaxAscii)) {
361 token = one_char_tokens[c0_];
362
363 switch (token) {
364 case Token::kLeftParen:
365 case Token::kRightParen:
366 case Token::kLeftBrace:
367 case Token::kRightBrace:
368 case Token::kLeftBracket:
369 case Token::kRightBracket:
370 case Token::kColon:
371 case Token::kSemicolon:
372 case Token::kComma:
373 case Token::kBitNot:
374 case Token::kIllegal:
375 // One character tokens.
376 return Select(token);
377
378 case Token::kConditional:
379 // ? ?. ?? ??=
380 Advance();
381 if (c0_ == '.') {
382 Advance();
383 if (!IsDecimalDigit(c0_)) return Token::kQuestionPeriod;
384 PushBack('.');
385 } else if (c0_ == '?') {
386 return Select('=', Token::kAssignNullish, Token::kNullish);
387 }
388 return Token::kConditional;
389
390 case Token::kString:
391 return ScanString();
392
393 case Token::kLessThan:
394 // < <= << <<= <!--
395 Advance();
396 if (c0_ == '=') return Select(Token::kLessThanEq);
397 if (c0_ == '<') return Select('=', Token::kAssignShl, Token::kShl);
398 if (c0_ == '!') {
399 token = ScanHtmlComment();
400 continue;
401 }
402 return Token::kLessThan;
403
404 case Token::kGreaterThan:
405 // > >= >> >>= >>> >>>=
406 Advance();
407 if (c0_ == '=') return Select(Token::kGreaterThanEq);
408 if (c0_ == '>') {
409 // >> >>= >>> >>>=
410 Advance();
411 if (c0_ == '=') return Select(Token::kAssignSar);
412 if (c0_ == '>') return Select('=', Token::kAssignShr, Token::kShr);
413 return Token::kSar;
414 }
415 return Token::kGreaterThan;
416
417 case Token::kAssign:
418 // = == === =>
419 Advance();
420 if (c0_ == '=') return Select('=', Token::kEqStrict, Token::kEq);
421 if (c0_ == '>') return Select(Token::kArrow);
422 return Token::kAssign;
423
424 case Token::kNot:
425 // ! != !==
426 Advance();
427 if (c0_ == '=')
428 return Select('=', Token::kNotEqStrict, Token::kNotEq);
429 return Token::kNot;
430
431 case Token::kAdd:
432 // + ++ +=
433 Advance();
434 if (c0_ == '+') return Select(Token::kInc);
435 if (c0_ == '=') return Select(Token::kAssignAdd);
436 return Token::kAdd;
437
438 case Token::kSub:
439 // - -- --> -=
440 Advance();
441 if (c0_ == '-') {
442 Advance();
443 if (c0_ == '>' && next().after_line_terminator) {
444 // For compatibility with SpiderMonkey, we skip lines that
445 // start with an HTML comment end '-->'.
446 token = SkipSingleHTMLComment();
447 continue;
448 }
449 return Token::kDec;
450 }
451 if (c0_ == '=') return Select(Token::kAssignSub);
452 return Token::kSub;
453
454 case Token::kMul:
455 // * *=
456 Advance();
457 if (c0_ == '*') return Select('=', Token::kAssignExp, Token::kExp);
458 if (c0_ == '=') return Select(Token::kAssignMul);
459 return Token::kMul;
460
461 case Token::kMod:
462 // % %=
463 return Select('=', Token::kAssignMod, Token::kMod);
464
465 case Token::kDiv:
466 // / // /* /=
467 Advance();
468 if (c0_ == '/') {
469 saw_non_comment_ = old_saw_non_comment;
470 base::uc32 c = Peek();
471 if (c == '#' || c == '@') {
472 Advance();
473 Advance();
474 token = SkipMagicComment(c);
475 continue;
476 }
477 token = SkipSingleLineComment();
478 continue;
479 }
480 if (c0_ == '*') {
481 saw_non_comment_ = old_saw_non_comment;
482 token = SkipMultiLineComment();
483 continue;
484 }
485 if (c0_ == '=') return Select(Token::kAssignDiv);
486 return Token::kDiv;
487
488 case Token::kBitAnd:
489 // & && &= &&=
490 Advance();
491 if (c0_ == '&') return Select('=', Token::kAssignAnd, Token::kAnd);
492 if (c0_ == '=') return Select(Token::kAssignBitAnd);
493 return Token::kBitAnd;
494
495 case Token::kBitOr:
496 // | || |= ||=
497 Advance();
498 if (c0_ == '|') return Select('=', Token::kAssignOr, Token::kOr);
499 if (c0_ == '=') return Select(Token::kAssignBitOr);
500 return Token::kBitOr;
501
502 case Token::kBitXor:
503 // ^ ^=
504 return Select('=', Token::kAssignBitXor, Token::kBitXor);
505
506 case Token::kPeriod:
507 // . Number
508 Advance();
509 if (IsDecimalDigit(c0_)) return ScanNumber(true);
510 if (c0_ == '.') {
511 if (Peek() == '.') {
512 Advance();
513 Advance();
514 return Token::kEllipsis;
515 }
516 }
517 return Token::kPeriod;
518
519 case Token::kTemplateSpan:
520 Advance();
521 return ScanTemplateSpan();
522
523 case Token::kPrivateName:
524 if (source_pos() == 0 && Peek() == '!') {
525 token = SkipSingleLineComment();
526 continue;
527 }
528 return ScanPrivateName();
529
530 case Token::kWhitespace:
531 token = SkipWhiteSpace();
532 continue;
533
534 case Token::kNumber:
535 return ScanNumber(false);
536
537 case Token::kIdentifier:
539
540 default:
541 UNREACHABLE();
542 }
543 }
544
545 if (IsIdentifierStart(c0_) ||
548 }
549 if (c0_ == kEndOfInput) {
550 return source_->has_parser_error() ? Token::kIllegal : Token::kEos;
551 }
552 token = SkipWhiteSpace();
553
554 // Continue scanning for tokens as long as we're just skipping whitespace.
555 } while (token == Token::kWhitespace);
556
557 return token;
558}
559
560void Scanner::Scan(TokenDesc* next_desc) {
561 DCHECK_EQ(next_desc, &next());
562
563 next_desc->token = ScanSingleToken();
564 DCHECK_IMPLIES(has_parser_error(), next_desc->token == Token::kIllegal);
565 next_desc->location.end_pos = source_pos();
566
567#ifdef DEBUG
568 SanityCheckTokenDesc(current());
569 SanityCheckTokenDesc(next());
570 SanityCheckTokenDesc(next_next());
571 SanityCheckTokenDesc(next_next_next());
572#endif
573}
574
576
577} // namespace internal
578} // namespace v8
579
580#endif // V8_PARSING_SCANNER_INL_H_
int length() const
Definition vector.h:64
constexpr T * begin() const
Definition vector.h:96
base::Vector< const uint8_t > one_byte_literal() const
static Token::Value GetToken(const char *str, int len)
V8_INLINE Token::Value ScanIdentifierOrKeywordInner()
V8_INLINE Token::Value ScanSingleToken()
Token::Value SkipMagicComment(base::uc32 hash_or_at_sign)
Definition scanner.cc:245
const TokenDesc & next_next() const
Definition scanner.h:742
void PushBack(base::uc32 ch)
Definition scanner.h:578
V8_INLINE void AddLiteralChar(base::uc32 c)
Definition scanner.h:535
Token::Value ScanIdentifierOrKeywordInnerSlow(bool escaped, bool can_be_keyword)
Definition scanner.cc:1001
bool CombineSurrogatePair()
Definition scanner.h:564
Token::Value ScanString()
Definition scanner.cc:550
Token::Value ScanTemplateSpan()
Definition scanner.cc:608
Token::Value ScanNumber(bool seen_period)
Definition scanner.cc:840
V8_INLINE bool has_parser_error() const
Definition scanner.h:258
Token::Value SkipMultiLineComment()
Definition scanner.cc:370
V8_INLINE void Scan()
Token::Value SkipSingleHTMLComment()
Definition scanner.cc:226
Utf16CharacterStream *const source_
Definition scanner.h:754
TokenDesc * next_
Definition scanner.h:748
V8_INLINE void AdvanceUntil(FunctionType check)
Definition scanner.h:560
V8_INLINE Token::Value ScanIdentifierOrKeyword()
TokenDesc & next()
Definition scanner.h:738
base::uc32 Peek() const
Definition scanner.h:585
base::uc32 ScanIdentifierUnicodeEscape()
Definition scanner.cc:973
static constexpr base::uc32 kEndOfInput
Definition scanner.h:277
const TokenDesc & next_next_next() const
Definition scanner.h:743
Token::Value SkipSingleLineComment()
Definition scanner.cc:234
static const int kMaxAscii
Definition scanner.h:499
V8_INLINE Token::Value SkipWhiteSpace()
static constexpr base::uc32 Invalid()
Definition scanner.h:280
Token::Value ScanHtmlComment()
Definition scanner.cc:415
Token::Value Select(Token::Value tok)
Definition scanner.h:587
Token::Value ScanPrivateName()
Definition scanner.cc:591
const TokenDesc & current() const
Definition scanner.h:740
V8_INLINE bool has_parser_error() const
Definition scanner.h:53
#define CALL_GET_SCAN_FLAGS(N)
V8_INLINE bool IsLineTerminator(uchar c)
Definition unicode.h:267
uint32_t uc32
Definition strings.h:19
bool IsIdentifierStart(base::uc32 c)
constexpr bool CanBeKeywordCharacter(char c)
constexpr bool IsKeywordStart(char c)
Definition scanner-inl.h:93
bool CanBeKeyword(uint8_t scan_flags)
constexpr Token::Value GetOneCharToken(char c)
constexpr bool IsAsciiIdentifier(base::uc32 c)
bool IsWhiteSpaceOrLineTerminator(base::uc32 c)
constexpr int N
static const constexpr Token::Value one_char_tokens[128]
constexpr bool IsInString(const char(&s)[N], char c, size_t i=0)
bool CharCanBeKeyword(base::uc32 c)
bool TerminatesLiteral(uint8_t scan_flags)
static constexpr const uint8_t character_scan_flags[128]
bool IdentifierNeedsSlowPath(uint8_t scan_flags)
Disallow flags or implications overriding each other abort_on_contradictory_flags true
Definition flags.cc:368
bool MayTerminateString(uint8_t scan_flags)
constexpr bool IsDecimalDigit(base::uc32 c)
V8_INLINE Token::Value KeywordOrIdentifierToken(const uint8_t *input, int input_length)
bool MultilineCommentCharacterNeedsSlowPath(uint8_t scan_flags)
constexpr uint8_t GetScanFlags(char c)
#define KEYWORD_GROUP_CHECK(ch)
#define KEYWORD_GROUP_CASE(ch)
#define KEYWORD_CHECK(keyword, token)
#define KEYWORD(keyword, token)
#define KEYWORDS(KEYWORD_GROUP, KEYWORD)
Definition scanner-inl.h:21
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define arraysize(array)
Definition macros.h:67
#define INT_0_TO_127_LIST(V)
Definition utils.h:625
#define V8_INLINE
Definition v8config.h:500
#define V8_LIKELY(condition)
Definition v8config.h:661
#define V8_UNLIKELY(condition)
Definition v8config.h:660