diff --git a/Cargo.lock b/Cargo.lock index e877105d0f95..1cd7d8461b20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1916,6 +1916,7 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", + "rayon", ] [[package]] @@ -5347,6 +5348,7 @@ dependencies = [ "backtrace", "codspeed-criterion-compat", "criterion", + "hashbrown 0.14.5", "indexmap 2.7.1", "num-bigint", "num_cpus", @@ -5764,6 +5766,7 @@ version = "11.0.0" dependencies = [ "indexmap 2.7.1", "rustc-hash 2.1.0", + "swc_allocator", "swc_atoms", "swc_common", "swc_ecma_ast", diff --git a/crates/swc_ecma_minifier/Cargo.toml b/crates/swc_ecma_minifier/Cargo.toml index 996722390405..b6a6d1279980 100644 --- a/crates/swc_ecma_minifier/Cargo.toml +++ b/crates/swc_ecma_minifier/Cargo.toml @@ -24,6 +24,7 @@ concurrent = [ "swc_ecma_transforms_optimization/concurrent", "rayon", "indexmap/rayon", + "hashbrown/rayon", ] debug = ["backtrace", "swc_ecma_transforms_optimization/debug"] default = ["serde-impl"] @@ -35,6 +36,7 @@ trace-ast = [] [dependencies] arrayvec = { workspace = true } backtrace = { workspace = true, optional = true } +hashbrown = { workspace = true } indexmap = { workspace = true } num-bigint = { workspace = true } num_cpus = { workspace = true } diff --git a/crates/swc_ecma_minifier/src/compress/hoist_decls.rs b/crates/swc_ecma_minifier/src/compress/hoist_decls.rs index 0b476ae0b439..9d309203be35 100644 --- a/crates/swc_ecma_minifier/src/compress/hoist_decls.rs +++ b/crates/swc_ecma_minifier/src/compress/hoist_decls.rs @@ -19,7 +19,10 @@ pub(super) struct DeclHoisterConfig { pub _top_level: bool, } -pub(super) fn decl_hoister(config: DeclHoisterConfig, data: &ProgramData) -> Hoister { +pub(super) fn decl_hoister<'a, 'alloc>( + config: DeclHoisterConfig, + data: &'a ProgramData<'alloc>, +) -> Hoister<'a, 'alloc> { Hoister { config, changed: false, @@ -27,13 +30,13 @@ pub(super) fn decl_hoister(config: DeclHoisterConfig, data: &ProgramData) -> Hoi } } -pub(super) struct Hoister<'a> { +pub(super) struct Hoister<'a, 'alloc> { config: DeclHoisterConfig, changed: bool, - data: &'a ProgramData, + data: &'a ProgramData<'alloc>, } -impl Repeated for Hoister<'_> { +impl Repeated for Hoister<'_, '_> { fn changed(&self) -> bool { self.changed } @@ -43,11 +46,12 @@ impl Repeated for Hoister<'_> { } } -impl Hoister<'_> { +impl<'alloc> Hoister<'_, 'alloc> { fn handle_stmt_likes(&mut self, stmts: &mut Vec) where T: StmtLike + IsModuleItem + ModuleItemExt, - Vec: for<'aa> VisitMutWith> + VisitWith>, + Vec: for<'aa> VisitMutWith> + + VisitWith>>, { stmts.visit_mut_children_with(self); let len = stmts.len(); @@ -279,7 +283,7 @@ impl Hoister<'_> { } } -impl VisitMut for Hoister<'_> { +impl VisitMut for Hoister<'_, '_> { noop_visit_mut_type!(); fn visit_mut_module_items(&mut self, stmts: &mut Vec) { diff --git a/crates/swc_ecma_minifier/src/compress/mod.rs b/crates/swc_ecma_minifier/src/compress/mod.rs index 7cc57a8dd111..dfdc0bf438fc 100644 --- a/crates/swc_ecma_minifier/src/compress/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/mod.rs @@ -2,10 +2,11 @@ use std::fmt::{self, Debug, Display, Formatter}; #[cfg(feature = "debug")] use std::thread; -use std::{borrow::Cow, fmt::Write, time::Instant}; +use std::{borrow::Cow, fmt::Write, mem::transmute, time::Instant}; #[cfg(feature = "pretty_assertions")] use pretty_assertions::assert_eq; +use swc_allocator::allocators::Arena; use swc_common::pass::{CompilerPass, Optional, Repeated}; use swc_ecma_ast::*; use swc_ecma_transforms_optimization::simplify::{ @@ -23,7 +24,7 @@ use crate::{ debug::{dump, AssertValid}, mode::Mode, option::{CompressOptions, MangleOptions}, - program_data::analyze, + program_data::{analyze, ProgramData}, util::{force_dump_program, now}, }; @@ -95,7 +96,8 @@ impl Compressor<'_> { ); if self.options.hoist_vars || self.options.hoist_fns { - let data = analyze(&*n, Some(self.marks)); + let arena = Arena::default(); + let data = analyze(&arena, &*n, Some(self.marks)); let mut v = decl_hoister( DeclHoisterConfig { @@ -256,23 +258,27 @@ impl Compressor<'_> { { let _timer = timer!("apply full optimizer"); - let mut data = analyze(&*n, Some(self.marks)); + let arena = Arena::default(); + + let mut data = analyze(&arena, &*n, Some(self.marks)); // TODO: reset_opt_flags // // This is swc version of `node.optimize(this);`. - let mut visitor = optimizer( - self.marks, - self.options, - self.mangle_options, - &mut data, - self.mode, - !self.dump_for_infinite_loop.is_empty(), - ); - n.visit_mut_with(&mut visitor); + { + let mut visitor = optimizer( + self.marks, + self.options, + self.mangle_options, + unsafe { transmute::<&mut ProgramData, &mut ProgramData>(&mut data) }, + self.mode, + !self.dump_for_infinite_loop.is_empty(), + ); + n.visit_mut_with(&mut visitor); - self.changed |= visitor.changed(); + self.changed |= visitor.changed(); + } // let done = dump(&*n); // debug!("===== Result =====\n{}", done); diff --git a/crates/swc_ecma_minifier/src/compress/optimize/arguments.rs b/crates/swc_ecma_minifier/src/compress/optimize/arguments.rs index a28bbb27b790..fb1addcf3231 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/arguments.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/arguments.rs @@ -9,7 +9,7 @@ use super::Optimizer; use crate::compress::optimize::is_left_access_to_arguments; /// Methods related to the option `arguments`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// /// - `arguments['foo']` => `arguments.foo` pub(super) fn optimize_str_access_to_arguments(&mut self, e: &mut Expr) { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/bools.rs b/crates/swc_ecma_minifier/src/compress/optimize/bools.rs index c541ee8c34ca..61eb619885ea 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/bools.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/bools.rs @@ -11,7 +11,7 @@ use crate::compress::{optimize::Ctx, util::negate_cost}; use crate::debug::dump; /// Methods related to the options `bools` and `bool_as_ints`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// **This negates bool**. /// /// Returns true if it's negated. diff --git a/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs b/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs index 768a9d956517..e570b830b01d 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/conditionals.rs @@ -17,7 +17,7 @@ use crate::{ /// Methods related to the option `conditionals`. All methods are noop if /// `conditionals` is false. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// Negates the condition of a `if` statement to reduce body size. pub(super) fn negate_if_stmt(&mut self, stmt: &mut IfStmt) { let alt = match stmt.alt.as_deref_mut() { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs b/crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs index 7fec4b5b8a05..8c19110be413 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs @@ -4,7 +4,7 @@ use swc_ecma_ast::*; use super::Optimizer; /// Methods related to option `dead_code`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// Optimize return value or argument of throw. /// /// This methods removes some useless assignments. diff --git a/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs b/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs index 41ff4f1bfb09..44209f16962e 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs @@ -9,7 +9,7 @@ use super::Optimizer; use crate::{compress::util::eval_as_number, maybe_par, DISABLE_BUGGY_PASSES}; /// Methods related to the option `evaluate`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// Evaluate expression if possible. /// /// This method call appropriate methods for each ast types. diff --git a/crates/swc_ecma_minifier/src/compress/optimize/if_return.rs b/crates/swc_ecma_minifier/src/compress/optimize/if_return.rs index dfd0ac823c4e..3ff6321dd4df 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/if_return.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/if_return.rs @@ -11,7 +11,7 @@ use crate::{compress::util::is_pure_undefined, util::ExprOptExt}; /// Methods related to the option `if_return`. All methods are noop if /// `if_return` is false. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { pub(super) fn merge_nested_if(&mut self, s: &mut IfStmt) { if !self.options.conditionals && !self.options.bools { return; diff --git a/crates/swc_ecma_minifier/src/compress/optimize/iife.rs b/crates/swc_ecma_minifier/src/compress/optimize/iife.rs index b947199e5a6a..090c93b37249 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/iife.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/iife.rs @@ -16,7 +16,7 @@ use crate::{ }; /// Methods related to the option `negate_iife`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// Negates iife, while ignore return value. pub(super) fn negate_iife_ignoring_ret(&mut self, e: &mut Expr) { if !self.options.negate_iife || self.ctx.in_bang_arg || self.ctx.dont_use_negated_iife { @@ -112,7 +112,7 @@ impl Optimizer<'_> { } /// Methods related to iife. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// # Example /// /// ## Input @@ -964,7 +964,7 @@ impl Optimizer<'_> { Stmt::Decl(Decl::Var(ref mut var)) => { for decl in &mut var.decls { if decl.init.is_some() { - let ids = find_pat_ids(decl); + let ids: Vec = find_pat_ids(decl); for id in ids { if let Some(usage) = self.data.vars.get_mut(&id) { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs index f30776b6842c..f6724b7136ab 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs @@ -16,7 +16,7 @@ use crate::{ }; /// Methods related to option `inline`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// Stores the value of a variable to inline it. /// /// This method may remove value of initializer. It mean that the value will diff --git a/crates/swc_ecma_minifier/src/compress/optimize/loops.rs b/crates/swc_ecma_minifier/src/compress/optimize/loops.rs index 1f1f59ee4e81..db14aadc88ef 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/loops.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/loops.rs @@ -5,7 +5,7 @@ use swc_ecma_utils::{ExprExt, Value::Known}; use crate::compress::{optimize::Optimizer, util::UnreachableHandler}; /// Methods related to the option `loops`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// `for(a;b;c;) break;` => `a;b;` pub(super) fn optimize_loops_with_break(&mut self, s: &mut Stmt) { if !self.options.loops { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs index 534ce9b556c8..e4b03e0ec173 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs @@ -56,14 +56,14 @@ mod unused; mod util; /// This pass is similar to `node.optimize` of terser. -pub(super) fn optimizer<'a>( +pub(super) fn optimizer<'a: 'alloc, 'alloc>( marks: Marks, options: &'a CompressOptions, mangle_options: Option<&'a MangleOptions>, - data: &'a mut ProgramData, + data: &'a mut ProgramData<'alloc>, mode: &'a dyn Mode, debug_infinite_loop: bool, -) -> impl 'a + VisitMut + Repeated { +) -> impl 'a + 'alloc + VisitMut + Repeated { assert!( options.top_retain.iter().all(|s| s.trim() != ""), "top_retain should not contain empty string" @@ -216,7 +216,7 @@ impl Ctx { } } -struct Optimizer<'a> { +struct Optimizer<'a, 'alloc> { marks: Marks, changed: bool, @@ -234,7 +234,7 @@ struct Optimizer<'a> { /// /// This is calculated multiple time, but only once per one /// `visit_mut_module`. - data: &'a mut ProgramData, + data: &'a mut ProgramData<'alloc>, ctx: Ctx, mode: &'a dyn Mode, @@ -317,7 +317,7 @@ impl Vars { } } -impl Repeated for Optimizer<'_> { +impl<'alloc> Repeated for Optimizer<'_, 'alloc> { fn changed(&self) -> bool { self.changed } @@ -344,7 +344,7 @@ impl From<&Function> for FnMetadata { } } -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { fn may_remove_ident(&self, id: &Ident) -> bool { if let Some(VarUsageInfo { exported: true, .. }) = self.data.vars.get(&id.clone().to_id()).map(|v| &**v) @@ -431,7 +431,9 @@ impl Optimizer<'_> { fn handle_stmt_likes(&mut self, stmts: &mut Vec, will_terminate: bool) where T: StmtLike + ModuleItemLike + ModuleItemExt + VisitMutWith + VisitWith, - Vec: VisitMutWith + VisitWith> + VisitWith, + Vec: VisitMutWith + + VisitWith>> + + VisitWith, { let mut use_asm = false; let prepend_stmts = self.prepend_stmts.take(); @@ -1526,7 +1528,7 @@ impl Optimizer<'_> { } } -impl VisitMut for Optimizer<'_> { +impl<'alloc> VisitMut for Optimizer<'_, 'alloc> { noop_visit_mut_type!(); #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] diff --git a/crates/swc_ecma_minifier/src/compress/optimize/ops.rs b/crates/swc_ecma_minifier/src/compress/optimize/ops.rs index b4bc8e4e70bb..83189fe0020a 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/ops.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/ops.rs @@ -9,7 +9,7 @@ use crate::{ util::{make_bool, ValueExt}, }; -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// /// - `'12' === `foo` => '12' == 'foo'` pub(super) fn optimize_bin_equal(&mut self, e: &mut BinExpr) { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/props.rs b/crates/swc_ecma_minifier/src/compress/optimize/props.rs index eedc9eca944e..b838aa83a079 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/props.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/props.rs @@ -6,7 +6,7 @@ use super::{unused::PropertyAccessOpts, Optimizer}; use crate::util::deeply_contains_this_expr; /// Methods related to the option `hoist_props`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { pub(super) fn hoist_props_of_var( &mut self, n: &mut VarDeclarator, @@ -241,7 +241,7 @@ fn is_expr_fine_for_hoist_props(value: &Expr) -> bool { } } -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// Converts `{ a: 1 }.a` into `1`. pub(super) fn handle_property_access(&mut self, e: &mut Expr) { if !self.options.props { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs index cf2c41649352..0efb68d75895 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs @@ -31,7 +31,7 @@ use crate::{ /// Methods related to the option `sequences`. All methods are noop if /// `sequences` is false. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// /// # Example /// diff --git a/crates/swc_ecma_minifier/src/compress/optimize/strings.rs b/crates/swc_ecma_minifier/src/compress/optimize/strings.rs index 967380f6a0eb..8c8bae1528cc 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/strings.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/strings.rs @@ -4,7 +4,7 @@ use swc_ecma_utils::{ExprExt, Value::Known}; use super::Optimizer; -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { pub(super) fn optimize_expr_in_str_ctx_unsafely(&mut self, e: &mut Expr) { if !self.options.unsafe_passes { return; diff --git a/crates/swc_ecma_minifier/src/compress/optimize/switches.rs b/crates/swc_ecma_minifier/src/compress/optimize/switches.rs index 8ca36657745e..6c470e8ac81e 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/switches.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/switches.rs @@ -7,7 +7,7 @@ use super::Optimizer; use crate::{compress::util::is_primitive, util::idents_used_by}; /// Methods related to option `switches`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { /// Handle switches in the case where we can know which branch will be /// taken. pub(super) fn optimize_const_switches(&mut self, s: &mut Stmt) { diff --git a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs index dc387d252374..da67e75360ff 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/unused.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/unused.rs @@ -19,7 +19,7 @@ pub(crate) struct PropertyAccessOpts { } /// Methods related to the option `unused`. -impl Optimizer<'_> { +impl<'alloc> Optimizer<'_, 'alloc> { #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] pub(super) fn drop_unused_var_declarator( &mut self, diff --git a/crates/swc_ecma_minifier/src/compress/optimize/util.rs b/crates/swc_ecma_minifier/src/compress/optimize/util.rs index 2c6d58bdb2e8..d79126b5db6a 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/util.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/util.rs @@ -15,7 +15,7 @@ use tracing::debug; use super::{Ctx, Optimizer}; use crate::HEAVY_TASK_PARALLELS; -impl<'b> Optimizer<'b> { +impl<'b, 'alloc> Optimizer<'b, 'alloc> { pub(super) fn normalize_expr(&mut self, e: &mut Expr) { match e { Expr::Seq(seq) => { @@ -78,7 +78,7 @@ impl<'b> Optimizer<'b> { } /// RAII guard to change context temporarically - pub(super) fn with_ctx(&mut self, mut ctx: Ctx) -> WithCtx<'_, 'b> { + pub(super) fn with_ctx<'a>(&'a mut self, mut ctx: Ctx) -> WithCtx<'a, 'b, 'alloc> { let mut scope_ctxt = ctx.scope; if self.ctx.scope != scope_ctxt { @@ -136,26 +136,26 @@ impl<'b> Optimizer<'b> { } } -pub(super) struct WithCtx<'a, 'b> { - reducer: &'a mut Optimizer<'b>, +pub(super) struct WithCtx<'a, 'b, 'alloc> { + reducer: &'a mut Optimizer<'b, 'alloc>, orig_ctx: Ctx, } -impl<'b> Deref for WithCtx<'_, 'b> { - type Target = Optimizer<'b>; +impl<'b, 'alloc> Deref for WithCtx<'_, 'b, 'alloc> { + type Target = Optimizer<'b, 'alloc>; fn deref(&self) -> &Self::Target { self.reducer } } -impl DerefMut for WithCtx<'_, '_> { +impl<'a, 'b, 'alloc> DerefMut for WithCtx<'a, 'b, 'alloc> { fn deref_mut(&mut self) -> &mut Self::Target { self.reducer } } -impl Drop for WithCtx<'_, '_> { +impl<'a, 'b, 'alloc> Drop for WithCtx<'a, 'b, 'alloc> { fn drop(&mut self) { self.reducer.ctx = self.orig_ctx.clone(); } diff --git a/crates/swc_ecma_minifier/src/pass/mangle_props.rs b/crates/swc_ecma_minifier/src/pass/mangle_props.rs index 7761ede068ee..c29598cf8307 100644 --- a/crates/swc_ecma_minifier/src/pass/mangle_props.rs +++ b/crates/swc_ecma_minifier/src/pass/mangle_props.rs @@ -2,6 +2,8 @@ use std::collections::HashSet; use once_cell::sync::Lazy; use rustc_hash::{FxHashMap, FxHashSet}; +use swc_atoms::Atom; +use swc_allocator::allocators::Arena; use swc_atoms::JsWord; use swc_ecma_ast::{ CallExpr, Callee, Expr, IdentName, KeyValueProp, Lit, MemberExpr, MemberProp, Program, Prop, @@ -101,6 +103,8 @@ pub(crate) fn mangle_properties( options: ManglePropertiesOptions, chars: Base54Chars, ) { + let arena = Arena::default(); + let mut state = ManglePropertiesState { options, chars, @@ -110,7 +114,7 @@ pub(crate) fn mangle_properties( n: 0, }; - let data = analyze(&*m, None); + let data = analyze(&arena, &*m, None); m.visit_mut_with(&mut PropertyCollector { state: &mut state, data, @@ -120,12 +124,12 @@ pub(crate) fn mangle_properties( } // Step 1 -- collect candidates to mangle -pub struct PropertyCollector<'a> { - data: ProgramData, +pub struct PropertyCollector<'a, 'alloc> { + data: ProgramData<'alloc>, state: &'a mut ManglePropertiesState, } -impl VisitMut for PropertyCollector<'_> { +impl<'alloc> VisitMut for PropertyCollector<'_, 'alloc> { fn visit_mut_call_expr(&mut self, call: &mut CallExpr) { call.visit_mut_children_with(self); diff --git a/crates/swc_ecma_minifier/src/program_data.rs b/crates/swc_ecma_minifier/src/program_data.rs index 8050028ac948..c9a319c345ad 100644 --- a/crates/swc_ecma_minifier/src/program_data.rs +++ b/crates/swc_ecma_minifier/src/program_data.rs @@ -1,8 +1,10 @@ -use std::collections::hash_map::Entry; - use indexmap::IndexSet; use rustc_hash::{FxBuildHasher, FxHashMap}; -use swc_atoms::JsWord; +use swc_allocator::{ + allocators::Arena, + api::{arena, hashbrown::hash_map::Entry}, +}; +use swc_atoms::Atom; use swc_common::SyntaxContext; use swc_ecma_ast::*; use swc_ecma_usage_analyzer::{ @@ -18,21 +20,25 @@ use swc_ecma_usage_analyzer::{ use swc_ecma_utils::{Merge, Type, Value}; use swc_ecma_visit::VisitWith; -pub(crate) fn analyze(n: &N, marks: Option) -> ProgramData +pub(crate) fn analyze<'alloc, N>( + alloc: &'alloc Arena, + n: &N, + marks: Option, +) -> ProgramData<'alloc> where - N: VisitWith>, + N: VisitWith>>, { - analyze_with_storage::(n, marks) + analyze_with_storage::, _>(&alloc, n, marks) } /// Analyzed info of a whole program we are working on. -#[derive(Debug, Default)] -pub(crate) struct ProgramData { - pub(crate) vars: FxHashMap>, +#[derive(Debug)] +pub(crate) struct ProgramData<'alloc> { + pub(crate) vars: arena::HashMap<'alloc, Id, Box>, pub(crate) top: ScopeData, - pub(crate) scopes: FxHashMap, + pub(crate) scopes: arena::HashMap<'alloc, SyntaxContext, ScopeData>, initialized_vars: IndexSet, } @@ -120,7 +126,7 @@ pub(crate) struct VarUsageInfo { /// PR. (because it's hard to review) infects_to: Vec, /// Only **string** properties. - pub(crate) accessed_props: Box>, + pub(crate) accessed_props: Box>, pub(crate) used_recursively: bool, } @@ -186,10 +192,19 @@ impl VarUsageInfo { } } -impl Storage for ProgramData { +impl<'alloc> Storage<'alloc> for ProgramData<'alloc> { type ScopeData = ScopeData; type VarData = VarUsageInfo; + fn new(alloc: &'alloc Arena) -> Self { + Self { + vars: arena::HashMap::with_hasher_in(FxBuildHasher::default(), alloc), + top: ScopeData::default(), + scopes: arena::HashMap::with_hasher_in(FxBuildHasher::default(), alloc), + initialized_vars: IndexSet::default(), + } + } + fn scope(&mut self, ctxt: swc_common::SyntaxContext) -> &mut Self::ScopeData { self.scopes.entry(ctxt).or_default() } @@ -491,7 +506,15 @@ impl Storage for ProgramData { } } -impl ScopeDataLike for ScopeData { +impl<'alloc> ScopeDataLike<'alloc> for ScopeData { + fn new(_alloc: &'alloc Arena) -> Self { + Self { + has_with_stmt: false, + has_eval_call: false, + used_arguments: false, + } + } + fn add_declared_symbol(&mut self, _: &Ident) {} fn merge(&mut self, other: Self, _: bool) { @@ -513,7 +536,7 @@ impl ScopeDataLike for ScopeData { } } -impl VarDataLike for VarUsageInfo { +impl<'alloc> VarDataLike<'alloc> for VarUsageInfo { fn mark_declared_as_fn_param(&mut self) { self.declared_as_fn_param = true; } @@ -547,7 +570,7 @@ impl VarDataLike for VarUsageInfo { self.indexed_with_dynamic_key = true; } - fn add_accessed_property(&mut self, name: swc_atoms::JsWord) { + fn add_accessed_property(&mut self, name: swc_atoms::Atom) { *self.accessed_props.entry(name).or_default() += 1; } @@ -584,7 +607,7 @@ impl VarDataLike for VarUsageInfo { } } -impl ProgramData { +impl<'alloc> ProgramData<'alloc> { /// This should be used only for conditionals pass. pub(crate) fn contains_unresolved(&self, e: &Expr) -> bool { match e { diff --git a/crates/swc_ecma_usage_analyzer/Cargo.toml b/crates/swc_ecma_usage_analyzer/Cargo.toml index 92d49c652124..49897d24e1fe 100644 --- a/crates/swc_ecma_usage_analyzer/Cargo.toml +++ b/crates/swc_ecma_usage_analyzer/Cargo.toml @@ -27,6 +27,7 @@ indexmap = { workspace = true } rustc-hash = { workspace = true } tracing = { workspace = true } +swc_allocator = { version = "3.0.1", path = "../swc_allocator", default-features = false } swc_atoms = { version = "4.0.0", path = "../swc_atoms" } swc_common = { version = "7.0.0", path = "../swc_common" } swc_ecma_ast = { version = "7.0.0", path = "../swc_ecma_ast" } diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/ctx.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/ctx.rs index dae2dc1c5414..5594c3544cc0 100644 --- a/crates/swc_ecma_usage_analyzer/src/analyzer/ctx.rs +++ b/crates/swc_ecma_usage_analyzer/src/analyzer/ctx.rs @@ -7,11 +7,11 @@ use swc_ecma_utils::{Type, Value}; use super::{storage::Storage, UsageAnalyzer}; -impl UsageAnalyzer +impl<'alloc, S> UsageAnalyzer<'alloc, S> where - S: Storage, + S: Storage<'alloc>, { - pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx { + pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx<'_, 'alloc, S> { let orig_ctx = self.ctx; self.ctx = ctx; WithCtx { @@ -47,37 +47,37 @@ pub struct Ctx { pub is_top_level: bool, } -pub(super) struct WithCtx<'a, S> +pub(super) struct WithCtx<'a, 'alloc, S> where - S: Storage, + S: Storage<'alloc>, { - analyzer: &'a mut UsageAnalyzer, + analyzer: &'a mut UsageAnalyzer<'alloc, S>, orig_ctx: Ctx, } -impl Deref for WithCtx<'_, S> +impl<'alloc, S> Deref for WithCtx<'_, 'alloc, S> where - S: Storage, + S: Storage<'alloc>, { - type Target = UsageAnalyzer; + type Target = UsageAnalyzer<'alloc, S>; fn deref(&self) -> &Self::Target { self.analyzer } } -impl DerefMut for WithCtx<'_, S> +impl<'alloc, S> DerefMut for WithCtx<'_, 'alloc, S> where - S: Storage, + S: Storage<'alloc>, { fn deref_mut(&mut self) -> &mut Self::Target { self.analyzer } } -impl Drop for WithCtx<'_, S> +impl<'alloc, S> Drop for WithCtx<'_, 'alloc, S> where - S: Storage, + S: Storage<'alloc>, { fn drop(&mut self) { self.analyzer.ctx = self.orig_ctx; diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs index 71efbb6bb17b..31f25b6fed53 100644 --- a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs +++ b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs @@ -1,4 +1,5 @@ use rustc_hash::FxHashMap; +use swc_allocator::allocators::Arena; use swc_common::SyntaxContext; use swc_ecma_ast::*; use swc_ecma_utils::{ @@ -22,17 +23,18 @@ pub mod storage; /// TODO: Scope-local. (Including block) /// /// If `marks` is [None], markers are ignored. -pub fn analyze_with_storage(n: &N, marks: Option) -> S +pub fn analyze_with_storage<'alloc, S, N>(alloc: &'alloc Arena, n: &N, marks: Option) -> S where - S: Storage, - N: VisitWith>, + S: Storage<'alloc>, + N: VisitWith>, { let _timer = timer!("analyze"); let mut v = UsageAnalyzer { - data: Default::default(), + alloc, + data: S::new(alloc), marks, - scope: Default::default(), + scope: S::ScopeData::new(alloc), ctx: Default::default(), expr_ctx: ExprCtx { unresolved_ctxt: SyntaxContext::empty() @@ -65,11 +67,11 @@ enum RecursiveUsage { } /// This assumes there are no two variable with same name and same span hygiene. -#[derive(Debug)] -pub struct UsageAnalyzer +pub struct UsageAnalyzer<'alloc, S> where - S: Storage, + S: Storage<'alloc>, { + alloc: &'alloc Arena, data: S, marks: Option, scope: S::ScopeData, @@ -78,23 +80,24 @@ where used_recursively: FxHashMap, } -impl UsageAnalyzer +impl<'alloc, S> UsageAnalyzer<'alloc, S> where - S: Storage, + S: Storage<'alloc>, { fn with_child(&mut self, child_ctxt: SyntaxContext, kind: ScopeKind, op: F) -> Ret where - F: FnOnce(&mut UsageAnalyzer) -> Ret, + F: FnOnce(&mut UsageAnalyzer<'alloc, S>) -> Ret, { let mut child = UsageAnalyzer { - data: Default::default(), + alloc: self.alloc, + data: S::new(self.alloc), marks: self.marks, ctx: Ctx { is_top_level: false, ..self.ctx }, expr_ctx: self.expr_ctx, - scope: Default::default(), + scope: S::ScopeData::new(self.alloc), used_recursively: self.used_recursively.clone(), }; @@ -221,9 +224,9 @@ where } } -impl Visit for UsageAnalyzer +impl<'alloc, S> Visit for UsageAnalyzer<'alloc, S> where - S: Storage, + S: Storage<'alloc>, { noop_visit_type!(); diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs index 49fea01d998b..73e67c98d3d3 100644 --- a/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs +++ b/crates/swc_ecma_usage_analyzer/src/analyzer/storage.rs @@ -1,3 +1,5 @@ +use swc_atoms::Atom; +use swc_allocator::allocators::Arena; use swc_atoms::JsWord; use swc_common::SyntaxContext; use swc_ecma_ast::*; @@ -6,9 +8,11 @@ use swc_ecma_utils::{Type, Value}; use super::{ctx::Ctx, ScopeKind}; use crate::alias::Access; -pub trait Storage: Sized + Default { - type ScopeData: ScopeDataLike; - type VarData: VarDataLike; +pub trait Storage<'alloc>: Sized { + type ScopeData: ScopeDataLike<'alloc>; + type VarData: VarDataLike<'alloc>; + + fn new(alloc: &'alloc Arena) -> Self; fn scope(&mut self, ctxt: SyntaxContext) -> &mut Self::ScopeData; @@ -36,7 +40,9 @@ pub trait Storage: Sized + Default { fn mark_property_mutation(&mut self, id: Id); } -pub trait ScopeDataLike: Sized + Default + Clone { +pub trait ScopeDataLike<'alloc>: Sized + Clone { + fn new(alloc: &'alloc Arena) -> Self; + fn add_declared_symbol(&mut self, id: &Ident); fn merge(&mut self, other: Self, is_child: bool); @@ -48,7 +54,7 @@ pub trait ScopeDataLike: Sized + Default + Clone { fn mark_with_stmt(&mut self); } -pub trait VarDataLike: Sized { +pub trait VarDataLike<'alloc>: Sized { /// See `declared_as_fn_param` of [crate::analyzer::VarUsageInfo]. fn mark_declared_as_fn_param(&mut self);