diff --git a/.changeset/lovely-snakes-wink.md b/.changeset/lovely-snakes-wink.md new file mode 100644 index 000000000000..4ae050f43731 --- /dev/null +++ b/.changeset/lovely-snakes-wink.md @@ -0,0 +1,5 @@ +ยก--- +swc_ecma_usage_analyzer: major +--- + +perf(es/minifier): Make analyzer not call `collect_infects_from` recursively diff --git a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs index ed383632ef6c..c312f05720c6 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs @@ -685,11 +685,9 @@ impl Optimizer<'_> { for i in collect_infects_from( &f.function, - AliasConfig { - marks: Some(self.marks), - ignore_nested: false, - need_all: true, - }, + AliasConfig::default() + .marks(Some(self.marks)) + .need_all(true), ) { if let Some(usage) = self.data.vars.get_mut(&i.0) { usage.ref_count += 1; diff --git a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs index a91aea93ac34..e9b5d710cb5c 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs @@ -1150,21 +1150,19 @@ impl Optimizer<'_> { Mergable::Var(a) => a.init.as_ref().map(|init| { collect_infects_from( init, - AliasConfig { - marks: Some(self.marks), - ignore_nested: true, - need_all: true, - }, + AliasConfig::default() + .marks(Some(self.marks)) + .ignore_nested(true) + .need_all(true), ) }), Mergable::Expr(a) => match a { Expr::Assign(a) if a.is_simple_assign() => Some(collect_infects_from( &a.right, - AliasConfig { - marks: Some(self.marks), - ignore_nested: true, - need_all: true, - }, + AliasConfig::default() + .marks(Some(self.marks)) + .ignore_nested(true) + .need_all(true), )), _ => None, @@ -1172,11 +1170,10 @@ impl Optimizer<'_> { Mergable::FnDecl(a) => Some(collect_infects_from( &a.function, - AliasConfig { - marks: Some(self.marks), - ignore_nested: true, - need_all: true, - }, + AliasConfig::default() + .marks(Some(self.marks)) + .ignore_nested(true) + .need_all(true), )), Mergable::Drop => return false, diff --git a/crates/swc_ecma_usage_analyzer/src/alias/mod.rs b/crates/swc_ecma_usage_analyzer/src/alias/mod.rs index 29c2d13fa3e9..51b86b53787b 100644 --- a/crates/swc_ecma_usage_analyzer/src/alias/mod.rs +++ b/crates/swc_ecma_usage_analyzer/src/alias/mod.rs @@ -12,12 +12,41 @@ use crate::{marks::Marks, util::is_global_var_with_pure_property_access}; mod ctx; #[derive(Default)] +#[non_exhaustive] pub struct AliasConfig { pub marks: Option, pub ignore_nested: bool, /// TODO(kdy1): This field is used for sequential inliner. /// It should be renamed to some correct name. pub need_all: bool, + + /// We can skip visiting children nodes in some cases. + /// + /// Because we recurse in the usage analyzer, we don't need to recurse into + /// child node that the usage analyzer will invoke [`collect_infects_from`] + /// on. + pub ignore_named_child_scope: bool, +} +impl AliasConfig { + pub fn marks(mut self, arg: Option) -> Self { + self.marks = arg; + self + } + + pub fn ignore_nested(mut self, arg: bool) -> Self { + self.ignore_nested = arg; + self + } + + pub fn ignore_named_child_scope(mut self, arg: bool) -> Self { + self.ignore_named_child_scope = arg; + self + } + + pub fn need_all(mut self, arg: bool) -> Self { + self.need_all = arg; + self + } } pub trait InfectableNode { @@ -99,8 +128,8 @@ pub struct InfectionCollector<'a> { } impl InfectionCollector<'_> { - fn add_id(&mut self, e: &Id) { - if self.exclude.contains(e) { + fn add_id(&mut self, e: Id) { + if self.exclude.contains(&e) { return; } @@ -109,7 +138,7 @@ impl InfectionCollector<'_> { } self.accesses.insert(( - e.clone(), + e, if self.ctx.is_callee { AccessKind::Call } else { @@ -122,6 +151,18 @@ impl InfectionCollector<'_> { impl Visit for InfectionCollector<'_> { noop_visit_type!(); + fn visit_assign_expr(&mut self, n: &AssignExpr) { + if self.config.ignore_named_child_scope + && n.op == op!("=") + && n.left.as_simple().and_then(|l| l.leftmost()).is_some() + { + n.left.visit_with(self); + return; + } + + n.visit_children_with(self); + } + fn visit_bin_expr(&mut self, e: &BinExpr) { match e.op { op!("in") @@ -163,6 +204,14 @@ impl Visit for InfectionCollector<'_> { } } + fn visit_callee(&mut self, n: &Callee) { + let ctx = Ctx { + is_callee: true, + ..self.ctx + }; + n.visit_children_with(&mut *self.with_ctx(ctx)); + } + fn visit_cond_expr(&mut self, e: &CondExpr) { { let ctx = Ctx { @@ -183,15 +232,11 @@ impl Visit for InfectionCollector<'_> { } } - fn visit_ident(&mut self, n: &Ident) { - self.add_id(&n.to_id()); - } - fn visit_expr(&mut self, e: &Expr) { match e { Expr::Ident(i) => { if self.ctx.track_expr_ident { - self.add_id(&i.to_id()); + self.add_id(i.to_id()); } } @@ -205,6 +250,25 @@ impl Visit for InfectionCollector<'_> { } } + fn visit_fn_decl(&mut self, n: &FnDecl) { + if self.config.ignore_named_child_scope { + return; + } + + n.visit_children_with(self); + } + + fn visit_fn_expr(&mut self, n: &FnExpr) { + if self.config.ignore_named_child_scope && n.ident.is_some() { + return; + } + n.visit_children_with(self); + } + + fn visit_ident(&mut self, n: &Ident) { + self.add_id(n.to_id()); + } + fn visit_member_expr(&mut self, n: &MemberExpr) { { let ctx = Ctx { @@ -232,6 +296,15 @@ impl Visit for InfectionCollector<'_> { } } + fn visit_prop_name(&mut self, n: &PropName) { + if let PropName::Computed(c) = &n { + c.visit_with(&mut *self.with_ctx(Ctx { + is_callee: false, + ..self.ctx + })); + } + } + fn visit_super_prop_expr(&mut self, n: &SuperPropExpr) { if let SuperProp::Computed(c) = &n.prop { c.visit_with(&mut *self.with_ctx(Ctx { @@ -277,20 +350,13 @@ impl Visit for InfectionCollector<'_> { e.arg.visit_with(&mut *self.with_ctx(ctx)); } - fn visit_prop_name(&mut self, n: &PropName) { - if let PropName::Computed(c) = &n { - c.visit_with(&mut *self.with_ctx(Ctx { - is_callee: false, - ..self.ctx - })); + fn visit_var_declarator(&mut self, n: &VarDeclarator) { + if self.config.ignore_named_child_scope { + if let (Pat::Ident(..), Some(..)) = (&n.name, n.init.as_deref()) { + return; + } } - } - fn visit_callee(&mut self, n: &Callee) { - let ctx = Ctx { - is_callee: true, - ..self.ctx - }; - n.visit_children_with(&mut *self.with_ctx(ctx)); + n.visit_children_with(self); } } diff --git a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs index 9e62aece3afc..8903cc417f21 100644 --- a/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs +++ b/crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs @@ -306,6 +306,7 @@ where &n.right, AliasConfig { marks: self.marks, + ignore_named_child_scope: true, ..Default::default() }, ) { @@ -758,6 +759,7 @@ where &n.function, AliasConfig { marks: self.marks, + ignore_named_child_scope: true, ..Default::default() }, ) { @@ -788,6 +790,7 @@ where &n.function, AliasConfig { marks: self.marks, + ignore_named_child_scope: true, ..Default::default() }, ) { @@ -1285,6 +1288,7 @@ where init, AliasConfig { marks: self.marks, + ignore_named_child_scope: true, ..Default::default() }, ) {