swc_ecma_minifier/compress/pure/
numbers.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
use swc_common::{util::take::Take, EqIgnoreSpan, Spanned, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_utils::num_from_str;

use super::Pure;

impl Pure<'_> {
    pub(super) fn optimize_expr_in_num_ctx(&mut self, e: &mut Expr) {
        match e {
            Expr::Lit(Lit::Str(Str { span, value, .. })) => {
                let value = if value.is_empty() {
                    0f64
                } else {
                    match num_from_str(value).into_result() {
                        Ok(f) if f.is_finite() => f,
                        _ => return,
                    }
                };

                self.changed = true;
                report_change!("numbers: Converting a string literal to {:?}", value);
                *e = Lit::Num(Number {
                    span: *span,
                    value,
                    raw: None,
                })
                .into();
            }
            Expr::Unary(UnaryExpr {
                arg,
                op: op!(unary, "+"),
                ..
            }) => {
                self.changed = true;
                report_change!("numbers: remove useless to number");
                let new_expr = *arg.take();
                *e = new_expr
            }
            _ => (),
        }
    }

    ///
    /// - `1 / -0` => `- 1 / 0`
    pub(super) fn lift_minus(&mut self, e: &mut Expr) {
        if !self.options.evaluate {
            return;
        }

        if let Expr::Bin(arg) = e {
            if arg.op != op!("*") && arg.op != op!("/") {
                return;
            }

            match &mut *arg.right {
                Expr::Unary(UnaryExpr {
                    op: op!(unary, "-"),
                    arg: right_arg,
                    ..
                }) => {
                    self.changed = true;
                    report_change!("numbers: Lifting `-`");

                    *e = UnaryExpr {
                        span: arg.span,
                        op: op!(unary, "-"),
                        arg: BinExpr {
                            span: arg.span,
                            op: arg.op,
                            left: arg.left.take(),
                            right: right_arg.take(),
                        }
                        .into(),
                    }
                    .into();
                }

                Expr::Lit(Lit::Num(Number { span, value, .. })) => {
                    if value.is_sign_negative() {
                        self.changed = true;
                        report_change!("numbers: Lifting `-` in a literal");

                        *e = UnaryExpr {
                            span: arg.span,
                            op: op!(unary, "-"),
                            arg: BinExpr {
                                span: arg.span,
                                op: arg.op,
                                left: arg.left.take(),
                                right: Box::new(Expr::Lit(Lit::Num(Number {
                                    span: *span,
                                    value: -*value,
                                    raw: None,
                                }))),
                            }
                            .into(),
                        }
                        .into();
                    }
                }

                _ => {}
            }
        }
    }

    pub(super) fn optimize_to_number(&mut self, e: &mut Expr) {
        match e {
            Expr::Bin(bin) => {
                if bin.op == op!("*")
                    && matches!(&*bin.left, Expr::Lit(Lit::Num(Number { value: 1.0, .. })))
                {
                    report_change!("numbers: Turn '1 *' into '+'");
                    self.changed = true;

                    let value = bin.right.take();
                    let span = bin.span;

                    *e = Expr::Unary(UnaryExpr {
                        span,
                        op: op!(unary, "+"),
                        arg: value,
                    })
                }
            }
            Expr::Assign(a @ AssignExpr { op: op!("="), .. }) => {
                if let (
                    AssignTarget::Simple(SimpleAssignTarget::Ident(l_id)),
                    Expr::Unary(UnaryExpr {
                        op: op!(unary, "+"),
                        arg,
                        ..
                    }),
                ) = (&a.left, &*a.right)
                {
                    if let Expr::Ident(r_id) = &**arg {
                        if l_id.id.eq_ignore_span(r_id) {
                            report_change!("numbers: Turn a = +a into a *= 1");
                            self.changed = true;

                            a.op = op!("*=");
                            a.right = Box::new(Expr::Lit(Lit::Num(Number {
                                span: DUMMY_SP,
                                value: 1.0,
                                raw: None,
                            })))
                        }
                    }
                }
            }
            _ => (),
        }
    }

    pub(super) fn optimize_to_int(&mut self, e: &mut Expr) {
        let span = e.span();

        match e {
            Expr::Bin(bin) => match (bin.op, &mut *bin.left, &mut *bin.right) {
                (op!("|"), Expr::Bin(bin_inner), Expr::Lit(Lit::Num(n)))
                | (op!("|"), Expr::Lit(Lit::Num(n)), Expr::Bin(bin_inner))
                    if matches!(
                        bin_inner.op,
                        op!("<<") | op!(">>") | op!(">>>") | op!("|") | op!("^") | op!("&")
                    ) && n.value == 0.0 =>
                {
                    report_change!("numbers: Turn '(a & b) | 0' into 'a & b'");
                    self.changed = true;

                    let value = bin_inner.take();

                    *bin = BinExpr { span, ..value };
                }

                (
                    op!("|"),
                    Expr::Unary(u @ UnaryExpr { op: op!("~"), .. }),
                    Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
                )
                | (
                    op!("|"),
                    Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
                    Expr::Unary(u @ UnaryExpr { op: op!("~"), .. }),
                ) => {
                    report_change!("numbers: Turn '~a | 0' into '~a'");
                    self.changed = true;

                    let value = u.take();

                    *e = Expr::Unary(value);
                }

                (
                    op!("<<") | op!(">>") | op!("^"),
                    _,
                    Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
                ) => {
                    report_change!("numbers: Turn 'a << 0' into 'a | 0'");
                    self.changed = true;
                    bin.op = op!("|");
                }
                (
                    op!("&"),
                    _,
                    Expr::Lit(Lit::Num(
                        n @ Number {
                            value: 4294967295.0, // 2^32 - 1
                            ..
                        },
                    )),
                )
                | (
                    op!("&"),
                    Expr::Lit(Lit::Num(
                        n @ Number {
                            value: 4294967295.0, // 2^32 - 1
                            ..
                        },
                    )),
                    _,
                ) => {
                    report_change!("numbers: Turn 'a & 0XFFFFFFFF' into 'a | 0'");
                    self.changed = true;
                    bin.op = op!("|");
                    n.value = 0.0;
                    n.raw = None;
                }

                _ => (),
            },

            Expr::Assign(
                a @ AssignExpr {
                    op: op!("<<=") | op!(">>=") | op!(">>>=") | op!("|=") | op!("^=") | op!("&="),
                    ..
                },
            ) => {
                if let Expr::Bin(BinExpr {
                    op: op!("|"),
                    left,
                    right,
                    ..
                }) = &mut *a.right
                {
                    if let (e, Expr::Lit(Lit::Num(Number { value: 0.0, .. })))
                    | (Expr::Lit(Lit::Num(Number { value: 0.0, .. })), e) =
                        (&mut **left, &mut **right)
                    {
                        report_change!("numbers: Turn 'a |= b | 0' into 'a |= b'");
                        self.changed = true;
                        let value = e.take();
                        *a.right = value;
                    }
                }
            }
            _ => (),
        }
    }
}