Skip to content

Commit ae9ebe9

Browse files
committed
perf(parser): cache cur_kind() to eliminate redundant calls (#14411)
## Summary Optimization to reduce redundant token kind extraction in hot paths by caching `cur_kind()` results before compound checks. ## Problem - Compound checks like `self.at(X) || self.at(Y)` call `cur_kind()` twice - Pattern `self.at(close) || self.has_fatal_error()` calls `cur_kind()` twice since `has_fatal_error()` internally calls it - Token kind extraction involves bit-packing operations: `((self.0 >> 64) & 0xFF) as u8` ## Solution Cache `cur_kind()` in a local variable before compound checks to eliminate redundant calls. ## Changes - **`cursor.rs:parse_delimited_list()`**: Hot loop parsing arrays, objects, parameters - reduced from 2-3 calls per iteration to 1 - **`arrow.rs:68`**: Cached kind for `LParen`/`LAngle` check - **`module.rs:101`**: Cached kind for `LCurly`/`Star` check - **`types.rs`**: Fixed 3 locations (lines 91, 558, 1188) with double `at()` calls ## Impact Every JavaScript array, object literal, function parameter list, and TypeScript type now extracts the token kind fewer times during parsing. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 2b6a3b4 commit ae9ebe9

File tree

4 files changed

+25
-12
lines changed

4 files changed

+25
-12
lines changed

crates/oxc_parser/src/cursor.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,16 +386,25 @@ impl<'a> ParserImpl<'a> {
386386
F: Fn(&mut Self) -> T,
387387
{
388388
let mut list = self.ast.vec();
389-
if self.at(close) || self.has_fatal_error() {
389+
// Cache cur_kind() to avoid redundant calls in compound checks
390+
let kind = self.cur_kind();
391+
if kind == close
392+
|| matches!(kind, Kind::Eof | Kind::Undetermined)
393+
|| self.fatal_error.is_some()
394+
{
390395
return (list, None);
391396
}
392397
list.push(f(self));
393398
loop {
394-
if self.at(close) || self.has_fatal_error() {
399+
let kind = self.cur_kind();
400+
if kind == close
401+
|| matches!(kind, Kind::Eof | Kind::Undetermined)
402+
|| self.fatal_error.is_some()
403+
{
395404
return (list, None);
396405
}
397406
self.expect(separator);
398-
if self.at(close) {
407+
if self.cur_kind() == close {
399408
let trailing_separator = self.prev_token_end - 1;
400409
return (list, Some(trailing_separator));
401410
}

crates/oxc_parser/src/js/arrow.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ impl<'a> ParserImpl<'a> {
6565
if self.cur_token().is_on_new_line() {
6666
return Tristate::False;
6767
}
68-
if !self.at(Kind::LParen) && !self.at(Kind::LAngle) {
68+
let kind = self.cur_kind();
69+
if kind != Kind::LParen && kind != Kind::LAngle {
6970
return Tristate::False;
7071
}
7172
}

crates/oxc_parser/src/js/module.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,13 @@ impl<'a> ParserImpl<'a> {
9898
self.error(diagnostics::escaped_keyword(token_after_import.span()));
9999
}
100100

101-
if self.at(Kind::LCurly) || self.at(Kind::Star) {
101+
let kind = self.cur_kind();
102+
if kind == Kind::LCurly || kind == Kind::Star {
102103
// `import type { ...`
103104
// `import type * ...`
104105
import_kind = ImportOrExportKind::Type;
105106
has_default_specifier = false;
106-
} else if self.cur_kind().is_binding_identifier() {
107+
} else if kind.is_binding_identifier() {
107108
// `import type something ...`
108109
let token = self.cur_token();
109110
let identifier_after_type = self.parse_binding_identifier();

crates/oxc_parser/src/ts/types.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,17 @@ impl<'a> ParserImpl<'a> {
8585
if self.at(Kind::LAngle) {
8686
return true;
8787
}
88-
if self.at(Kind::New) {
88+
let kind = self.cur_kind();
89+
if kind == Kind::New {
8990
return true;
9091
}
91-
if !self.at(Kind::LParen) && !self.at(Kind::Abstract) {
92+
if kind != Kind::LParen && kind != Kind::Abstract {
9293
return false;
9394
}
9495
let checkpoint = self.checkpoint();
95-
let first = self.cur_kind();
9696
self.bump_any();
9797

98-
match first {
98+
match kind {
9999
Kind::Abstract => {
100100
// `abstract new ...`
101101
if self.at(Kind::New) {
@@ -555,7 +555,8 @@ impl<'a> ParserImpl<'a> {
555555

556556
fn is_start_of_mapped_type(&mut self) -> bool {
557557
self.bump_any();
558-
if self.at(Kind::Plus) || self.at(Kind::Minus) {
558+
let kind = self.cur_kind();
559+
if kind == Kind::Plus || kind == Kind::Minus {
559560
self.bump_any();
560561
return self.at(Kind::Readonly);
561562
}
@@ -1184,7 +1185,8 @@ impl<'a> ParserImpl<'a> {
11841185
let (key, computed) = self.parse_property_name();
11851186
let optional = self.eat(Kind::Question);
11861187

1187-
if self.at(Kind::LParen) || self.at(Kind::LAngle) {
1188+
let kind = self.cur_kind();
1189+
if kind == Kind::LParen || kind == Kind::LAngle {
11881190
for modifier in modifiers.iter() {
11891191
if modifier.kind == ModifierKind::Readonly {
11901192
self.error(

0 commit comments

Comments
 (0)