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
use swc_common::{util::take::Take, EqIgnoreSpan, 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) {
        if let Expr::Lit(Lit::Str(Str { span, value, .. })) = e {
            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();
        }
    }

    ///
    /// - `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,
                            })))
                        }
                    }
                }
            }
            _ => (),
        }
    }
}