swc_ecma_minifier/compress/optimize/
ops.rs1use swc_common::{util::take::Take, EqIgnoreSpan, Spanned};
2use swc_ecma_ast::*;
3use swc_ecma_utils::{ExprExt, Type, Value};
4use Value::Known;
5
6use super::{BitCtx, Optimizer};
7use crate::{compress::util::negate, util::make_bool};
8
9impl Optimizer<'_> {
10 pub(super) fn optimize_bin_equal(&mut self, e: &mut BinExpr) {
13 if !self.options.comparisons {
14 return;
15 }
16
17 match e.op {
18 op!("===") | op!("==") | op!("!==") | op!("!=") => {
19 if e.left.is_ident() && e.left.eq_ignore_span(&e.right) {
20 let id: Ident = e.left.clone().ident().unwrap();
21 if let Some(t) = self.typeofs.get(&id.to_id()) {
22 match &**t {
23 "object" | "function" => {
24 e.left = Box::new(make_bool(
25 e.span,
26 e.op == op!("===") || e.op == op!("=="),
27 ));
28 e.right.take();
29
30 self.changed = true;
31 report_change!("Evaluate comparing to self");
32
33 return;
34 }
35 _ => {}
36 }
37 }
38 }
39 }
40
41 _ => {}
42 }
43
44 if e.op == op!("===") || e.op == op!("!==") {
45 if (e.left.is_ident() || e.left.is_member()) && e.left.eq_ignore_span(&e.right) {
46 self.changed = true;
47 report_change!("Reducing comparison of same variable ({})", e.op);
48
49 e.op = if e.op == op!("===") {
50 op!("==")
51 } else {
52 op!("!=")
53 };
54 return;
55 }
56 }
57
58 if e.op == op!("===") {
59 if let Known(lt) = e.left.get_type(self.ctx.expr_ctx) {
60 if let Known(rt) = e.right.get_type(self.ctx.expr_ctx) {
61 if lt == rt {
62 e.op = op!("==");
63 self.changed = true;
64 report_change!(
65 "Reduced `===` to `==` because types of operands are identical"
66 )
67 }
68 }
69 }
70 }
71 }
72
73 pub(super) fn remove_bin_paren(&mut self, n: &mut BinExpr) {
78 if let Expr::Bin(right) = &mut *n.right {
79 if right.op == n.op {
80 if n.op.may_short_circuit()
81 || (right.left.is_str() && right.op == op!(bin, "+"))
82 || (n.left.is_str() && right.right.is_str())
83 {
84 self.changed = true;
85 report_change!("Remove extra paren in binary expression");
86 let left = n.left.take();
87 let BinExpr {
88 op,
89 left: rl,
90 right: rr,
91 ..
92 } = right.take();
93 *n.left = BinExpr {
94 span: left.span(),
95 op,
96 left,
97 right: rl,
98 }
99 .into();
100 n.right = rr;
101 }
102 }
103 }
104 }
105
106 pub(super) fn optimize_bangbang(&mut self, e: &mut Expr) {
110 if let Expr::Unary(UnaryExpr {
111 op: op!("!"), arg, ..
112 }) = e
113 {
114 if let Expr::Unary(UnaryExpr {
115 op: op!("!"), arg, ..
116 }) = &mut **arg
117 {
118 match &**arg {
119 Expr::Unary(UnaryExpr { op: op!("!"), .. })
120 | Expr::Bin(BinExpr { op: op!("in"), .. })
121 | Expr::Bin(BinExpr {
122 op: op!("instanceof"),
123 ..
124 })
125 | Expr::Bin(BinExpr { op: op!("=="), .. })
126 | Expr::Bin(BinExpr { op: op!("!="), .. })
127 | Expr::Bin(BinExpr { op: op!("==="), .. })
128 | Expr::Bin(BinExpr { op: op!("!=="), .. })
129 | Expr::Bin(BinExpr { op: op!("<="), .. })
130 | Expr::Bin(BinExpr { op: op!("<"), .. })
131 | Expr::Bin(BinExpr { op: op!(">="), .. })
132 | Expr::Bin(BinExpr { op: op!(">"), .. }) => {
133 if let Known(Type::Bool) = arg.get_type(self.ctx.expr_ctx) {
134 self.changed = true;
135 report_change!("Optimizing: `!!expr` => `expr`");
136 *e = *arg.take();
137 }
138 }
139
140 _ => {}
141 }
142 }
143 }
144 }
145
146 pub(super) fn negate_twice(&mut self, e: &mut Expr, is_ret_val_ignored: bool) {
148 self.negate(e, is_ret_val_ignored);
149 self.negate(e, is_ret_val_ignored);
150 }
151
152 pub(super) fn negate(&mut self, e: &mut Expr, is_ret_val_ignored: bool) {
153 negate(
154 self.ctx.expr_ctx,
155 e,
156 self.ctx.bit_ctx.contains(BitCtx::InBoolCtx),
157 is_ret_val_ignored,
158 )
159 }
160
161 pub(super) fn compress_logical_exprs_as_bang_bang(&mut self, e: &mut Expr, _in_bool_ctx: bool) {
171 if !self.options.conditionals && !self.options.reduce_vars {
172 return;
173 }
174
175 let bin = match e {
176 Expr::Bin(bin) => bin,
177 _ => return,
178 };
179
180 match bin.op {
181 op!("&&") | op!("||") => {
182 self.compress_logical_exprs_as_bang_bang(&mut bin.left, true);
183 self.compress_logical_exprs_as_bang_bang(&mut bin.right, true);
184 }
185
186 _ => {}
187 }
188
189 let lt = bin.left.get_type(self.ctx.expr_ctx);
190 match lt {
191 Known(Type::Bool) => {}
193 _ => return,
194 }
195
196 let rt = bin.right.get_type(self.ctx.expr_ctx);
197 match rt {
198 Known(Type::Bool) => {}
199 _ => return,
200 }
201
202 match bin.op {
203 op!("&&") => {
204 let rb = bin.right.as_pure_bool(self.ctx.expr_ctx);
205 let rb = match rb {
206 Value::Known(v) => v,
207 _ => return,
208 };
209
210 if rb {
211 self.changed = true;
212 report_change!("Optimizing: e && true => !!e");
213
214 self.negate_twice(&mut bin.left, false);
215 *e = *bin.left.take();
216 }
217 }
218 op!("||") => {
219 let rb = bin.right.as_pure_bool(self.ctx.expr_ctx);
220 let rb = match rb {
221 Value::Known(v) => v,
222 _ => return,
223 };
224
225 if !rb {
226 self.changed = true;
227 report_change!("Optimizing: e || false => !!e");
228
229 self.negate_twice(&mut bin.left, false);
230 *e = *bin.left.take();
231 }
232 }
233 _ => {}
234 }
235 }
236
237 pub(super) fn compress_typeofs(&mut self, e: &mut Expr) {
238 if !self.options.typeofs {
239 return;
240 }
241
242 if let Expr::Unary(UnaryExpr {
243 span,
244 op: op!("typeof"),
245 arg,
246 ..
247 }) = e
248 {
249 match &**arg {
250 Expr::Ident(arg) => {
251 if let Some(value) = self.typeofs.get(&arg.to_id()).cloned() {
252 report_change!(
253 "Converting typeof of variable to literal as we know the value"
254 );
255 self.changed = true;
256 *e = Lit::Str(Str {
257 span: *span,
258 raw: None,
259 value: value.into(),
260 })
261 .into();
262 }
263 }
264
265 Expr::Arrow(..) | Expr::Fn(..) | Expr::Class(..) => {
266 report_change!("Converting typeof to 'function' as we know the value");
267 self.changed = true;
268 *e = Lit::Str(Str {
269 span: *span,
270 raw: None,
271 value: "function".into(),
272 })
273 .into();
274 }
275
276 Expr::Array(..) | Expr::Object(..) => {
277 report_change!("Converting typeof to 'object' as we know the value");
278 self.changed = true;
279 *e = Lit::Str(Str {
280 span: *span,
281 raw: None,
282 value: "object".into(),
283 })
284 .into();
285 }
286 _ => {}
287 }
288 }
289 }
290}