swc_ecma_minifier/compress/pure/
numbers.rs1use swc_common::{util::take::Take, EqIgnoreSpan, Spanned, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_utils::num_from_str;
4
5use super::Pure;
6
7impl Pure<'_> {
8 pub(super) fn optimize_expr_in_num_ctx(&mut self, e: &mut Expr) {
9 match e {
10 Expr::Lit(Lit::Str(Str { span, value, .. })) => {
11 let Some(value) = value.as_str() else {
12 return;
13 };
14 let value = if value.is_empty() {
15 0f64
16 } else {
17 match num_from_str(value).into_result() {
18 Ok(f) if f.is_finite() => f,
19 _ => return,
20 }
21 };
22
23 self.changed = true;
24 report_change!("numbers: Converting a string literal to {:?}", value);
25 *e = Lit::Num(Number {
26 span: *span,
27 value,
28 raw: None,
29 })
30 .into();
31 }
32 Expr::Unary(UnaryExpr {
33 arg,
34 op: op!(unary, "+"),
35 ..
36 }) => {
37 self.changed = true;
38 report_change!("numbers: remove useless to number");
39 let new_expr = *arg.take();
40 *e = new_expr
41 }
42 _ => (),
43 }
44 }
45
46 pub(super) fn lift_minus(&mut self, e: &mut Expr) {
49 if !self.options.evaluate {
50 return;
51 }
52
53 if let Expr::Bin(arg) = e {
54 if arg.op != op!("*") && arg.op != op!("/") {
55 return;
56 }
57
58 match &mut *arg.right {
59 Expr::Unary(UnaryExpr {
60 op: op!(unary, "-"),
61 arg: right_arg,
62 ..
63 }) => {
64 self.changed = true;
65 report_change!("numbers: Lifting `-`");
66
67 *e = UnaryExpr {
68 span: arg.span,
69 op: op!(unary, "-"),
70 arg: BinExpr {
71 span: arg.span,
72 op: arg.op,
73 left: arg.left.take(),
74 right: right_arg.take(),
75 }
76 .into(),
77 }
78 .into();
79 }
80
81 Expr::Lit(Lit::Num(Number { span, value, .. })) => {
82 if value.is_sign_negative() {
83 self.changed = true;
84 report_change!("numbers: Lifting `-` in a literal");
85
86 *e = UnaryExpr {
87 span: arg.span,
88 op: op!(unary, "-"),
89 arg: BinExpr {
90 span: arg.span,
91 op: arg.op,
92 left: arg.left.take(),
93 right: Box::new(Expr::Lit(Lit::Num(Number {
94 span: *span,
95 value: -*value,
96 raw: None,
97 }))),
98 }
99 .into(),
100 }
101 .into();
102 }
103 }
104
105 _ => {}
106 }
107 }
108 }
109
110 pub(super) fn optimize_to_number(&mut self, e: &mut Expr) {
111 match e {
112 Expr::Bin(bin) => {
113 if bin.op == op!("*")
114 && matches!(&*bin.left, Expr::Lit(Lit::Num(Number { value: 1.0, .. })))
115 {
116 report_change!("numbers: Turn '1 *' into '+'");
117 self.changed = true;
118
119 let value = bin.right.take();
120 let span = bin.span;
121
122 *e = Expr::Unary(UnaryExpr {
123 span,
124 op: op!(unary, "+"),
125 arg: value,
126 })
127 }
128 }
129 Expr::Assign(a @ AssignExpr { op: op!("="), .. }) => {
130 if let (
131 AssignTarget::Simple(SimpleAssignTarget::Ident(l_id)),
132 Expr::Unary(UnaryExpr {
133 op: op!(unary, "+"),
134 arg,
135 ..
136 }),
137 ) = (&a.left, &*a.right)
138 {
139 if let Expr::Ident(r_id) = &**arg {
140 if l_id.id.eq_ignore_span(r_id) {
141 report_change!("numbers: Turn a = +a into a *= 1");
142 self.changed = true;
143
144 a.op = op!("*=");
145 a.right = Box::new(Expr::Lit(Lit::Num(Number {
146 span: DUMMY_SP,
147 value: 1.0,
148 raw: None,
149 })))
150 }
151 }
152 }
153 }
154 _ => (),
155 }
156 }
157
158 pub(super) fn optimize_to_int(&mut self, e: &mut Expr) {
159 let span = e.span();
160
161 match e {
162 Expr::Bin(bin) => match (bin.op, &mut *bin.left, &mut *bin.right) {
163 (op!("|"), Expr::Bin(bin_inner), Expr::Lit(Lit::Num(n)))
164 | (op!("|"), Expr::Lit(Lit::Num(n)), Expr::Bin(bin_inner))
165 if matches!(
166 bin_inner.op,
167 op!("<<") | op!(">>") | op!(">>>") | op!("|") | op!("^") | op!("&")
168 ) && n.value == 0.0 =>
169 {
170 report_change!("numbers: Turn '(a & b) | 0' into 'a & b'");
171 self.changed = true;
172
173 let value = bin_inner.take();
174
175 *bin = BinExpr { span, ..value };
176 }
177
178 (
179 op!("<<") | op!(">>") | op!(">>>") | op!("|") | op!("^") | op!("&"),
180 e @ Expr::Bin(BinExpr { op: op!("|"), .. }),
181 _,
182 )
183 | (
184 op!("<<") | op!(">>") | op!(">>>") | op!("|") | op!("^") | op!("&"),
185 _,
186 e @ Expr::Bin(BinExpr { op: op!("|"), .. }),
187 ) => {
188 if let Expr::Bin(bin) = e {
189 match (&mut *bin.left, &mut *bin.right) {
190 (Expr::Lit(Lit::Num(n)), inner) | (inner, Expr::Lit(Lit::Num(n)))
191 if n.value == 0.0 =>
192 {
193 report_change!("numbers: Turn '(a | 0) ^ b' into 'a ^ b'");
194 self.changed = true;
195
196 let new_expr = inner.take();
197
198 *e = new_expr
199 }
200 _ => {}
201 }
202 }
203 }
204
205 (
206 op!("|"),
207 Expr::Unary(u @ UnaryExpr { op: op!("~"), .. }),
208 Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
209 )
210 | (
211 op!("|"),
212 Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
213 Expr::Unary(u @ UnaryExpr { op: op!("~"), .. }),
214 ) => {
215 report_change!("numbers: Turn '~a | 0' into '~a'");
216 self.changed = true;
217
218 let value = u.take();
219
220 *e = Expr::Unary(value);
221 }
222
223 (
224 op!("<<") | op!(">>") | op!("^"),
225 _,
226 Expr::Lit(Lit::Num(Number { value: 0.0, .. })),
227 ) => {
228 report_change!("numbers: Turn 'a << 0' into 'a | 0'");
229 self.changed = true;
230 bin.op = op!("|");
231 }
232 (
233 op!("&"),
234 _,
235 Expr::Lit(Lit::Num(
236 n @ Number {
237 value: 4294967295.0, ..
239 },
240 )),
241 )
242 | (
243 op!("&"),
244 Expr::Lit(Lit::Num(
245 n @ Number {
246 value: 4294967295.0, ..
248 },
249 )),
250 _,
251 ) => {
252 report_change!("numbers: Turn 'a & 0XFFFFFFFF' into 'a | 0'");
253 self.changed = true;
254 bin.op = op!("|");
255 n.value = 0.0;
256 n.raw = None;
257 }
258
259 _ => (),
260 },
261
262 Expr::Assign(
263 a @ AssignExpr {
264 op: op!("<<=") | op!(">>=") | op!(">>>=") | op!("|=") | op!("^=") | op!("&="),
265 ..
266 },
267 ) => {
268 if let Expr::Bin(BinExpr {
269 op: op!("|"),
270 left,
271 right,
272 ..
273 }) = &mut *a.right
274 {
275 if let (e, Expr::Lit(Lit::Num(Number { value: 0.0, .. })))
276 | (Expr::Lit(Lit::Num(Number { value: 0.0, .. })), e) =
277 (&mut **left, &mut **right)
278 {
279 report_change!("numbers: Turn 'a |= b | 0' into 'a |= b'");
280 self.changed = true;
281 let value = e.take();
282 *a.right = value;
283 }
284 }
285 }
286 _ => (),
287 }
288 }
289}