Skip to content

Commit

Permalink
perf(es/minifier): Merge binding analyzer into infection analyzer (#9973
Browse files Browse the repository at this point in the history
)
  • Loading branch information
kdy1 authored Jan 29, 2025
1 parent 1b57261 commit ca8a71f
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 26 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-geese-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
swc_ecma_usage_analyzer: major
---

perf(es/minifier): Improve time complexity of usage analyzer
19 changes: 10 additions & 9 deletions crates/swc_ecma_usage_analyzer/src/alias/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use std::ops::{Deref, DerefMut};

use super::InfectionCollector;

impl<'a> InfectionCollector<'a> {
pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx<'_, 'a> {
impl InfectionCollector {
pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx {
let orig_ctx = self.ctx;
self.ctx = ctx;

Expand All @@ -15,31 +15,32 @@ impl<'a> InfectionCollector<'a> {
}

#[derive(Debug, Default, Clone, Copy)]
pub struct Ctx {
pub(crate) struct Ctx {
pub track_expr_ident: bool,
pub is_callee: bool,
pub is_pat_decl: bool,
}

pub(super) struct WithCtx<'a, 'b> {
analyzer: &'a mut InfectionCollector<'b>,
pub(super) struct WithCtx<'a> {
analyzer: &'a mut InfectionCollector,
orig_ctx: Ctx,
}

impl<'b> Deref for WithCtx<'_, 'b> {
type Target = InfectionCollector<'b>;
impl Deref for WithCtx<'_> {
type Target = InfectionCollector;

fn deref(&self) -> &Self::Target {
self.analyzer
}
}

impl DerefMut for WithCtx<'_, '_> {
impl DerefMut for WithCtx<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.analyzer
}
}

impl Drop for WithCtx<'_, '_> {
impl Drop for WithCtx<'_> {
fn drop(&mut self) {
self.analyzer.ctx = self.orig_ctx;
}
Expand Down
97 changes: 80 additions & 17 deletions crates/swc_ecma_usage_analyzer/src/alias/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#![allow(clippy::needless_update)]

use rustc_hash::FxHashSet;
use swc_common::{collections::AHashSet, SyntaxContext};
use swc_common::SyntaxContext;
use swc_ecma_ast::*;
use swc_ecma_utils::{collect_decls, BindingCollector};
use swc_ecma_utils::BindingCollector;
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};

pub use self::ctx::Ctx;
use self::ctx::Ctx;
use crate::{marks::Marks, util::is_global_var_with_pure_property_access};

mod ctx;
Expand Down Expand Up @@ -85,9 +85,7 @@ pub type Access = (Id, AccessKind);

pub fn collect_infects_from<N>(node: &N, config: AliasConfig) -> FxHashSet<Access>
where
N: InfectableNode
+ VisitWith<BindingCollector<Id>>
+ for<'aa> VisitWith<InfectionCollector<'aa>>,
N: InfectableNode + VisitWith<BindingCollector<Id>> + VisitWith<InfectionCollector>,
{
if config.ignore_nested && node.is_fn_or_arrow_expr() {
return Default::default();
Expand All @@ -96,17 +94,17 @@ where
let unresolved_ctxt = config
.marks
.map(|m| SyntaxContext::empty().apply_mark(m.unresolved_mark));
let decls = collect_decls(node);

let mut visitor = InfectionCollector {
config,
unresolved_ctxt,

exclude: &decls,
ctx: Ctx {
track_expr_ident: true,
..Default::default()
},

bindings: FxHashSet::default(),
accesses: FxHashSet::default(),
};

Expand All @@ -115,21 +113,28 @@ where
visitor.accesses
}

pub struct InfectionCollector<'a> {
pub struct InfectionCollector {
#[allow(unused)]
config: AliasConfig,
unresolved_ctxt: Option<SyntaxContext>,

exclude: &'a AHashSet<Id>,
bindings: FxHashSet<Id>,

ctx: Ctx,

accesses: FxHashSet<Access>,
}

impl InfectionCollector<'_> {
fn add_id(&mut self, e: Id) {
if self.exclude.contains(&e) {
impl InfectionCollector {
fn add_binding(&mut self, e: &Ident) {
if self.bindings.insert(e.to_id()) {
self.accesses.remove(&(e.to_id(), AccessKind::Reference));
self.accesses.remove(&(e.to_id(), AccessKind::Call));
}
}

fn add_usage(&mut self, e: Id) {
if self.bindings.contains(&e) {
return;
}

Expand All @@ -148,9 +153,21 @@ impl InfectionCollector<'_> {
}
}

impl Visit for InfectionCollector<'_> {
impl Visit for InfectionCollector {
noop_visit_type!();

fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
let old = self.ctx.is_pat_decl;

for p in &n.params {
self.ctx.is_pat_decl = true;
p.visit_with(self);
}

n.body.visit_with(self);
self.ctx.is_pat_decl = old;
}

fn visit_assign_expr(&mut self, n: &AssignExpr) {
if self.config.ignore_named_child_scope
&& n.op == op!("=")
Expand All @@ -163,6 +180,14 @@ impl Visit for InfectionCollector<'_> {
n.visit_children_with(self);
}

fn visit_assign_pat_prop(&mut self, node: &AssignPatProp) {
node.value.visit_with(self);

if self.ctx.is_pat_decl {
self.add_binding(&node.key.clone().into());
}
}

fn visit_bin_expr(&mut self, e: &BinExpr) {
match e.op {
op!("in")
Expand Down Expand Up @@ -212,6 +237,12 @@ impl Visit for InfectionCollector<'_> {
n.visit_children_with(&mut *self.with_ctx(ctx));
}

fn visit_class_decl(&mut self, node: &ClassDecl) {
self.add_binding(&node.ident);

node.visit_children_with(self);
}

fn visit_cond_expr(&mut self, e: &CondExpr) {
{
let ctx = Ctx {
Expand All @@ -236,13 +267,14 @@ impl Visit for InfectionCollector<'_> {
match e {
Expr::Ident(i) => {
if self.ctx.track_expr_ident {
self.add_id(i.to_id());
self.add_usage(i.to_id());
}
}

_ => {
let ctx = Ctx {
track_expr_ident: true,
is_pat_decl: false,
..self.ctx
};
e.visit_children_with(&mut *self.with_ctx(ctx));
Expand All @@ -251,6 +283,8 @@ impl Visit for InfectionCollector<'_> {
}

fn visit_fn_decl(&mut self, n: &FnDecl) {
self.add_binding(&n.ident);

if self.config.ignore_named_child_scope {
return;
}
Expand All @@ -266,7 +300,7 @@ impl Visit for InfectionCollector<'_> {
}

fn visit_ident(&mut self, n: &Ident) {
self.add_id(n.to_id());
self.add_usage(n.to_id());
}

fn visit_member_expr(&mut self, n: &MemberExpr) {
Expand Down Expand Up @@ -296,6 +330,23 @@ impl Visit for InfectionCollector<'_> {
}
}

fn visit_param(&mut self, node: &Param) {
let old = self.ctx.is_pat_decl;
self.ctx.is_pat_decl = true;
node.visit_children_with(self);
self.ctx.is_pat_decl = old;
}

fn visit_pat(&mut self, node: &Pat) {
node.visit_children_with(self);

if self.ctx.is_pat_decl {
if let Pat::Ident(i) = node {
self.add_binding(i)
}
}
}

fn visit_prop_name(&mut self, n: &PropName) {
if let PropName::Computed(c) = &n {
c.visit_with(&mut *self.with_ctx(Ctx {
Expand Down Expand Up @@ -351,12 +402,24 @@ impl Visit for InfectionCollector<'_> {
}

fn visit_var_declarator(&mut self, n: &VarDeclarator) {
{
let old = self.ctx.is_pat_decl;
self.ctx.is_pat_decl = true;
n.name.visit_with(self);
self.ctx.is_pat_decl = old;
}

if self.config.ignore_named_child_scope {
if let (Pat::Ident(..), Some(..)) = (&n.name, n.init.as_deref()) {
return;
}
}

n.visit_children_with(self);
{
let old = self.ctx.is_pat_decl;
self.ctx.is_pat_decl = false;
n.init.visit_with(self);
self.ctx.is_pat_decl = old;
}
}
}

0 comments on commit ca8a71f

Please sign in to comment.