swc_ecma_minifier/compress/pure/
vars.rs1use rustc_hash::FxHashSet;
2use swc_common::{util::take::Take, DUMMY_SP};
3use swc_ecma_ast::*;
4use swc_ecma_utils::{prepend_stmt, StmtLike};
5use swc_ecma_visit::{
6 noop_visit_mut_type, noop_visit_type, Visit, VisitMut, VisitMutWith, VisitWith,
7};
8
9use super::Pure;
10use crate::{
11 compress::util::{drop_invalid_stmts, is_directive},
12 util::ModuleItemExt,
13};
14
15impl Pure<'_> {
16 pub(super) fn join_vars<T>(&mut self, stmts: &mut Vec<T>)
22 where
23 T: ModuleItemExt,
24 {
25 if !self.options.join_vars {
26 return;
27 }
28
29 {
30 let can_work =
33 stmts
34 .windows(2)
35 .any(|stmts| match (stmts[0].as_stmt(), stmts[1].as_stmt()) {
36 (Some(Stmt::Decl(Decl::Var(l))), Some(r)) => match r {
37 Stmt::Decl(Decl::Var(r)) => l.kind == r.kind,
38 Stmt::For(ForStmt { init: None, .. }) => l.kind == VarDeclKind::Var,
39 Stmt::For(ForStmt {
40 init: Some(VarDeclOrExpr::VarDecl(v)),
41 ..
42 }) if matches!(
43 &**v,
44 VarDecl {
45 kind: VarDeclKind::Var,
46 ..
47 },
48 ) =>
49 {
50 l.kind == VarDeclKind::Var
51 }
52 _ => false,
53 },
54 _ => false,
55 });
56
57 if !can_work {
58 return;
59 }
60 }
61
62 report_change!("join_vars: Joining variables");
63 self.changed = true;
64
65 let mut cur: Option<Box<VarDecl>> = None;
66
67 let mut new: Vec<T> = Vec::with_capacity(stmts.len() * 2 + 1);
68 stmts.take().into_iter().for_each(|stmt| {
69 match stmt.try_into_stmt() {
70 Ok(stmt) => {
71 if is_directive(&stmt) {
72 return new.push(T::from(stmt));
73 }
74
75 match stmt {
76 Stmt::Decl(Decl::Var(var)) => match &mut cur {
77 Some(v) if var.kind == v.kind => {
78 v.decls.extend(var.decls);
79 }
80 _ => {
81 if let Some(s) = cur.take().map(|c| c.into()) {
82 new.push(T::from(s));
83 }
84 cur = Some(var);
85 }
86 },
87 Stmt::For(mut stmt) => match &mut stmt.init {
88 Some(VarDeclOrExpr::VarDecl(var))
89 if matches!(
90 &**var,
91 VarDecl {
92 kind: VarDeclKind::Var,
93 ..
94 }
95 ) =>
96 {
97 match &mut cur {
98 Some(cur) if cur.kind == var.kind => {
99 cur.decls.append(&mut var.decls);
101 var.decls = cur.decls.take();
102
103 new.push(T::from(stmt.into()));
104 }
105 _ => {
106 if let Some(s) = cur.take() {
107 new.push(T::from(s.into()));
108 }
109 new.push(T::from(stmt.into()));
110 }
111 }
112 }
113 None if cur
114 .as_ref()
115 .map(|v| v.kind == VarDeclKind::Var)
116 .unwrap_or(true) =>
117 {
118 stmt.init = cur
119 .take()
120 .and_then(|v| if v.decls.is_empty() { None } else { Some(v) })
121 .map(VarDeclOrExpr::VarDecl);
122
123 new.push(T::from(stmt.into()));
124 }
125 _ => {
126 if let Some(s) = cur.take() {
127 new.push(T::from(s.into()));
128 }
129 new.push(T::from(stmt.into()));
130 }
131 },
132 _ => {
133 if let Some(s) = cur.take() {
134 new.push(T::from(s.into()));
135 }
136 new.push(T::from(stmt));
137 }
138 }
139 }
140 Err(item) => {
141 if let Some(s) = cur.take() {
142 new.push(T::from(s.into()));
143 }
144 new.push(item);
145 }
146 }
147 });
148
149 if let Some(s) = cur.take() {
150 new.push(T::from(s.into()));
151 }
152
153 drop_invalid_stmts(&mut new);
154
155 *stmts = new;
156 }
157
158 pub(super) fn remove_duplicate_vars(&mut self, vars: &mut Vec<VarDeclarator>) {
160 let mut found = FxHashSet::default();
161
162 vars.retain(|v| {
163 if v.init.is_some() {
164 return true;
165 }
166
167 match &v.name {
168 Pat::Ident(i) => found.insert(i.to_id()),
169 _ => true,
170 }
171 })
172 }
173
174 pub(super) fn collapse_vars_without_init<T>(&mut self, stmts: &mut Vec<T>, target: VarDeclKind)
180 where
181 T: StmtLike,
182 Vec<T>:
183 VisitWith<VarWithOutInitCounter> + VisitMutWith<VarMover> + VisitMutWith<VarPrepender>,
184 {
185 if !self.options.collapse_vars {
186 return;
187 }
188
189 {
190 let mut need_work = false;
191 let mut found_vars_without_init = false;
192 let mut found_other = false;
193 let if_need_work = stmts.iter().any(|stmt| {
194 match stmt.as_stmt() {
195 Some(Stmt::Decl(Decl::Var(v))) if v.kind == target => {
196 if !(found_other && found_vars_without_init)
197 && v.decls.iter().all(|v| v.init.is_none())
198 {
199 if found_other {
200 need_work = true;
201 }
202
203 found_vars_without_init = true;
204 } else {
205 if found_vars_without_init && self.options.join_vars {
206 need_work = true;
207 }
208 found_other = true;
209 }
210 }
211
212 Some(Stmt::Expr(s)) => match &*s.expr {
214 Expr::Lit(Lit::Str(..)) => {}
215 _ => {
216 found_other = true;
217 }
218 },
219
220 _ => {
221 found_other = true;
222 }
223 }
224 need_work
225 });
226
227 let visitor_need_work = if target == VarDeclKind::Var {
229 let mut v = VarWithOutInitCounter {
230 target,
231 need_work: Default::default(),
232 found_var_without_init: Default::default(),
233 found_var_with_init: Default::default(),
234 };
235 stmts.visit_with(&mut v);
236 v.need_work
237 } else {
238 false
239 };
240 if !if_need_work && !visitor_need_work {
241 return;
242 }
243 }
244
245 report_change!("collapse_vars: Collapsing variables without an initializer");
248
249 let vars = {
250 let mut v = VarMover {
251 target,
252 vars: Default::default(),
253 var_decl_kind: Default::default(),
254 };
255 stmts.visit_mut_with(&mut v);
256
257 v.vars
258 };
259
260 let mut prepender = VarPrepender { target, vars };
263 if target == VarDeclKind::Var {
264 stmts.visit_mut_with(&mut prepender);
265 }
266
267 if !prepender.vars.is_empty() {
268 match stmts.get_mut(0).and_then(|v| v.as_stmt_mut()) {
269 Some(Stmt::Decl(Decl::Var(v))) if v.kind == target => {
270 prepender.vars.append(&mut v.decls);
271 v.decls = prepender.vars;
272 }
273 _ => {
274 prepend_stmt(
275 stmts,
276 T::from(
277 VarDecl {
278 span: DUMMY_SP,
279 kind: target,
280 declare: Default::default(),
281 decls: prepender.vars,
282 ..Default::default()
283 }
284 .into(),
285 ),
286 );
287 }
288 }
289 }
290 }
291}
292
293pub(super) struct VarWithOutInitCounter {
296 target: VarDeclKind,
297 need_work: bool,
298 found_var_without_init: bool,
299 found_var_with_init: bool,
300}
301
302impl Visit for VarWithOutInitCounter {
303 noop_visit_type!(fail);
304
305 fn visit_arrow_expr(&mut self, _: &ArrowExpr) {}
306
307 fn visit_constructor(&mut self, _: &Constructor) {}
308
309 fn visit_function(&mut self, _: &Function) {}
310
311 fn visit_getter_prop(&mut self, _: &GetterProp) {}
312
313 fn visit_setter_prop(&mut self, _: &SetterProp) {}
314
315 fn visit_var_decl(&mut self, v: &VarDecl) {
316 v.visit_children_with(self);
317
318 if v.kind != self.target {
319 return;
320 }
321
322 let mut found_init = false;
323 for d in &v.decls {
324 if d.init.is_some() {
325 found_init = true;
326 } else {
327 if found_init {
328 self.need_work = true;
329 return;
330 }
331 }
332 }
333
334 if v.decls.iter().all(|v| v.init.is_none()) {
335 if self.found_var_without_init || self.found_var_with_init {
336 self.need_work = true;
337 }
338 self.found_var_without_init = true;
339 } else {
340 self.found_var_with_init = true;
341 }
342 }
343
344 fn visit_module_item(&mut self, s: &ModuleItem) {
345 if let ModuleItem::Stmt(_) = s {
346 s.visit_children_with(self);
347 }
348 }
349
350 fn visit_block_stmt(&mut self, n: &BlockStmt) {
351 if self.target != VarDeclKind::Var {
352 return;
354 }
355
356 n.visit_children_with(self);
357 }
358
359 fn visit_for_head(&mut self, _: &ForHead) {}
360}
361
362pub(super) struct VarMover {
364 target: VarDeclKind,
365
366 vars: Vec<VarDeclarator>,
367 var_decl_kind: Option<VarDeclKind>,
368}
369
370impl VisitMut for VarMover {
371 noop_visit_mut_type!(fail);
372
373 fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {}
375
376 fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) {
377 if self.target != VarDeclKind::Var {
378 return;
380 }
381
382 n.visit_mut_children_with(self);
383 }
384
385 fn visit_mut_constructor(&mut self, _: &mut Constructor) {}
387
388 fn visit_mut_for_head(&mut self, _: &mut ForHead) {}
389
390 fn visit_mut_function(&mut self, _: &mut Function) {}
392
393 fn visit_mut_getter_prop(&mut self, _: &mut GetterProp) {}
394
395 fn visit_mut_module_decl(&mut self, _: &mut ModuleDecl) {}
396
397 fn visit_mut_module_item(&mut self, s: &mut ModuleItem) {
398 if let ModuleItem::Stmt(_) = s {
399 s.visit_mut_children_with(self);
400 }
401 }
402
403 fn visit_mut_opt_var_decl_or_expr(&mut self, n: &mut Option<VarDeclOrExpr>) {
404 n.visit_mut_children_with(self);
405
406 if let Some(VarDeclOrExpr::VarDecl(var)) = n {
407 if var.decls.is_empty() {
408 *n = None;
409 }
410 }
411 }
412
413 fn visit_mut_setter_prop(&mut self, _: &mut SetterProp) {}
414
415 fn visit_mut_stmt(&mut self, s: &mut Stmt) {
416 s.visit_mut_children_with(self);
417
418 match s {
419 Stmt::Decl(Decl::Var(v)) if v.decls.is_empty() => {
420 s.take();
421 }
422 _ => {}
423 }
424 }
425
426 fn visit_mut_stmts(&mut self, s: &mut Vec<Stmt>) {
427 s.visit_mut_children_with(self);
428
429 s.retain(|s| !matches!(s, Stmt::Empty(..)));
430 }
431
432 fn visit_mut_var_decl(&mut self, v: &mut VarDecl) {
433 let old = self.var_decl_kind.take();
434 self.var_decl_kind = Some(v.kind);
435 v.visit_mut_children_with(self);
436 self.var_decl_kind = old;
437 }
438
439 fn visit_mut_var_declarators(&mut self, d: &mut Vec<VarDeclarator>) {
440 d.visit_mut_children_with(self);
441
442 if self.var_decl_kind != Some(self.target) {
443 return;
444 }
445
446 if d.iter().all(|v| v.init.is_some()) {
447 return;
448 }
449
450 let has_init = d.iter().any(|v| v.init.is_some());
451
452 if has_init {
453 if self.target == VarDeclKind::Let {
454 return;
455 }
456
457 let mut new = Vec::with_capacity(d.len());
458
459 d.take().into_iter().for_each(|v| {
460 if v.init.is_some() {
461 new.push(v)
462 } else {
463 self.vars.push(v)
464 }
465 });
466
467 *d = new;
468 }
469
470 let mut new = Vec::new();
471
472 if has_init {
473 new.append(&mut self.vars);
474 }
475
476 d.take().into_iter().for_each(|v| {
477 if v.init.is_some() {
478 new.push(v)
479 } else {
480 self.vars.push(v)
481 }
482 });
483
484 *d = new;
485 }
486}
487
488pub(super) struct VarPrepender {
489 target: VarDeclKind,
490
491 vars: Vec<VarDeclarator>,
492}
493
494impl VisitMut for VarPrepender {
495 noop_visit_mut_type!(fail);
496
497 fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {}
499
500 fn visit_mut_constructor(&mut self, _: &mut Constructor) {}
502
503 fn visit_mut_function(&mut self, _: &mut Function) {}
505
506 fn visit_mut_getter_prop(&mut self, _: &mut GetterProp) {}
507
508 fn visit_mut_setter_prop(&mut self, _: &mut SetterProp) {}
509
510 fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) {
511 if self.target != VarDeclKind::Var {
512 return;
514 }
515
516 n.visit_mut_children_with(self);
517 }
518
519 fn visit_mut_var_decl(&mut self, v: &mut VarDecl) {
520 if self.vars.is_empty() {
521 return;
522 }
523
524 if v.kind != self.target {
525 return;
526 }
527
528 if v.decls.iter().any(|d| d.init.is_some()) {
529 let mut decls = self.vars.take();
530 decls.extend(v.decls.take());
531
532 v.decls = decls;
533 }
534 }
535
536 fn visit_mut_module_decl(&mut self, _: &mut ModuleDecl) {}
537}