swc_ecma_minifier/compress/
mod.rs1use std::borrow::Cow;
2#[cfg(feature = "debug")]
3use std::thread;
4
5#[cfg(feature = "pretty_assertions")]
6use pretty_assertions::assert_eq;
7use swc_common::pass::{CompilerPass, Repeated};
8use swc_ecma_ast::*;
9use swc_ecma_usage_analyzer::marks::Marks;
10use swc_ecma_visit::VisitMutWith;
11#[cfg(debug_assertions)]
12use swc_ecma_visit::VisitWith;
13use swc_timer::timer;
14use tracing::debug;
15
16pub(crate) use self::pure::{pure_optimizer, PureOptimizerConfig};
17use self::{hoist_decls::DeclHoisterConfig, optimize::optimizer};
18#[cfg(debug_assertions)]
19use crate::debug::AssertValid;
20use crate::{
21 compress::hoist_decls::decl_hoister,
22 debug::dump,
23 mode::Mode,
24 option::{CompressOptions, MangleOptions},
25 program_data::analyze,
26 util::force_dump_program,
27};
28
29mod hoist_decls;
30mod optimize;
31mod pure;
32mod util;
33
34pub(crate) fn compressor<'a, M>(
35 marks: Marks,
36 options: &'a CompressOptions,
37 mangle_options: Option<&'a MangleOptions>,
38 mode: &'a M,
39) -> impl 'a + Pass
40where
41 M: Mode,
42{
43 Compressor {
44 marks,
45 options,
46 mangle_options,
47 changed: false,
48 pass: 1,
49 mode,
50 }
51}
52
53struct Compressor<'a> {
54 marks: Marks,
55 options: &'a CompressOptions,
56 mangle_options: Option<&'a MangleOptions>,
57 changed: bool,
58 pass: usize,
59
60 mode: &'a dyn Mode,
61}
62
63impl CompilerPass for Compressor<'_> {
64 fn name(&self) -> Cow<'static, str> {
65 "compressor".into()
66 }
67}
68
69impl Pass for Compressor<'_> {
70 fn process(&mut self, program: &mut Program) {
71 self.optimize_unit_repeatedly(program);
72 }
73}
74
75impl Compressor<'_> {
76 fn optimize_unit_repeatedly(&mut self, n: &mut Program) {
77 trace_op!(
78 "Optimizing a compile unit within `{:?}`",
79 thread::current().name()
80 );
81
82 if self.options.hoist_vars || self.options.hoist_fns {
83 let data = analyze(&*n, Some(self.marks), false);
84
85 let mut v = decl_hoister(
86 DeclHoisterConfig {
87 hoist_fns: self.options.hoist_fns,
88 hoist_vars: self.options.hoist_vars,
89 _top_level: self.options.top_level(),
90 },
91 &data,
92 );
93 n.visit_mut_with(&mut v);
94 self.changed |= v.changed();
95 }
96
97 loop {
98 self.changed = false;
99 self.optimize_unit(n);
100 self.pass += 1;
101 if !self.changed {
102 break;
103 }
104 }
105
106 self.pass = 1;
107 }
115
116 fn optimize_unit(&mut self, n: &mut Program) {
118 let _timer = timer!("optimize", pass = self.pass);
119
120 if self.options.passes != 0 && self.options.passes < self.pass {
121 let done = dump(&*n, false);
122 debug!("===== Done =====\n{}", done);
123 return;
124 }
125
126 if self.pass > 200 {
128 let code = force_dump_program(n);
129
130 panic!(
131 "Infinite loop detected (current pass = {})\n{}",
132 self.pass, code
133 );
134 }
135
136 #[cfg(feature = "debug")]
137 let start = {
138 let start = force_dump_program(n);
139 debug!("===== Start =====\n{}", start);
140 start
141 };
142
143 {
144 let _timer = timer!("apply pure optimizer");
145
146 let mut visitor = pure_optimizer(
147 self.options,
148 self.marks,
149 PureOptimizerConfig {
150 enable_join_vars: self.pass > 1,
151 force_str_for_tpl: self.mode.force_str_for_tpl(),
152 },
153 );
154 n.visit_mut_with(&mut visitor);
155
156 self.changed |= visitor.changed();
157
158 #[cfg(feature = "debug")]
159 if visitor.changed() {
160 let src = force_dump_program(n);
161 debug!(
162 "===== Before pure =====\n{}\n===== After pure =====\n{}",
163 start, src
164 );
165 }
166 }
167
168 #[cfg(debug_assertions)]
169 {
170 n.visit_with(&mut AssertValid);
171 }
172
173 {
174 let _timer = timer!("apply full optimizer");
175
176 let mut data = analyze(&*n, Some(self.marks), false);
177
178 let mut visitor = optimizer(
183 self.marks,
184 self.options,
185 self.mangle_options,
186 &mut data,
187 self.mode,
188 );
189 n.visit_mut_with(&mut visitor);
190
191 self.changed |= visitor.changed();
192
193 }
196 }
197}