From 7495fb438fd8e5a37949bcbb0d6405c830e2d97a Mon Sep 17 00:00:00 2001 From: Ulrich Stark Date: Sun, 15 Jun 2025 16:54:56 +0200 Subject: [PATCH] perf(parser): speed up simple lookaheads by introducing Lexer::peek_token --- crates/oxc_parser/src/js/class.rs | 9 ++------- crates/oxc_parser/src/js/function.rs | 9 ++++----- crates/oxc_parser/src/js/module.rs | 18 +++--------------- crates/oxc_parser/src/js/statement.rs | 13 +++++-------- crates/oxc_parser/src/lexer/mod.rs | 7 +++++++ crates/oxc_parser/src/modifiers.rs | 7 +------ crates/oxc_parser/src/ts/statement.rs | 9 +++------ crates/oxc_parser/src/ts/types.rs | 16 ++++------------ 8 files changed, 29 insertions(+), 59 deletions(-) diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index 8df4e618844f2..23a8c0c9ac345 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -222,7 +222,7 @@ impl<'a> ParserImpl<'a> { ); // static { block } - if self.at(Kind::Static) && self.lookahead(Self::next_token_is_open_brace) { + if self.at(Kind::Static) && self.lexer.peek_token().kind() == Kind::LCurly { for decorator in decorators { self.error(diagnostics::decorators_are_not_valid_here(decorator.span)); } @@ -439,12 +439,7 @@ impl<'a> ParserImpl<'a> { let ident = self.parse_identifier_name(); return Some(PropertyKey::StaticIdentifier(self.alloc(ident))); } - if self.at(Kind::Str) - && self.lookahead(|p| { - p.bump_any(); - p.at(Kind::LParen) - }) - { + if self.at(Kind::Str) && self.lexer.peek_token().kind() == Kind::LParen { return self.try_parse(|p| { let string_literal = p.parse_literal_string(); if string_literal.value != "constructor" { diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index a679bb05ce845..a8e728cc96c0e 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -22,11 +22,10 @@ impl FunctionKind { impl<'a> ParserImpl<'a> { pub(crate) fn at_function_with_async(&mut self) -> bool { self.at(Kind::Function) - || self.at(Kind::Async) - && self.lookahead(|p| { - p.bump_any(); - p.at(Kind::Function) && !p.token.is_on_new_line() - }) + || self.at(Kind::Async) && { + let token = self.lexer.peek_token(); + token.kind() == Kind::Function && !token.is_on_new_line() + } } pub(crate) fn parse_function_body(&mut self) -> Box<'a, FunctionBody<'a>> { diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index f72b9e0d0dbfb..3628c980dfbd4 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -389,13 +389,7 @@ impl<'a> ParserImpl<'a> { Kind::Eq if self.is_ts => ModuleDeclaration::TSExportAssignment( self.parse_ts_export_assignment_declaration(span), ), - Kind::As - if self.is_ts - && self.lookahead(|p| { - p.bump_any(); - p.at(Kind::Namespace) - }) => - { + Kind::As if self.is_ts && self.lexer.peek_token().kind() == Kind::Namespace => { // `export as namespace ...` ModuleDeclaration::TSNamespaceExportDeclaration( self.parse_ts_export_namespace(span), @@ -411,10 +405,7 @@ impl<'a> ParserImpl<'a> { ModuleDeclaration::ExportNamedDeclaration(self.parse_export_named_specifiers(span)) } Kind::Type if self.is_ts => { - let checkpoint = self.checkpoint(); - self.bump_any(); - let next_kind = self.cur_kind(); - self.rewind(checkpoint); + let next_kind = self.lexer.peek_token().kind(); match next_kind { // `export type { ...` @@ -879,10 +870,7 @@ impl<'a> ParserImpl<'a> { return ImportOrExportKind::Value; } - let checkpoint = self.checkpoint(); - self.bump_any(); - let next_kind = self.cur_kind(); - self.rewind(checkpoint); + let next_kind = self.lexer.peek_token().kind(); if matches!(next_kind, Kind::LCurly | Kind::Star) { self.bump_any(); diff --git a/crates/oxc_parser/src/js/statement.rs b/crates/oxc_parser/src/js/statement.rs index ec1f0861a6b41..c8fdea9baea9c 100644 --- a/crates/oxc_parser/src/js/statement.rs +++ b/crates/oxc_parser/src/js/statement.rs @@ -353,14 +353,11 @@ impl<'a> ParserImpl<'a> { } // [+Using] using [no LineTerminator here] ForBinding[?Yield, ?Await, ~Pattern] - if self.at(Kind::Using) - && self.lookahead(|p| { - p.bump_any(); - !p.cur_token().is_on_new_line() - && !p.at(Kind::Of) - && p.cur_kind().is_binding_identifier() - }) - { + if self.at(Kind::Using) && { + let token = self.lexer.peek_token(); + let kind = token.kind(); + !token.is_on_new_line() && kind != Kind::Of && kind.is_binding_identifier() + } { return self.parse_using_declaration_for_statement(span, r#await); } diff --git a/crates/oxc_parser/src/lexer/mod.rs b/crates/oxc_parser/src/lexer/mod.rs index 5c31b1572c3b8..970f1d43e755a 100644 --- a/crates/oxc_parser/src/lexer/mod.rs +++ b/crates/oxc_parser/src/lexer/mod.rs @@ -154,6 +154,13 @@ impl<'a> Lexer<'a> { self.token = checkpoint.token; } + pub fn peek_token(&mut self) -> Token { + let checkpoint = self.checkpoint(); + let token = self.next_token(); + self.rewind(checkpoint); + token + } + /// Set context pub fn set_context(&mut self, context: LexerContext) { self.context = context; diff --git a/crates/oxc_parser/src/modifiers.rs b/crates/oxc_parser/src/modifiers.rs index f099dd61c1be1..f5ef617c42662 100644 --- a/crates/oxc_parser/src/modifiers.rs +++ b/crates/oxc_parser/src/modifiers.rs @@ -387,7 +387,7 @@ impl<'a> ParserImpl<'a> { // we're at the start of a static block (stop_on_start_of_class_static_block && matches!(self.cur_kind(), Kind::Static) - && self.lookahead(Self::next_token_is_open_brace)) + && self.lexer.peek_token().kind() == Kind::LCurly) // we may be at the start of a static block || (has_seen_static_modifier && matches!(self.cur_kind(), Kind::Static)) // next token is not a modifier @@ -398,11 +398,6 @@ impl<'a> ParserImpl<'a> { Some(self.modifier(kind, self.end_span(span))) } - pub(crate) fn next_token_is_open_brace(&mut self) -> bool { - self.bump_any(); - self.at(Kind::LCurly) - } - pub(crate) fn parse_contextual_modifier(&mut self, kind: Kind) -> bool { self.at(kind) && self.try_parse(Self::next_token_can_follow_modifier).is_some() } diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index c63c983883315..828c3870538e2 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -233,7 +233,9 @@ impl<'a> ParserImpl<'a> { return self.parse_signature_member(CallOrConstructorSignature::Call); } - if kind == Kind::New && self.lookahead(Self::is_next_token_open_paren_or_angle_bracket) { + if kind == Kind::New + && matches!(self.lexer.peek_token().kind(), Kind::LParen | Kind::LAngle) + { return self.parse_signature_member(CallOrConstructorSignature::Constructor); } @@ -259,11 +261,6 @@ impl<'a> ParserImpl<'a> { self.parse_property_or_method_signature(span, &modifiers) } - fn is_next_token_open_paren_or_angle_bracket(&mut self) -> bool { - self.bump_any(); - matches!(self.cur_kind(), Kind::LParen | Kind::LAngle) - } - pub(crate) fn is_index_signature(&mut self) -> bool { self.at(Kind::LBrack) && self.lookahead(Self::is_unambiguously_index_signature) } diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index d5c7c29205761..4813ead2ac97b 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -450,11 +450,6 @@ impl<'a> ParserImpl<'a> { self.cur_kind().is_identifier_name() && !self.cur_token().is_on_new_line() } - fn is_next_token_number(&mut self) -> bool { - self.bump_any(); - self.cur_kind().is_number() - } - fn parse_keyword_and_no_dot(&mut self) -> TSType<'a> { let span = self.start_span(); let ty = match self.cur_kind() { @@ -548,7 +543,7 @@ impl<'a> ParserImpl<'a> { | Kind::NoSubstitutionTemplate | Kind::TemplateHead => true, Kind::Function => !in_start_of_parameter, - Kind::Minus => !in_start_of_parameter && self.lookahead(Self::is_next_token_number), + Kind::Minus => !in_start_of_parameter && self.lexer.peek_token().kind().is_number(), Kind::LParen => { !in_start_of_parameter && self.lookahead(Self::is_start_of_parenthesized_or_function_type) @@ -1402,13 +1397,10 @@ impl<'a> ParserImpl<'a> { | Kind::New | Kind::Slash | Kind::SlashEq => true, - Kind::Import => self.lookahead(Self::is_next_token_paren_less_than_or_dot), + Kind::Import => { + matches!(self.lexer.peek_token().kind(), Kind::LParen | Kind::LAngle | Kind::Dot) + } _ => false, } } - - fn is_next_token_paren_less_than_or_dot(&mut self) -> bool { - self.bump_any(); - matches!(self.cur_kind(), Kind::LParen | Kind::LAngle | Kind::Dot) - } }