Skip to content

Commit 4fc0868

Browse files
authored
refactor(parser): reduce unnecessary backtracking in hot paths (#12708)
1 parent 7660a88 commit 4fc0868

File tree

3 files changed

+29
-27
lines changed

3 files changed

+29
-27
lines changed

crates/oxc_parser/src/js/arrow.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -192,20 +192,19 @@ impl<'a> ParserImpl<'a> {
192192
}
193193

194194
fn is_un_parenthesized_async_arrow_function_worker(&mut self) -> bool {
195-
let checkpoint = self.checkpoint();
196-
self.bump(Kind::Async);
197-
// If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function
198-
// but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher"
199-
if !self.cur_token().is_on_new_line() && self.cur_kind().is_binding_identifier() {
200-
// Arrow before newline is checked in `parse_simple_arrow_function_expression`
201-
self.bump_any();
202-
if self.at(Kind::Arrow) {
203-
self.rewind(checkpoint);
204-
return true;
195+
// Use lookahead to avoid checkpoint/rewind
196+
self.lookahead(|parser| {
197+
parser.bump(Kind::Async);
198+
// If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function
199+
// but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher"
200+
if !parser.cur_token().is_on_new_line() && parser.cur_kind().is_binding_identifier() {
201+
// Arrow before newline is checked in `parse_simple_arrow_function_expression`
202+
parser.bump_any();
203+
parser.at(Kind::Arrow)
204+
} else {
205+
false
205206
}
206-
}
207-
self.rewind(checkpoint);
208-
false
207+
})
209208
}
210209

211210
pub(crate) fn parse_simple_arrow_function_expression(

crates/oxc_parser/src/js/expression.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -731,24 +731,26 @@ impl<'a> ParserImpl<'a> {
731731
let mut question_dot = false;
732732
let is_property_access = if allow_optional_chain && self.at(Kind::QuestionDot) {
733733
// ?.
734-
let checkpoint = self.checkpoint();
735-
self.bump_any();
736-
let kind = self.cur_kind();
737-
let is_identifier_or_keyword = kind.is_identifier_or_keyword();
738-
if kind == Kind::LBrack
739-
|| is_identifier_or_keyword
740-
|| kind.is_template_start_of_tagged_template()
734+
// Fast check to avoid checkpoint/rewind in common cases
735+
let next_kind = self.lexer.peek_token().kind();
736+
if next_kind == Kind::LBrack
737+
|| next_kind.is_identifier_or_keyword()
738+
|| next_kind.is_template_start_of_tagged_template()
741739
{
740+
// This is likely a valid optional chain, proceed with normal parsing
741+
self.bump_any(); // consume ?.
742+
let kind = self.cur_kind();
743+
let is_identifier_or_keyword = kind.is_identifier_or_keyword();
742744
// ?.[
743745
// ?.something
744746
// ?.template`...`
745747
*in_optional_chain = true;
746748
question_dot = true;
747749
is_identifier_or_keyword
748750
} else {
751+
// This is not a valid optional chain pattern, don't consume ?.
749752
// Should be a cold branch here, as most real-world optional chaining will look like
750753
// `?.something` or `?.[expr]`
751-
self.rewind(checkpoint);
752754
false
753755
}
754756
} else {

crates/oxc_parser/src/ts/types.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -430,14 +430,15 @@ impl<'a> ParserImpl<'a> {
430430
Kind::LParen => self.parse_parenthesized_type(),
431431
Kind::Import => TSType::TSImportType(self.parse_ts_import_type()),
432432
Kind::Asserts => {
433-
let checkpoint = self.checkpoint();
434-
let asserts_start_span = self.start_span();
435-
self.bump_any(); // bump `asserts`
436-
437-
if self.is_token_identifier_or_keyword_on_same_line() {
433+
// Use lookahead to check if this is an asserts type predicate
434+
if self.lookahead(|parser| {
435+
parser.bump(Kind::Asserts);
436+
parser.is_token_identifier_or_keyword_on_same_line()
437+
}) {
438+
let asserts_start_span = self.start_span();
439+
self.bump_any(); // bump `asserts`
438440
self.parse_asserts_type_predicate(asserts_start_span)
439441
} else {
440-
self.rewind(checkpoint);
441442
self.parse_type_reference()
442443
}
443444
}

0 commit comments

Comments
 (0)