perf(parser): optimize lexer checkpoint to avoid unnecessary error

cloning (#13537)

1pct perf improvement on some pf the parser benchmarks

![Screenshot 2025-09-04 at
13.52.11.png](https://app.graphite.dev/user-attachments/assets/86e6aa27-6da3-48d1-97cc-fb3119c68885.png)
This commit is contained in:
Cameron authored and GitHub committed 2025-09-15 20:29:20 +08:00
1 parent a8675f408f
commit c794002134
3 files changed
+46 -5

No files matched your search

+11 -1
View File
@@ -265,6 +265,16 @@ impl<'a> ParserImpl<'a> {
}
}
pub(crate) fn checkpoint_with_error_recovery(&mut self) -> ParserCheckpoint<'a> {
ParserCheckpoint {
lexer: self.lexer.checkpoint_with_error_recovery(),
cur_token: self.token,
prev_span_end: self.prev_token_end,
errors_pos: self.errors.len(),
fatal_error: self.fatal_error.take(),
}
}
pub(crate) fn rewind(&mut self, checkpoint: ParserCheckpoint<'a>) {
let ParserCheckpoint { lexer, cur_token, prev_span_end, errors_pos, fatal_error } =
checkpoint;
@@ -280,7 +290,7 @@ impl<'a> ParserImpl<'a> {
&mut self,
func: impl FnOnce(&mut ParserImpl<'a>) -> T,
) -> Option<T> {
let checkpoint = self.checkpoint();
let checkpoint = self.checkpoint_with_error_recovery();
let ctx = self.ctx;
let node = func(self);
if self.fatal_error.is_none() {
+1 -1
View File
@@ -342,7 +342,7 @@ impl<'a> ParserImpl<'a> {
return None;
}
let checkpoint = self.checkpoint();
let checkpoint = self.checkpoint_with_error_recovery();
let head = self.parse_parenthesized_arrow_function_head();
if self.has_fatal_error() {
+34 -3
View File
@@ -44,7 +44,14 @@ use trivia_builder::TriviaBuilder;
pub struct LexerCheckpoint<'a> {
source_position: SourcePosition<'a>,
token: Token,
errors: Option<Vec<OxcDiagnostic>>,
errors_snapshot: ErrorSnapshot,
}
#[derive(Debug, Clone)]
enum ErrorSnapshot {
Empty,
Count(usize),
Full(Vec<OxcDiagnostic>),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@@ -137,16 +144,40 @@ impl<'a> Lexer<'a> {
/// Creates a checkpoint storing the current lexer state.
/// Use `rewind` to restore the lexer to the state stored in the checkpoint.
pub fn checkpoint(&self) -> LexerCheckpoint<'a> {
let errors_snapshot = if self.errors.is_empty() {
ErrorSnapshot::Empty
} else {
ErrorSnapshot::Count(self.errors.len())
};
LexerCheckpoint {
source_position: self.source.position(),
token: self.token,
errors: { if self.errors.is_empty() { None } else { Some(self.errors.clone()) } },
errors_snapshot,
}
}
/// Create a checkpoint that can handle error popping.
/// This is more expensive as it clones the errors vector.
pub(crate) fn checkpoint_with_error_recovery(&self) -> LexerCheckpoint<'a> {
let errors_snapshot = if self.errors.is_empty() {
ErrorSnapshot::Empty
} else {
ErrorSnapshot::Full(self.errors.clone())
};
LexerCheckpoint {
source_position: self.source.position(),
token: self.token,
errors_snapshot,
}
}
/// Rewinds the lexer to the same state as when the passed in `checkpoint` was created.
pub fn rewind(&mut self, checkpoint: LexerCheckpoint<'a>) {
self.errors = checkpoint.errors.unwrap_or_default();
match checkpoint.errors_snapshot {
ErrorSnapshot::Empty => self.errors.clear(),
ErrorSnapshot::Count(len) => self.errors.truncate(len),
ErrorSnapshot::Full(errors) => self.errors = errors,
}
self.source.set_position(checkpoint.source_position);
self.token = checkpoint.token;
}