swc_ecma_minifier/compress/optimize/
loops.rs1use swc_common::{util::take::Take, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_utils::{ExprExt, Value::Known};
4
5use crate::compress::{optimize::Optimizer, util::UnreachableHandler};
6
7impl Optimizer<'_> {
9 pub(super) fn optimize_loops_with_break(&mut self, s: &mut Stmt) {
11 if !self.options.loops {
12 return;
13 }
14
15 let f = match s {
17 Stmt::For(v) => v,
18 _ => return,
19 };
20
21 match &mut *f.body {
23 Stmt::Break(BreakStmt { label: None, .. }) => {}
24 _ => return,
25 }
26
27 self.changed = true;
28 report_change!("loops: Removing a for loop with instant break");
29 self.prepend_stmts.extend(f.init.take().map(|init| {
30 match init {
31 VarDeclOrExpr::VarDecl(var) => var.into(),
32 VarDeclOrExpr::Expr(expr) => ExprStmt {
33 span: DUMMY_SP,
34 expr,
35 }
36 .into(),
37 #[cfg(swc_ast_unknown)]
38 _ => panic!("unable to access unknown nodes"),
39 }
40 }));
41 self.prepend_stmts.extend(f.test.take().map(|expr| {
42 ExprStmt {
43 span: DUMMY_SP,
44 expr,
45 }
46 .into()
47 }));
48
49 *s = EmptyStmt { span: DUMMY_SP }.into()
50 }
51
52 pub(super) fn optimize_loops_if_cond_is_false(&mut self, stmt: &mut Stmt) {
55 if !self.options.loops {
56 return;
57 }
58
59 match stmt {
60 Stmt::While(w) => {
61 let (purity, val) = w.test.cast_to_bool(self.ctx.expr_ctx);
62 if let Known(false) = val {
63 if purity.is_pure() {
64 let changed = UnreachableHandler::preserve_vars(stmt);
65 self.changed |= changed;
66 if changed {
67 report_change!(
68 "loops: Removing unreachable while statement without side effects"
69 );
70 }
71 } else {
72 let changed = UnreachableHandler::preserve_vars(&mut w.body);
73 self.changed |= changed;
74 if changed {
75 report_change!("loops: Removing unreachable body of a while statement");
76 }
77 }
78 }
79 }
80 Stmt::For(f) => {
81 if let Some(test) = &mut f.test {
82 let (purity, val) = test.cast_to_bool(self.ctx.expr_ctx);
83 if let Known(false) = val {
84 let changed = UnreachableHandler::preserve_vars(&mut f.body);
85 self.changed |= changed;
86 if changed {
87 report_change!("loops: Removing unreachable body of a for statement");
88 }
89 self.changed |= f.init.is_some() | f.update.is_some();
90
91 self.prepend_stmts.extend(f.init.take().map(|init| {
92 match init {
93 VarDeclOrExpr::VarDecl(var) => var.into(),
94 VarDeclOrExpr::Expr(expr) => ExprStmt {
95 span: DUMMY_SP,
96 expr,
97 }
98 .into(),
99 #[cfg(swc_ast_unknown)]
100 _ => panic!("unable to access unknown nodes"),
101 }
102 }));
103 self.prepend_stmts.push(
104 ExprStmt {
105 span: DUMMY_SP,
106 expr: f.test.take().unwrap(),
107 }
108 .into(),
109 );
110 f.update = None;
111 *stmt = *f.body.take();
112 } else if let Known(true) = val {
113 if purity.is_pure() {
114 self.changed = true;
115 report_change!(
116 "loops: Removing `test` part of a for stmt as it's always true"
117 );
118 f.test = None;
119 }
120 }
121 }
122 }
123 _ => {}
124 }
125 }
126}