perf(minifier): use IdentHashSet in PrivateMemberUsageStack (#26331)

Use `IdentHashSet` in `PrivateMemberUsageStack`. This reduces the `Ident` -> `Str` conversion and also makes the `Set::contains` operation faster as the hashing doesn't happen.

I used AI to generate the code and then reviewed the code.
This commit is contained in:
sapphi-red committed 2026-09-06 07:27:08 +00:00
1 parent d19c42ac87
commit 51366fb9ff
3 files changed
+17 -21

No files matched your search

+2 -2
View File
@@ -792,7 +792,7 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
if ctx.is_tree_shake_only() {
return;
}
ctx.state.private_member_usage.record_use(node.field.name.into());
ctx.state.private_member_usage.record_use(node.field.name);
}
fn exit_private_in_expression(
@@ -803,6 +803,6 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
if ctx.is_tree_shake_only() {
return;
}
ctx.state.private_member_usage.record_use(node.left.name.into());
ctx.state.private_member_usage.record_use(node.left.name);
}
}
@@ -1,5 +1,6 @@
use oxc_ast::ast::*;
use oxc_ecmascript::side_effects::MayHaveSideEffects;
use oxc_str::Ident;
use crate::TraverseCtx;
@@ -21,8 +22,7 @@ impl<'a> PeepholeOptimizations {
let PropertyKey::PrivateIdentifier(private_id) = &prop.key else {
return true;
};
let name: Str = private_id.name.into();
if ctx.state.private_member_usage.is_used(&name) {
if ctx.state.private_member_usage.is_used(&private_id.name) {
return true;
}
prop.value.as_ref().is_some_and(|value| value.may_have_side_effects(ctx))
@@ -31,15 +31,13 @@ impl<'a> PeepholeOptimizations {
let PropertyKey::PrivateIdentifier(private_id) = &method.key else {
return true;
};
let name: Str = private_id.name.into();
ctx.state.private_member_usage.is_used(&name)
ctx.state.private_member_usage.is_used(&private_id.name)
}
ClassElement::AccessorProperty(accessor) => {
let PropertyKey::PrivateIdentifier(private_id) = &accessor.key else {
return true;
};
let name: Str = private_id.name.into();
if ctx.state.private_member_usage.is_used(&name) {
if ctx.state.private_member_usage.is_used(&private_id.name) {
return true;
}
accessor.value.as_ref().is_some_and(|value| value.may_have_side_effects(ctx))
@@ -62,25 +60,25 @@ impl<'a> PeepholeOptimizations {
});
}
pub fn declared_private_member_names(body: &ClassBody<'a>) -> impl Iterator<Item = Str<'a>> {
pub fn declared_private_member_names(body: &ClassBody<'a>) -> impl Iterator<Item = Ident<'a>> {
body.body.iter().filter_map(|element| match element {
ClassElement::PropertyDefinition(prop) => {
let PropertyKey::PrivateIdentifier(private_id) = &prop.key else {
return None;
};
Some(private_id.name.into())
Some(private_id.name)
}
ClassElement::MethodDefinition(method) => {
let PropertyKey::PrivateIdentifier(private_id) = &method.key else {
return None;
};
Some(private_id.name.into())
Some(private_id.name)
}
ClassElement::AccessorProperty(accessor) => {
let PropertyKey::PrivateIdentifier(private_id) = &accessor.key else {
return None;
};
Some(private_id.name.into())
Some(private_id.name)
}
ClassElement::StaticBlock(_) => None,
ClassElement::TSIndexSignature(_) => {
+7 -9
View File
@@ -1,10 +1,8 @@
use rustc_hash::FxHashSet;
use oxc_allocator::{Allocator, BitSet};
use oxc_data_structures::stack::NonEmptyStack;
use oxc_semantic::Scoping;
use oxc_span::SourceType;
use oxc_str::Str;
use oxc_str::{Ident, IdentHashSet};
use oxc_syntax::scope::ScopeId;
use crate::{CompressOptions, symbol_state::SymbolState};
@@ -195,12 +193,12 @@ impl<'a> MinifierState<'a> {
/// Private member names used in each currently enclosing class.
pub struct PrivateMemberUsageStack<'a> {
stack: NonEmptyStack<FxHashSet<Str<'a>>>,
stack: NonEmptyStack<IdentHashSet<'a>>,
}
impl<'a> PrivateMemberUsageStack<'a> {
pub fn new() -> Self {
Self { stack: NonEmptyStack::new(FxHashSet::default()) }
Self { stack: NonEmptyStack::new(IdentHashSet::default()) }
}
/// Whether all class scopes have been exited.
@@ -209,11 +207,11 @@ impl<'a> PrivateMemberUsageStack<'a> {
}
pub fn enter_class(&mut self) {
self.stack.push(FxHashSet::default());
self.stack.push(IdentHashSet::default());
}
/// Exit a class and propagate uses of names declared by an outer class.
pub fn exit_class(&mut self, declared_private_members: impl Iterator<Item = Str<'a>>) {
pub fn exit_class(&mut self, declared_private_members: impl Iterator<Item = Ident<'a>>) {
let mut used_private_members = self.stack.pop();
declared_private_members.for_each(|name| {
used_private_members.remove(&name);
@@ -221,11 +219,11 @@ impl<'a> PrivateMemberUsageStack<'a> {
self.stack.last_mut().extend(used_private_members);
}
pub fn record_use(&mut self, name: Str<'a>) {
pub fn record_use(&mut self, name: Ident<'a>) {
self.stack.last_mut().insert(name);
}
pub fn is_used(&self, name: &Str<'a>) -> bool {
pub fn is_used(&self, name: &Ident<'a>) -> bool {
self.stack.last().contains(name)
}
}