diff --git a/Cargo.lock b/Cargo.lock index faeacdbcadb0f..d4b64f902f4c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1830,6 +1830,7 @@ name = "oxc_mangler" version = "0.45.0" dependencies = [ "itertools", + "oxc_allocator", "oxc_ast", "oxc_index", "oxc_semantic", diff --git a/crates/oxc_mangler/Cargo.toml b/crates/oxc_mangler/Cargo.toml index 522a465157697..6532b5a20e635 100644 --- a/crates/oxc_mangler/Cargo.toml +++ b/crates/oxc_mangler/Cargo.toml @@ -21,9 +21,11 @@ test = false doctest = false [dependencies] -itertools = { workspace = true } +oxc_allocator = { workspace = true } oxc_ast = { workspace = true } oxc_index = { workspace = true } oxc_semantic = { workspace = true } oxc_span = { workspace = true } + +itertools = { workspace = true } rustc-hash = { workspace = true } diff --git a/crates/oxc_mangler/src/lib.rs b/crates/oxc_mangler/src/lib.rs index a96366e4945c1..64744ddf22907 100644 --- a/crates/oxc_mangler/src/lib.rs +++ b/crates/oxc_mangler/src/lib.rs @@ -1,11 +1,11 @@ use itertools::Itertools; -use oxc_ast::ast::{Declaration, Program, Statement}; -use oxc_index::{index_vec, Idx, IndexVec}; -use oxc_semantic::{ReferenceId, ScopeTree, SemanticBuilder, SymbolId, SymbolTable}; -use oxc_span::CompactStr; use rustc_hash::FxHashSet; -type Slot = usize; +use oxc_allocator::{Allocator, Vec}; +use oxc_ast::ast::{Declaration, Program, Statement}; +use oxc_index::Idx; +use oxc_semantic::{ReferenceId, ScopeTree, SemanticBuilder, SymbolId, SymbolTable}; +use oxc_span::{Atom, CompactStr}; #[derive(Default, Debug, Clone, Copy)] pub struct MangleOptions { @@ -13,6 +13,8 @@ pub struct MangleOptions { pub debug: bool, } +type Slot = usize; + /// # Name Mangler / Symbol Minification /// /// See: @@ -102,6 +104,8 @@ impl Mangler { Default::default() }; + let allocator = Allocator::default(); + // Mangle the symbol table by computing slots from the scope tree. // A slot is the occurrence index of a binding identifier inside a scope. let mut symbol_table = symbol_table; @@ -109,11 +113,17 @@ impl Mangler { // Total number of slots for all scopes let mut total_number_of_slots: Slot = 0; - // All symbols with their assigned slots - let mut slots: IndexVec = index_vec![0; symbol_table.len()]; + // All symbols with their assigned slots. Keyed by symbol id. + let mut slots: Vec<'_, Slot> = Vec::with_capacity_in(symbol_table.len(), &allocator); + for _ in 0..symbol_table.len() { + slots.push(0); + } // Keep track of the maximum slot number for each scope - let mut max_slot_for_scope = vec![0; scope_tree.len()]; + let mut max_slot_for_scope = Vec::with_capacity_in(scope_tree.len(), &allocator); + for _ in 0..scope_tree.len() { + max_slot_for_scope.push(0); + } // Walk the scope tree and compute the slot number for each scope for scope_id in scope_tree.descendants_from_root() { @@ -128,10 +138,10 @@ impl Mangler { if !bindings.is_empty() { // Sort `bindings` in declaration order. - let mut bindings = bindings.values().copied().collect::>(); + let mut bindings = bindings.values().copied().collect::>(); bindings.sort_unstable(); for symbol_id in bindings { - slots[symbol_id] = slot; + slots[symbol_id.index()] = slot; slot += 1; } } @@ -149,12 +159,13 @@ impl Mangler { scope_tree, total_number_of_slots, &slots, + &allocator, ); let root_unresolved_references = scope_tree.root_unresolved_references(); let root_bindings = scope_tree.get_bindings(scope_tree.root_scope_id()); - let mut reserved_names = Vec::with_capacity(total_number_of_slots); + let mut reserved_names = Vec::with_capacity_in(total_number_of_slots, &allocator); let generate_name = if self.options.debug { debug_name } else { base54 }; let mut count = 0; @@ -199,8 +210,10 @@ impl Mangler { // (freq_iter is sorted by frequency from highest to lowest, // so taking means take the N most frequent symbols remaining) let slice_of_same_len_strings = slice_of_same_len_strings_group.collect_vec(); - let mut symbols_renamed_in_this_batch = - freq_iter.by_ref().take(slice_of_same_len_strings.len()).collect::>(); + let mut symbols_renamed_in_this_batch = freq_iter + .by_ref() + .take(slice_of_same_len_strings.len()) + .collect::>(); debug_assert!(symbols_renamed_in_this_batch.len() == slice_of_same_len_strings.len()); @@ -226,17 +239,23 @@ impl Mangler { self } - fn tally_slot_frequencies( - &self, + fn tally_slot_frequencies<'a>( + &'a self, symbol_table: &SymbolTable, exported_symbols: &FxHashSet, scope_tree: &ScopeTree, total_number_of_slots: usize, - slots: &IndexVec, - ) -> Vec { + slots: &[Slot], + allocator: &'a Allocator, + ) -> Vec<'a, SlotFrequency<'a>> { let root_scope_id = scope_tree.root_scope_id(); - let mut frequencies = vec![SlotFrequency::default(); total_number_of_slots]; - for (symbol_id, slot) in slots.iter_enumerated() { + let mut frequencies = Vec::with_capacity_in(total_number_of_slots, allocator); + for _ in 0..total_number_of_slots { + frequencies.push(SlotFrequency::new(allocator)); + } + + for (symbol_id, slot) in slots.iter().copied().enumerate() { + let symbol_id = SymbolId::from_usize(symbol_id); if symbol_table.get_scope_id(symbol_id) == root_scope_id && (!self.options.top_level || exported_symbols.contains(&symbol_id)) { @@ -245,8 +264,8 @@ impl Mangler { if is_special_name(symbol_table.get_name(symbol_id)) { continue; } - let index = *slot; - frequencies[index].slot = *slot; + let index = slot; + frequencies[index].slot = slot; frequencies[index].frequency += symbol_table.get_resolved_reference_ids(symbol_id).len(); frequencies[index].symbol_ids.push(symbol_id); @@ -255,7 +274,9 @@ impl Mangler { frequencies } - fn collect_exported_symbols(program: &Program) -> (FxHashSet, FxHashSet) { + fn collect_exported_symbols<'a>( + program: &Program<'a>, + ) -> (FxHashSet>, FxHashSet) { program .body .iter() @@ -274,7 +295,7 @@ impl Mangler { itertools::Either::Right(decl.id().into_iter()) } }) - .map(|id| (id.name.to_compact_str(), id.symbol_id())) + .map(|id| (id.name.clone(), id.symbol_id())) .collect() } } @@ -283,11 +304,17 @@ fn is_special_name(name: &str) -> bool { matches!(name, "exports" | "arguments") } -#[derive(Debug, Default, Clone)] -struct SlotFrequency { +#[derive(Debug)] +struct SlotFrequency<'a> { pub slot: Slot, pub frequency: usize, - pub symbol_ids: Vec, + pub symbol_ids: Vec<'a, SymbolId>, +} + +impl<'a> SlotFrequency<'a> { + fn new(allocator: &'a Allocator) -> Self { + Self { slot: 0, frequency: 0, symbol_ids: Vec::new_in(allocator) } + } } #[rustfmt::skip]