From f92242dcb17a7b6dc25dac63d052df9e3d7042bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 15 Apr 2022 17:25:31 +0900 Subject: [PATCH] feat(es/minifier): Improve sequential inliner (#4335) - We now skip more expressions. --- .../src/compress/optimize/sequences.rs | 55 +++++++++++++++---- .../src/compress/pure/misc.rs | 4 ++ crates/swc_ecma_minifier/tests/TODO.txt | 3 - crates/swc_ecma_minifier/tests/golden.txt | 3 + .../collapse_vars/issue_2954_2/output.js | 9 ++- .../collapse_vars/issue_2954_3/output.js | 12 ++-- 6 files changed, 61 insertions(+), 25 deletions(-) diff --git a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs index 13857a665c8..5102fa7802b 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs @@ -607,7 +607,7 @@ where return; } - if self.ctx.in_top_level() && !self.options.top_level() { + if self.ctx.is_top_level_for_block_level_vars() && !self.options.top_level() { log_abort!("sequences: [x] Top level"); return; } @@ -848,8 +848,10 @@ where Ok(()) } + #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] fn is_skippable_for_seq(&self, a: Option<&Mergable>, e: &Expr) -> bool { if self.ctx.in_try_block { + log_abort!("try block"); return false; } @@ -863,47 +865,54 @@ where match a { Mergable::Var(a) => { if is_ident_used_by(e.to_id(), &**a) { + log_abort!("ident used by a (var)"); return false; } } Mergable::Expr(a) => { if is_ident_used_by(e.to_id(), &**a) { + log_abort!("ident used by a (expr)"); return false; } } } } - true + return true; } - Expr::Lit(..) => true, + Expr::Lit(..) => return true, Expr::Unary(UnaryExpr { op: op!("!") | op!("void") | op!("typeof"), arg, .. - }) => self.is_skippable_for_seq(a, arg), + }) => return self.is_skippable_for_seq(a, arg), Expr::Bin(BinExpr { left, right, .. }) => { - self.is_skippable_for_seq(a, left) && self.is_skippable_for_seq(a, right) + return self.is_skippable_for_seq(a, left) && self.is_skippable_for_seq(a, right) } Expr::Assign(e) => { let left_id = e.left.as_ident(); let left_id = match left_id { Some(v) => v, - _ => return false, + _ => { + log_abort!("e.left is not ident"); + return false; + } }; if let Some(a) = a { match a { Mergable::Var(a) => { if is_ident_used_by(left_id.to_id(), &**a) { + log_abort!("e.left is used by a (var)"); return false; } } Mergable::Expr(a) => { if is_ident_used_by(left_id.to_id(), &**a) { + log_abort!("e.left is used by a (expr)"); return false; } } @@ -915,6 +924,7 @@ where } if contains_this_expr(&*e.right) { + log_abort!("e.right contains this"); return false; } @@ -924,10 +934,11 @@ where } if used_ids.len() != 1 || !used_ids.contains(&left_id.to_id()) { + log_abort!("bad used_ids"); return false; } - self.is_skippable_for_seq(a, &e.right) + return self.is_skippable_for_seq(a, &e.right); } Expr::Object(e) => { @@ -937,21 +948,45 @@ where // TODO: Check for side effects in object properties. - false + log_abort!("unimpl: object"); + + return false; } Expr::Array(e) => { for elem in e.elems.iter().flatten() { if !self.is_skippable_for_seq(a, &elem.expr) { + log_abort!("array element"); return false; } } - true + return true; } - _ => false, + Expr::Call(e) => { + if e.args.is_empty() { + if let Callee::Expr(callee) = &e.callee { + if let Expr::Fn(callee) = &**callee { + let ids = idents_used_by(&callee.function); + + if ids + .iter() + .all(|id| id.1.outer() == self.marks.top_level_mark) + { + return true; + } + } + } + } + } + + _ => {} } + + log_abort!("sequences: skip: Unknown expr: {}", dump(e, true)); + + false } /// Returns true if something is modified. diff --git a/crates/swc_ecma_minifier/src/compress/pure/misc.rs b/crates/swc_ecma_minifier/src/compress/pure/misc.rs index fb1c647e71f..a4dc76f6bfb 100644 --- a/crates/swc_ecma_minifier/src/compress/pure/misc.rs +++ b/crates/swc_ecma_minifier/src/compress/pure/misc.rs @@ -34,6 +34,10 @@ impl Pure<'_> { } pub(super) fn remove_useless_return(&mut self, stmts: &mut Vec) { + if !self.options.dead_code && !self.options.reduce_vars { + return; + } + if let Some(Stmt::Return(ReturnStmt { arg: None, .. })) = stmts.last() { self.changed = true; report_change!("misc: Removing useless return"); diff --git a/crates/swc_ecma_minifier/tests/TODO.txt b/crates/swc_ecma_minifier/tests/TODO.txt index 39dae4d32f5..5045fa5e558 100644 --- a/crates/swc_ecma_minifier/tests/TODO.txt +++ b/crates/swc_ecma_minifier/tests/TODO.txt @@ -88,9 +88,6 @@ collapse_vars/issue_2453/input.js collapse_vars/issue_2497/input.js collapse_vars/issue_2506/input.js collapse_vars/issue_2914_2/input.js -collapse_vars/issue_2931/input.js -collapse_vars/issue_2954_2/input.js -collapse_vars/issue_2954_3/input.js collapse_vars/issue_315/input.js collapse_vars/lvalues_def/input.js collapse_vars/may_throw_2/input.js diff --git a/crates/swc_ecma_minifier/tests/golden.txt b/crates/swc_ecma_minifier/tests/golden.txt index 82915735ba7..e9c45b681ec 100644 --- a/crates/swc_ecma_minifier/tests/golden.txt +++ b/crates/swc_ecma_minifier/tests/golden.txt @@ -195,7 +195,10 @@ collapse_vars/issue_2891_1/input.js collapse_vars/issue_2891_2/input.js collapse_vars/issue_2908/input.js collapse_vars/issue_2914_1/input.js +collapse_vars/issue_2931/input.js collapse_vars/issue_2954_1/input.js +collapse_vars/issue_2954_2/input.js +collapse_vars/issue_2954_3/input.js collapse_vars/issue_2974/input.js collapse_vars/issue_3032/input.js collapse_vars/issue_3096/input.js diff --git a/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2954_2/output.js b/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2954_2/output.js index d9a2fb89a5b..0fb5387805b 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2954_2/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2954_2/output.js @@ -1,13 +1,12 @@ -var a = "FAIL_1", - b; +var b, a = "FAIL_1"; try { throw 0; } catch (e) { do { a = "FAIL_2"; - (b = (function () { + (b = function() { throw new Error("PASS"); - })()) && b.c; - } while (0); + }()) && b.c; + }while (0) } console.log(a); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2954_3/output.js b/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2954_3/output.js index ad2dcf6668b..ce04f3887ee 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2954_3/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_2954_3/output.js @@ -1,12 +1,10 @@ -var a = "FAIL_1", - b; -try { -} finally { +var b, a = "FAIL_1"; +try {} finally{ do { a = "FAIL_2"; - (b = (function () { + (b = function() { throw new Error("PASS"); - })()) && b.c; - } while (0); + }()) && b.c; + }while (0) } console.log(a);