swc_ecma_minifier/compress/pure/
arrows.rs

1use swc_common::{util::take::Take, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_utils::{contains_arguments, contains_this_expr};
4
5use super::Pure;
6use crate::compress::util::contains_super;
7
8/// Methods related to the option `arrows`.
9impl Pure<'_> {
10    pub(super) fn unsafe_optimize_fn_as_arrow(&mut self, e: &mut Expr) {
11        if self.options.ecma < EsVersion::Es2015 {
12            return;
13        }
14
15        if !self.options.unsafe_arrows {
16            return;
17        }
18
19        if let Expr::Fn(FnExpr {
20            ident: None,
21            function,
22        }) = e
23        {
24            if function.params.iter().any(contains_this_expr)
25                || contains_this_expr(&function.body)
26                || function.is_generator
27            {
28                return;
29            }
30
31            self.changed = true;
32            report_change!("unsafe_arrows: Fn expr => arrow");
33
34            *e = ArrowExpr {
35                span: function.span,
36                params: function.params.take().into_iter().map(|p| p.pat).collect(),
37                body: Box::new(BlockStmtOrExpr::BlockStmt(function.body.take().unwrap())),
38                is_async: function.is_async,
39                is_generator: function.is_generator,
40                ..Default::default()
41            }
42            .into();
43        }
44    }
45
46    pub(super) fn optimize_arrow_body(&mut self, b: &mut BlockStmtOrExpr) {
47        match b {
48            BlockStmtOrExpr::BlockStmt(s) => {
49                if s.stmts.len() == 1 {
50                    if let Stmt::Return(s) = &mut s.stmts[0] {
51                        if let Some(arg) = &mut s.arg {
52                            report_change!("arrows: Optimizing the body of an arrow");
53                            *b = BlockStmtOrExpr::Expr(arg.take());
54                        }
55                    }
56                }
57            }
58            BlockStmtOrExpr::Expr(_) => {}
59            #[cfg(swc_ast_unknown)]
60            _ => panic!("unable to access unknown nodes"),
61        }
62    }
63
64    pub(super) fn optimize_arrow_method_prop(&mut self, p: &mut Prop) {
65        if !self.options.unsafe_methods && !self.options.arrows {
66            return;
67        }
68
69        if let Prop::Method(m) = p {
70            if m.function.is_generator
71                || contains_arguments(&m.function.body)
72                || contains_super(&m.function.body)
73                || m.function.params.iter().any(contains_this_expr)
74            {
75                return;
76            }
77
78            let m_span = m.function.span;
79
80            if let Some(body) = &mut m.function.body {
81                if body.stmts.len() == 1
82                    && matches!(
83                        body.stmts[0],
84                        Stmt::Return(ReturnStmt { arg: Some(..), .. })
85                    )
86                {
87                    if contains_this_expr(body) {
88                        return;
89                    }
90                    self.changed = true;
91                    report_change!("Method property => arrow");
92
93                    let arg = body
94                        .take()
95                        .stmts
96                        .remove(0)
97                        .expect_return_stmt()
98                        .arg
99                        .take()
100                        .unwrap();
101
102                    *p = Prop::KeyValue(KeyValueProp {
103                        key: m.key.take(),
104                        value: ArrowExpr {
105                            span: m_span,
106                            params: m
107                                .function
108                                .params
109                                .take()
110                                .into_iter()
111                                .map(|v| v.pat)
112                                .collect(),
113                            body: Box::new(BlockStmtOrExpr::Expr(arg)),
114                            is_async: m.function.is_async,
115                            is_generator: m.function.is_generator,
116                            ..Default::default()
117                        }
118                        .into(),
119                    });
120                    return;
121                }
122            }
123        }
124
125        if let Prop::KeyValue(kv) = p {
126            // See https://github.com/swc-project/swc/pull/6521
127            //
128            // ({foo(){}}).foo.toString()
129            //
130            // returns `foo(){}`
131            if !self.options.unsafe_methods {
132                return;
133            }
134
135            //
136            if contains_this_expr(&kv.value) {
137                return;
138            }
139
140            match &mut *kv.value {
141                Expr::Arrow(m) if m.body.is_block_stmt() => {
142                    *p = Prop::Method(MethodProp {
143                        key: kv.key.take(),
144                        function: Box::new(Function {
145                            params: m
146                                .params
147                                .take()
148                                .into_iter()
149                                .map(|pat| Param {
150                                    span: DUMMY_SP,
151                                    decorators: Default::default(),
152                                    pat,
153                                })
154                                .collect(),
155                            span: m.span,
156                            body: m.body.take().block_stmt(),
157                            is_generator: m.is_generator,
158                            is_async: m.is_async,
159                            ..Default::default()
160                        }),
161                    });
162                }
163                _ => (),
164            }
165        }
166    }
167}