swc_ecma_minifier/compress/optimize/
loops.rs

1use 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
7/// Methods related to the option `loops`.
8impl Optimizer<'_> {
9    /// `for(a;b;c;) break;` => `a;b;`
10    pub(super) fn optimize_loops_with_break(&mut self, s: &mut Stmt) {
11        if !self.options.loops {
12            return;
13        }
14
15        // As we normalize loops, this is enough.
16        let f = match s {
17            Stmt::For(v) => v,
18            _ => return,
19        };
20
21        // We only care about instant breaks.
22        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    ///
53    /// - `while(false) { var a; foo() }` => `var a;`
54    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}