Skip to content

Commit

Permalink
top_level_vars
Browse files Browse the repository at this point in the history
  • Loading branch information
kdy1 committed Jun 18, 2024
1 parent 35b245a commit 643daee
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 31 deletions.
63 changes: 48 additions & 15 deletions crates/turbopack-ecmascript/src/tree_shake/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use swc_core::{
use turbo_tasks::RcStr;

use super::{
util::{ids_captured_by, ids_used_by, ids_used_by_ignoring_nested},
util::{collect_top_level_decls, ids_captured_by, ids_used_by, ids_used_by_ignoring_nested},
Key, TURBOPACK_PART_IMPORT_SOURCE,
};
use crate::magic_identifier;
Expand Down Expand Up @@ -557,6 +557,7 @@ impl DepGraph {
unresolved_ctxt: SyntaxContext,
top_level_ctxt: SyntaxContext,
) -> (Vec<ItemId>, FxHashMap<ItemId, ItemData>) {
let top_level_vars = collect_top_level_decls(module);
let mut exports = vec![];
let mut items = FxHashMap::default();
let mut ids = vec![];
Expand Down Expand Up @@ -701,10 +702,15 @@ impl DepGraph {
&export.decl,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
used_ids.write.insert(default_var.to_id());
let captured_ids =
ids_captured_by(&export.decl, unresolved_ctxt, top_level_ctxt);
let captured_ids = ids_captured_by(
&export.decl,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
let data = ItemData {
read_vars: used_ids.read,
eventual_read_vars: captured_ids.read,
Expand Down Expand Up @@ -741,9 +747,14 @@ impl DepGraph {
&export.expr,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
let captured_ids = ids_captured_by(
&export.expr,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
let captured_ids =
ids_captured_by(&export.expr, unresolved_ctxt, top_level_ctxt);

used_ids.write.insert(default_var.to_id());

Expand Down Expand Up @@ -875,7 +886,12 @@ impl DepGraph {
};
ids.push(id.clone());

let vars = ids_used_by(&f.function, unresolved_ctxt, top_level_ctxt);
let vars = ids_used_by(
&f.function,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
let var_decls = {
let mut v = IndexSet::with_capacity_and_hasher(1, Default::default());
v.insert(f.ident.to_id());
Expand Down Expand Up @@ -905,7 +921,8 @@ impl DepGraph {
};
ids.push(id.clone());

let vars = ids_used_by(&c.class, unresolved_ctxt, top_level_ctxt);
let vars =
ids_used_by(&c.class, unresolved_ctxt, top_level_ctxt, &top_level_vars);
let var_decls = {
let mut v = IndexSet::with_capacity_and_hasher(1, Default::default());
v.insert(c.ident.to_id());
Expand Down Expand Up @@ -941,9 +958,14 @@ impl DepGraph {
&decl.init,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
let eventual_vars = ids_captured_by(
&decl.init,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
let eventual_vars =
ids_captured_by(&decl.init, unresolved_ctxt, top_level_ctxt);

let side_effects = vars.found_unresolved;

Expand Down Expand Up @@ -972,9 +994,14 @@ impl DepGraph {
expr: box Expr::Assign(assign),
..
})) => {
let mut used_ids =
ids_used_by_ignoring_nested(item, unresolved_ctxt, top_level_ctxt);
let captured_ids = ids_captured_by(item, unresolved_ctxt, top_level_ctxt);
let mut used_ids = ids_used_by_ignoring_nested(
item,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
let captured_ids =
ids_captured_by(item, unresolved_ctxt, top_level_ctxt, &top_level_vars);

if assign.op != op!("=") {
used_ids.read.extend(used_ids.write.iter().cloned());
Expand All @@ -983,6 +1010,7 @@ impl DepGraph {
&assign.left,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
used_ids.read.extend(extra_ids.read);
used_ids.write.extend(extra_ids.write);
Expand Down Expand Up @@ -1017,9 +1045,14 @@ impl DepGraph {
_ => {
// Default to normal

let used_ids =
ids_used_by_ignoring_nested(item, unresolved_ctxt, top_level_ctxt);
let captured_ids = ids_captured_by(item, unresolved_ctxt, top_level_ctxt);
let used_ids = ids_used_by_ignoring_nested(
item,
unresolved_ctxt,
top_level_ctxt,
&top_level_vars,
);
let captured_ids =
ids_captured_by(item, unresolved_ctxt, top_level_ctxt, &top_level_vars);
let data = ItemData {
read_vars: used_ids.read,
eventual_read_vars: captured_ids.read,
Expand Down
160 changes: 144 additions & 16 deletions crates/turbopack-ecmascript/src/tree_shake/util.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
use std::hash::BuildHasherDefault;

use indexmap::IndexSet;
use rustc_hash::FxHasher;
use rustc_hash::{FxHashSet, FxHasher};
use swc_core::{
common::SyntaxContext,
ecma::{
ast::{
AssignTarget, BlockStmtOrExpr, Constructor, ExportNamedSpecifier, ExportSpecifier,
Expr, Function, Id, Ident, MemberExpr, MemberProp, NamedExport, Pat, PropName,
ArrowExpr, AssignPatProp, AssignTarget, BlockStmtOrExpr, ClassDecl, ClassExpr,
Constructor, DefaultDecl, ExportDefaultDecl, ExportNamedSpecifier, ExportSpecifier,
Expr, FnDecl, FnExpr, Function, Id, Ident, ImportSpecifier, MemberExpr, MemberProp,
NamedExport, Param, Pat, PropName, VarDeclarator,
},
visit::{noop_visit_type, Visit, VisitWith},
},
Expand All @@ -27,10 +29,11 @@ struct Target {
}

/// A visitor which collects variables which are read or written.
#[derive(Default)]
pub(crate) struct IdentUsageCollector {
pub(crate) struct IdentUsageCollector<'a> {
unresolved: SyntaxContext,
top_level: SyntaxContext,
top_level_vars: &'a FxHashSet<Id>,

vars: Vars,

is_nested: bool,
Expand All @@ -39,7 +42,7 @@ pub(crate) struct IdentUsageCollector {
mode: Option<Mode>,
}

impl IdentUsageCollector {
impl IdentUsageCollector<'_> {
fn with_nested(&mut self, f: impl FnOnce(&mut Self)) {
if !self.target.eventual {
return;
Expand All @@ -58,7 +61,7 @@ impl IdentUsageCollector {
}
}

impl Visit for IdentUsageCollector {
impl Visit for IdentUsageCollector<'_> {
fn visit_assign_target(&mut self, n: &AssignTarget) {
self.with_mode(Some(Mode::Write), |this| {
n.visit_children_with(this);
Expand Down Expand Up @@ -114,6 +117,7 @@ impl Visit for IdentUsageCollector {
if n.span.ctxt != self.unresolved
&& n.span.ctxt != self.top_level
&& n.span.ctxt != SyntaxContext::empty()
&& !self.top_level_vars.contains(&n.to_id())
{
return;
}
Expand Down Expand Up @@ -186,18 +190,26 @@ pub(crate) struct Vars {
///
/// Note: This functions accept `SyntaxContext` to filter out variables which
/// are not interesting. We only need to analyze top-level variables.
pub(crate) fn ids_captured_by<N>(n: &N, unresolved: SyntaxContext, top_level: SyntaxContext) -> Vars
pub(crate) fn ids_captured_by<'a, N>(
n: &N,
unresolved: SyntaxContext,
top_level: SyntaxContext,
top_level_vars: &'a FxHashSet<Id>,
) -> Vars
where
N: VisitWith<IdentUsageCollector>,
N: VisitWith<IdentUsageCollector<'a>>,
{
let mut v = IdentUsageCollector {
unresolved,
top_level,
top_level_vars,
target: Target {
direct: false,
eventual: true,
},
..Default::default()
vars: Vars::default(),
is_nested: false,
mode: None,
};
n.visit_with(&mut v);
v.vars
Expand All @@ -207,18 +219,26 @@ where
///
/// Note: This functions accept `SyntaxContext` to filter out variables which
/// are not interesting. We only need to analyze top-level variables.
pub(crate) fn ids_used_by<N>(n: &N, unresolved: SyntaxContext, top_level: SyntaxContext) -> Vars
pub(crate) fn ids_used_by<'a, N>(
n: &N,
unresolved: SyntaxContext,
top_level: SyntaxContext,
top_level_vars: &'a FxHashSet<Id>,
) -> Vars
where
N: VisitWith<IdentUsageCollector>,
N: VisitWith<IdentUsageCollector<'a>>,
{
let mut v = IdentUsageCollector {
unresolved,
top_level,
top_level_vars,
target: Target {
direct: true,
eventual: true,
},
..Default::default()
vars: Vars::default(),
is_nested: false,
mode: None,
};
n.visit_with(&mut v);
v.vars
Expand All @@ -228,23 +248,131 @@ where
///
/// Note: This functions accept `SyntaxContext` to filter out variables which
/// are not interesting. We only need to analyze top-level variables.
pub(crate) fn ids_used_by_ignoring_nested<N>(
pub(crate) fn ids_used_by_ignoring_nested<'a, N>(
n: &N,
unresolved: SyntaxContext,
top_level: SyntaxContext,
top_level_vars: &'a FxHashSet<Id>,
) -> Vars
where
N: VisitWith<IdentUsageCollector>,
N: VisitWith<IdentUsageCollector<'a>>,
{
let mut v = IdentUsageCollector {
unresolved,
top_level,
top_level_vars,
target: Target {
direct: true,
eventual: false,
},
..Default::default()
vars: Vars::default(),
is_nested: false,
mode: None,
};
n.visit_with(&mut v);
v.vars
}

pub struct TopLevelBindingCollector {
bindings: FxHashSet<Id>,
is_pat_decl: bool,
}

impl TopLevelBindingCollector {
fn add(&mut self, i: &Ident) {
self.bindings.insert(i.to_id());
}
}

impl Visit for TopLevelBindingCollector {
noop_visit_type!();

fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}

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

if self.is_pat_decl {
self.add(&node.key);
}
}

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

fn visit_expr(&mut self, _: &Expr) {}

fn visit_export_default_decl(&mut self, e: &ExportDefaultDecl) {
match &e.decl {
DefaultDecl::Class(ClassExpr {
ident: Some(ident), ..
}) => {
self.add(ident);
}
DefaultDecl::Fn(FnExpr {
ident: Some(ident),
function: f,
}) if f.body.is_some() => {
self.add(ident);
}
_ => {}
}
}

fn visit_fn_decl(&mut self, node: &FnDecl) {
self.add(&node.ident);
}

fn visit_import_specifier(&mut self, node: &ImportSpecifier) {
match node {
ImportSpecifier::Named(s) => self.add(&s.local),
ImportSpecifier::Default(s) => {
self.add(&s.local);
}
ImportSpecifier::Namespace(s) => {
self.add(&s.local);
}
}
}

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

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

if self.is_pat_decl {
if let Pat::Ident(i) = node {
self.add(&i.id)
}
}
}

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

self.is_pat_decl = false;
node.init.visit_with(self);
self.is_pat_decl = old;
}
}

/// Collects binding identifiers.
pub fn collect_top_level_decls<N>(n: &N) -> FxHashSet<Id>
where
N: VisitWith<TopLevelBindingCollector>,
{
let mut v = TopLevelBindingCollector {
bindings: Default::default(),
is_pat_decl: false,
};
n.visit_with(&mut v);
v.bindings
}

0 comments on commit 643daee

Please sign in to comment.