swc_ecma_minifier/compress/
mod.rs

1use 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        // let last_mark = n.remove_mark();
108        // assert!(
109        //     N::is_module() || last_mark == self.marks.standalone,
110        //     "{:?}; last={:?}",
111        //     self.marks,
112        //     last_mark
113        // );
114    }
115
116    /// Optimize a module. `N` can be [Module] or [FnExpr].
117    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        // This exists to prevent hanging.
127        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            // TODO: reset_opt_flags
179            //
180            // This is swc version of `node.optimize(this);`.
181
182            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            // let done = dump(&*n);
194            // debug!("===== Result =====\n{}", done);
195        }
196    }
197}