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