Skip to content

Commit 5cf8bc8

Browse files
author
Cameron McHenry
committed
refactor(mangler): use external allocator
1 parent fa924ab commit 5cf8bc8

File tree

2 files changed

+102
-15
lines changed

2 files changed

+102
-15
lines changed

crates/oxc_mangler/src/lib.rs

Lines changed: 98 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ pub struct MangleOptions {
3737

3838
type Slot = usize;
3939

40+
/// Enum to handle both owned and borrowed allocators. This is not `Cow` because that type
41+
/// requires `ToOwned`/`Clone`, which is not implemented for `Allocator`. Although this does
42+
/// incur some pointer indirect on each reference to the allocator, it allows the API to be
43+
/// more ergonomic by either accepting an existing allocator, or allowing an internal one to
44+
/// be created and used temporarily automatically.
45+
enum TempAllocator<'t> {
46+
Owned(Allocator),
47+
Borrowed(&'t Allocator),
48+
}
49+
50+
impl TempAllocator<'_> {
51+
/// Get a reference to the allocator, regardless of whether it's owned or borrowed
52+
fn as_ref(&self) -> &Allocator {
53+
match self {
54+
TempAllocator::Owned(allocator) => allocator,
55+
TempAllocator::Borrowed(allocator) => allocator,
56+
}
57+
}
58+
}
59+
4060
/// # Name Mangler / Symbol Minification
4161
///
4262
/// ## Example
@@ -155,17 +175,80 @@ type Slot = usize;
155175
/// - slot 1: `top_level_b`, `foo_a`, `bar_a`
156176
/// - slot 2: `foo`
157177
/// - slot 3: `bar`
158-
#[derive(Default)]
159-
pub struct Mangler {
178+
pub struct Mangler<'t> {
160179
options: MangleOptions,
180+
/// An allocator meant to be used for temporary allocations during mangling.
181+
/// It can be cleared after mangling is done, to free up memory for subsequent
182+
/// files or other operations.
183+
temp_allocator: TempAllocator<'t>,
161184
}
162185

163-
impl Mangler {
186+
impl Default for Mangler<'_> {
187+
fn default() -> Self {
188+
Self {
189+
options: MangleOptions::default(),
190+
temp_allocator: TempAllocator::Owned(Allocator::default()),
191+
}
192+
}
193+
}
194+
195+
impl<'t> Mangler<'t> {
164196
#[must_use]
165197
pub fn new() -> Self {
166198
Self::default()
167199
}
168200

201+
/// Creates a new `Mangler` using an existing temporary allocator. This is an allocator
202+
/// that can be reset after mangling and is only used for temporary allocations during
203+
/// the mangling process. This makes processing multiple files at once much more efficient,
204+
/// because the same memory can be used for mangling each file.
205+
///
206+
/// # Examples
207+
///
208+
/// ```rust
209+
/// use oxc_allocator::Allocator;
210+
/// use oxc_mangler::Mangler;
211+
/// use oxc_parser::Parser;
212+
/// use oxc_span::SourceType;
213+
///
214+
/// let allocator = Allocator::default();
215+
/// let temp_allocator = Allocator::default();
216+
/// let source = "function myFunction(param) { return param + 1; }";
217+
///
218+
/// let parsed = Parser::new(&allocator, source, SourceType::mjs()).parse();
219+
/// let mangled_symbols = Mangler::new_with_temp_allocator(&temp_allocator)
220+
/// .build(&parsed.program);
221+
///
222+
/// // Reset the allocator to free temporary memory
223+
/// temp_allocator.reset();
224+
/// ```
225+
///
226+
/// Processing multiple files:
227+
///
228+
/// ```rust
229+
/// # use oxc_allocator::Allocator;
230+
/// # use oxc_mangler::Mangler;
231+
/// # use oxc_parser::Parser;
232+
/// # use oxc_span::SourceType;
233+
/// let allocator = Allocator::default();
234+
/// let temp_allocator = Allocator::default();
235+
/// let files = ["function foo() {}", "function bar() {}"];
236+
///
237+
/// for source in files {
238+
/// let parsed = Parser::new(&allocator, source, SourceType::mjs()).parse();
239+
/// let mangled_symbols = Mangler::new_with_temp_allocator(&temp_allocator)
240+
/// .build(&parsed.program);
241+
/// temp_allocator.reset(); // Free memory between files
242+
/// }
243+
/// ```
244+
#[must_use]
245+
pub fn new_with_temp_allocator(temp_allocator: &'t Allocator) -> Self {
246+
Self {
247+
options: MangleOptions::default(),
248+
temp_allocator: TempAllocator::Borrowed(temp_allocator),
249+
}
250+
}
251+
169252
#[must_use]
170253
pub fn with_options(mut self, options: MangleOptions) -> Self {
171254
self.options = options;
@@ -216,10 +299,11 @@ impl Mangler {
216299
let (keep_name_names, keep_name_symbols) =
217300
Mangler::collect_keep_name_symbols(self.options.keep_names, scoping, ast_nodes);
218301

219-
let allocator = Allocator::default();
220-
221302
// All symbols with their assigned slots. Keyed by symbol id.
222-
let mut slots = Vec::from_iter_in(iter::repeat_n(0, scoping.symbols_len()), &allocator);
303+
let mut slots = Vec::from_iter_in(
304+
iter::repeat_n(0, scoping.symbols_len()),
305+
self.temp_allocator.as_ref(),
306+
);
223307

224308
// Stores the lived scope ids for each slot. Keyed by slot number.
225309
let mut slot_liveness: std::vec::Vec<FixedBitSet> = vec![];
@@ -300,13 +384,13 @@ impl Mangler {
300384
&keep_name_symbols,
301385
total_number_of_slots,
302386
&slots,
303-
&allocator,
304387
);
305388

306389
let root_unresolved_references = scoping.root_unresolved_references();
307390
let root_bindings = scoping.get_bindings(scoping.root_scope_id());
308391

309-
let mut reserved_names = Vec::with_capacity_in(total_number_of_slots, &allocator);
392+
let mut reserved_names =
393+
Vec::with_capacity_in(total_number_of_slots, self.temp_allocator.as_ref());
310394

311395
let mut count = 0;
312396
for _ in 0..total_number_of_slots {
@@ -387,12 +471,12 @@ impl Mangler {
387471
keep_name_symbols: &FxHashSet<SymbolId>,
388472
total_number_of_slots: usize,
389473
slots: &[Slot],
390-
allocator: &'a Allocator,
391474
) -> Vec<'a, SlotFrequency<'a>> {
392475
let root_scope_id = scoping.root_scope_id();
393476
let mut frequencies = Vec::from_iter_in(
394-
repeat_with(|| SlotFrequency::new(allocator)).take(total_number_of_slots),
395-
allocator,
477+
repeat_with(|| SlotFrequency::new(self.temp_allocator.as_ref()))
478+
.take(total_number_of_slots),
479+
self.temp_allocator.as_ref(),
396480
);
397481

398482
for (symbol_id, slot) in slots.iter().copied().enumerate() {
@@ -463,9 +547,9 @@ struct SlotFrequency<'a> {
463547
pub symbol_ids: Vec<'a, SymbolId>,
464548
}
465549

466-
impl<'a> SlotFrequency<'a> {
467-
fn new(allocator: &'a Allocator) -> Self {
468-
Self { slot: 0, frequency: 0, symbol_ids: Vec::new_in(allocator) }
550+
impl<'t> SlotFrequency<'t> {
551+
fn new(temp_allocator: &'t Allocator) -> Self {
552+
Self { slot: 0, frequency: 0, symbol_ids: Vec::new_in(temp_allocator) }
469553
}
470554
}
471555

tasks/benchmark/benches/minifier.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,17 @@ fn bench_mangler(criterion: &mut Criterion) {
5757
let source_type = SourceType::from_path(&file.file_name).unwrap();
5858
let source_text = file.source_text.as_str();
5959
let mut allocator = Allocator::default();
60+
let mut temp_allocator = Allocator::default();
6061
group.bench_function(id, |b| {
6162
b.iter_with_setup_wrapper(|runner| {
6263
allocator.reset();
64+
temp_allocator.reset();
6365
let program = Parser::new(&allocator, source_text, source_type).parse().program;
6466
let mut semantic =
6567
SemanticBuilder::new().with_scope_tree_child_ids(true).build(&program).semantic;
6668
runner.run(|| {
67-
Mangler::new().build_with_semantic(&mut semantic, &program);
69+
Mangler::new_with_temp_allocator(&temp_allocator)
70+
.build_with_semantic(&mut semantic, &program);
6871
});
6972
});
7073
});

0 commit comments

Comments
 (0)