Skip to content

Commit f7a6df1

Browse files
committed
hoist var decl
1 parent 1c49ef5 commit f7a6df1

File tree

6 files changed

+187
-29
lines changed

6 files changed

+187
-29
lines changed

crates/swc_ecma_ast/src/expr.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1575,12 +1575,10 @@ impl TryFrom<Box<Expr>> for SimpleAssignTarget {
15751575
bridge_from!(SimpleAssignTarget, BindingIdent, Ident);
15761576

15771577
impl SimpleAssignTarget {
1578-
pub fn leftmost(&self) -> Option<Cow<Ident>> {
1578+
pub fn leftmost(&self) -> Option<&Ident> {
15791579
match self {
1580-
SimpleAssignTarget::Ident(i) => {
1581-
Some(Cow::Owned(Ident::new(i.sym.clone(), i.span, i.ctxt)))
1582-
}
1583-
SimpleAssignTarget::Member(MemberExpr { obj, .. }) => obj.leftmost().map(Cow::Borrowed),
1580+
SimpleAssignTarget::Ident(i) => Some(&i.id),
1581+
SimpleAssignTarget::Member(MemberExpr { obj, .. }) => obj.leftmost(),
15841582
_ => None,
15851583
}
15861584
}

crates/swc_ecma_minifier/src/compress/optimize/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ struct Optimizer<'a> {
241241
ctx: Ctx,
242242

243243
mode: &'a dyn Mode,
244-
r: &'a Resolver,
244+
r: &'a mut Resolver,
245245

246246
functions: Box<FxHashMap<Id, FnMetadata>>,
247247
}

crates/swc_ecma_minifier/src/compress/optimize/props.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use swc_common::{util::take::Take, DUMMY_SP};
22
use swc_ecma_ast::*;
3+
use swc_ecma_transforms_base::resolve::RefTo;
34
use swc_ecma_utils::{contains_this_expr, private_ident, prop_name_eq, ExprExt};
45

56
use super::{unused::PropertyAccessOpts, BitCtx, Optimizer};
@@ -164,18 +165,25 @@ impl Optimizer<'_> {
164165
_ => unreachable!(),
165166
};
166167

167-
let new_var_name = private_ident!(format!("{}_{}", name.id.sym, suffix));
168+
let mut new_var_name_ident = private_ident!(format!("{}_{}", name.id.sym, suffix));
169+
let new_var_decl_name_id = name.id.node_id;
170+
self.r
171+
.add_reference_map(&mut new_var_name_ident, new_var_decl_name_id);
168172

169173
let new_var = VarDeclarator {
170174
span: DUMMY_SP,
171-
name: new_var_name.clone().into(),
175+
name: {
176+
let mut n = new_var_name_ident.clone();
177+
n.node_id = new_var_decl_name_id;
178+
n.into()
179+
},
172180
init: Some(value),
173181
definite: false,
174182
};
175183

176184
self.vars
177185
.hoisted_props
178-
.insert((name.node_id, key), new_var_name);
186+
.insert((name.node_id, key), new_var_name_ident);
179187

180188
new_vars.push(new_var);
181189
}
@@ -207,10 +215,16 @@ impl Optimizer<'_> {
207215
_ => return,
208216
};
209217

218+
let node_id = match self.r.find_binding_by_ident(obj) {
219+
RefTo::Binding(id) => id,
220+
RefTo::Unresolved => return,
221+
RefTo::Itself => unreachable!(),
222+
};
223+
debug_assert!(node_id != obj.node_id);
210224
if let Some(value) = self
211225
.vars
212226
.hoisted_props
213-
.get(&(obj.node_id, sym.clone()))
227+
.get(&(node_id, sym.clone()))
214228
.cloned()
215229
{
216230
report_change!("hoist_props: Inlining `{}.{}`", obj.sym, sym);

crates/swc_ecma_transforms_base/src/resolve/mod.rs

Lines changed: 133 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use self::{
1111
reference::ReferenceMap,
1212
scope::{ScopeArena, ScopeId},
1313
};
14+
use crate::resolve::scope::ScopeKind;
1415

1516
#[derive(Debug)]
1617
pub struct Resolver {
@@ -20,8 +21,6 @@ pub struct Resolver {
2021
current_scope_id: ScopeId,
2122

2223
current_node_id: u32,
23-
24-
in_binding: bool,
2524
}
2625

2726
pub fn name_resolution(root: &mut impl VisitMutWith<Resolver>) -> Resolver {
@@ -35,8 +34,6 @@ pub fn name_resolution(root: &mut impl VisitMutWith<Resolver>) -> Resolver {
3534

3635
scopes,
3736
current_scope_id,
38-
39-
in_binding: false,
4037
};
4138

4239
root.visit_mut_with(&mut resolver);
@@ -52,6 +49,13 @@ impl Resolver {
5249
pub fn find_binding_by_ident(&self, ident: &Ident) -> RefTo {
5350
self.references.get_binding(ident.node_id)
5451
}
52+
53+
pub fn add_reference_map(&mut self, from: &mut Ident, to: NodeId) {
54+
debug_assert!(from.node_id == NodeId::DUMMY);
55+
debug_assert!(to != NodeId::DUMMY);
56+
from.node_id = self.next_node_id();
57+
self.references.add_reference(from.node_id, to);
58+
}
5559
}
5660

5761
impl Resolver {
@@ -61,16 +65,21 @@ impl Resolver {
6165
ret
6266
}
6367

64-
fn add_binding(&mut self, node: &mut Ident) {
65-
let id = self.next_node_id();
66-
debug_assert!(node.node_id == NodeId::DUMMY);
67-
node.node_id = id;
68+
fn add_binding(&mut self, id: NodeId, sym: Atom) {
69+
debug_assert!(id != NodeId::DUMMY);
6870
self.scopes
6971
.get_mut(self.current_scope_id)
70-
.add_binding(node.sym.clone(), id);
72+
.add_binding(sym, id);
7173
self.references.add_binding(id);
7274
}
7375

76+
fn add_binding_for_ident(&mut self, node: &mut Ident) {
77+
let id = self.next_node_id();
78+
debug_assert!(node.node_id == NodeId::DUMMY);
79+
node.node_id = id;
80+
self.add_binding(id, node.sym.clone());
81+
}
82+
7483
fn add_reference(&mut self, node: &mut Ident, to: NodeId) {
7584
let id = self.next_node_id();
7685
debug_assert!(node.node_id == NodeId::DUMMY);
@@ -98,15 +107,85 @@ impl Resolver {
98107
scope_id = parent;
99108
}
100109
}
110+
111+
fn with_new_scope(&mut self, kind: ScopeKind, f: impl FnOnce(&mut Self)) {
112+
let saved_scope_id = self.current_scope_id;
113+
self.current_scope_id = self.scopes.alloc_new_scope(self.current_scope_id, kind);
114+
f(self);
115+
self.current_scope_id = saved_scope_id;
116+
}
117+
118+
fn visit_pat_with_binding(&mut self, pat: &mut Pat, is_var: bool) {
119+
let hoist = |this: &mut Self, atom: &Atom, id: NodeId| {
120+
if !is_var {
121+
return;
122+
}
123+
let mut scope_id = this.current_scope_id;
124+
loop {
125+
let Some(parent) = this.scopes.get(scope_id).parent() else {
126+
return;
127+
};
128+
129+
let s = this.scopes.get_mut(parent);
130+
s.add_binding(atom.clone(), id);
131+
if !matches!(s.kind(), ScopeKind::Block) {
132+
return;
133+
}
134+
scope_id = parent;
135+
}
136+
};
137+
match pat {
138+
Pat::Ident(n) => {
139+
self.add_binding_for_ident(n);
140+
hoist(self, &n.sym, n.node_id);
141+
}
142+
Pat::Array(n) => {
143+
for elem in n.elems.iter_mut() {
144+
if let Some(elem) = elem {
145+
self.visit_pat_with_binding(elem, is_var);
146+
}
147+
}
148+
}
149+
Pat::Rest(n) => {
150+
self.visit_pat_with_binding(&mut n.arg, is_var);
151+
}
152+
Pat::Object(n) => {
153+
for prop in n.props.iter_mut() {
154+
match prop {
155+
ObjectPatProp::KeyValue(p) => {
156+
self.visit_pat_with_binding(&mut p.value, is_var);
157+
}
158+
ObjectPatProp::Assign(p) => {
159+
self.add_binding_for_ident(&mut p.key.id);
160+
hoist(self, &p.key.sym, p.key.node_id);
161+
p.value.visit_mut_children_with(self);
162+
}
163+
ObjectPatProp::Rest(p) => {
164+
self.visit_pat_with_binding(&mut p.arg, is_var);
165+
}
166+
}
167+
}
168+
}
169+
Pat::Assign(n) => {
170+
// TODO:
171+
self.visit_pat_with_binding(&mut n.left, is_var);
172+
n.right.visit_mut_children_with(self);
173+
}
174+
Pat::Invalid(n) => {
175+
// TODO:
176+
}
177+
Pat::Expr(n) => {
178+
// TODO:
179+
}
180+
}
181+
}
101182
}
102183

103184
impl VisitMut for Resolver {
104185
noop_visit_mut_type!();
105186

106187
fn visit_mut_ident(&mut self, node: &mut Ident) {
107-
if self.in_binding {
108-
self.add_binding(node);
109-
} else if let Some(reference) = self.lookup_binding(&node.sym, self.current_scope_id) {
188+
if let Some(reference) = self.lookup_binding(&node.sym, self.current_scope_id) {
110189
self.add_reference(node, reference);
111190
} else {
112191
// TODO: unnecessary to mark all ident to unresolved,
@@ -115,13 +194,49 @@ impl VisitMut for Resolver {
115194
}
116195
}
117196

118-
fn visit_mut_var_declarator(&mut self, node: &mut VarDeclarator) {
119-
let saved_in_binding = self.in_binding;
197+
fn visit_mut_var_decl(&mut self, node: &mut VarDecl) {
198+
for decl in &mut node.decls {
199+
self.visit_pat_with_binding(&mut decl.name, node.kind == VarDeclKind::Var);
200+
decl.init.visit_mut_children_with(self);
201+
}
202+
}
120203

121-
self.in_binding = true;
122-
node.name.visit_mut_children_with(self);
123-
self.in_binding = saved_in_binding;
204+
fn visit_mut_block_stmt(&mut self, node: &mut BlockStmt) {
205+
self.with_new_scope(ScopeKind::Block, |this| {
206+
node.visit_mut_children_with(this);
207+
});
208+
}
209+
210+
fn visit_mut_fn_decl(&mut self, node: &mut FnDecl) {
211+
self.add_binding_for_ident(&mut node.ident);
212+
213+
self.with_new_scope(ScopeKind::Fn, |this| {
214+
node.function.visit_mut_children_with(this);
215+
});
216+
}
217+
218+
fn visit_mut_fn_expr(&mut self, node: &mut FnExpr) {
219+
self.with_new_scope(ScopeKind::Fn, |this| {
220+
if let Some(ident) = &mut node.ident {
221+
this.add_binding_for_ident(ident);
222+
}
223+
node.function.visit_mut_children_with(this);
224+
});
225+
}
226+
227+
fn visit_mut_arrow_expr(&mut self, node: &mut ArrowExpr) {
228+
self.with_new_scope(ScopeKind::Fn, |this| {
229+
for param in &mut node.params {
230+
this.visit_pat_with_binding(param, false);
231+
}
232+
node.body.visit_mut_children_with(this);
233+
});
234+
}
124235

125-
node.init.visit_mut_children_with(self);
236+
fn visit_mut_class_decl(&mut self, node: &mut ClassDecl) {
237+
self.add_binding_for_ident(&mut node.ident);
238+
self.with_new_scope(ScopeKind::Class, |this| {
239+
node.class.visit_mut_children_with(this);
240+
});
126241
}
127242
}

crates/swc_ecma_transforms_base/src/resolve/scope.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ use swc_common::NodeId;
66
pub(super) struct Scope {
77
parent: Option<ScopeId>,
88
bindings: IndexMap<Atom, NodeId>,
9+
kind: ScopeKind,
10+
}
11+
12+
#[derive(Debug, Default, Clone, Copy)]
13+
pub(super) enum ScopeKind {
14+
#[default]
15+
Block,
16+
Fn,
17+
Class,
918
}
1019

1120
impl Scope {
@@ -20,6 +29,10 @@ impl Scope {
2029
pub(super) fn parent(&self) -> Option<ScopeId> {
2130
self.parent
2231
}
32+
33+
pub(super) fn kind(&self) -> ScopeKind {
34+
self.kind
35+
}
2336
}
2437

2538
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -39,6 +52,17 @@ impl ScopeArena {
3952
ScopeId::ROOT
4053
}
4154

55+
pub(super) fn alloc_new_scope(&mut self, parent: ScopeId, kind: ScopeKind) -> ScopeId {
56+
debug_assert!(parent.0 < self.0.len() as u32);
57+
let id = ScopeId(self.0.len() as u32);
58+
self.0.push(Scope {
59+
parent: Some(parent),
60+
bindings: IndexMap::new(),
61+
kind,
62+
});
63+
id
64+
}
65+
4266
pub(super) fn get(&self, id: ScopeId) -> &Scope {
4367
debug_assert!(id.0 < self.0.len() as u32);
4468
unsafe { self.0.get_unchecked(id.0 as usize) }

crates/swc_ecma_usage_analyzer/src/analyzer/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,13 @@ where
438438

439439
if let Callee::Expr(callee) = &n.callee {
440440
for_each_id_ref_in_expr(callee, &mut |i| {
441-
self.data.var_or_default(i.node_id).mark_used_as_callee();
441+
let node_id = match self.r.find_binding_by_ident(i) {
442+
RefTo::Binding(node_id) => node_id,
443+
RefTo::Unresolved => return,
444+
RefTo::Itself => unreachable!(),
445+
};
446+
debug_assert!(node_id != i.node_id);
447+
self.data.var_or_default(node_id).mark_used_as_callee();
442448
});
443449

444450
match &**callee {
@@ -1045,6 +1051,7 @@ where
10451051
RefTo::Unresolved => return,
10461052
RefTo::Itself => unreachable!(),
10471053
};
1054+
debug_assert!(node_id != obj.node_id);
10481055
let v = self.data.var_or_default(node_id);
10491056
v.mark_has_property_access();
10501057

0 commit comments

Comments
 (0)