mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
perf(parser): optimize lexer checkpoint to avoid unnecessary error
cloning (#13537) 1pct perf improvement on some pf the parser benchmarks 
This commit is contained in:
1 parent
a8675f408f
commit
c794002134
3 files changed
+46
-5
No files matched your search
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user