swc_ecma_minifier/compress/optimize/
bools.rs1use swc_common::{util::take::Take, EqIgnoreSpan, Span, Spanned, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_utils::{ExprExt, Type, Value};
4
5use super::Optimizer;
6use crate::program_data::VarUsageInfoFlags;
7
8impl Optimizer<'_> {
10 pub(super) fn compress_typeof_undefined(&mut self, e: &mut BinExpr) {
13 fn opt(o: &mut Optimizer, l: &mut Expr, r: &mut Expr) -> bool {
14 match (&mut *l, &mut *r) {
15 (
16 Expr::Lit(Lit::Str(Str { value: l_v, .. })),
17 Expr::Unary(UnaryExpr {
18 op: op!("typeof"),
19 arg,
20 ..
21 }),
22 ) if &**l_v == "undefined" => {
23 if let Expr::Ident(arg) = &**arg {
25 if let Some(usage) = o.data.vars.get(&arg.to_id()) {
26 if !usage.flags.contains(VarUsageInfoFlags::DECLARED) {
27 return false;
28 }
29 }
30 }
31
32 *l = *Expr::undefined(l.span());
33 *r = *arg.take();
34 true
35 }
36 _ => false,
37 }
38 }
39
40 match e.op {
41 op!("==") | op!("!=") | op!("===") | op!("!==") => {}
42 _ => return,
43 }
44
45 if opt(self, &mut e.left, &mut e.right) || opt(self, &mut e.right, &mut e.left) {
46 e.op = match e.op {
47 op!("==") => {
48 op!("===")
49 }
50 op!("!=") => {
51 op!("!==")
52 }
53 _ => e.op,
54 };
55 }
56 }
57
58 pub(super) fn optimize_optional_chain_generated(&mut self, e: &mut BinExpr) {
61 if e.op == op!("||") || e.op == op!("&&") {
62 {
63 let res = self.optimize_optional_chain_generated_inner(
64 e.span,
65 e.op,
66 &mut e.left,
67 &mut e.right,
68 );
69 if let Some(res) = res {
70 self.changed = true;
71 report_change!("bools: Optimizing `=== null || === undefined` to `== null`");
72 *e = res;
73 return;
74 }
75 }
76
77 if let (Expr::Bin(left), right) = (&mut *e.left, &mut *e.right) {
78 if e.op == left.op {
79 let res = self.optimize_optional_chain_generated_inner(
80 right.span(),
81 e.op,
82 &mut left.right,
83 &mut *right,
84 );
85 if let Some(res) = res {
86 self.changed = true;
87 report_change!(
88 "bools: Optimizing `=== null || === undefined` to `== null`"
89 );
90 *e = BinExpr {
91 span: e.span,
92 op: e.op,
93 left: left.left.take(),
94 right: res.into(),
95 };
96 }
97 }
98 }
99 }
100 }
101
102 fn optimize_optional_chain_generated_inner(
103 &mut self,
104 span: Span,
105 top_op: BinaryOp,
106 e_left: &mut Expr,
107 e_right: &mut Expr,
108 ) -> Option<BinExpr> {
109 let (cmp, op, left, right) = match &mut *e_left {
110 Expr::Bin(left_bin) => {
111 if left_bin.op != op!("===") && left_bin.op != op!("!==") {
112 return None;
113 }
114
115 if top_op == op!("&&") && left_bin.op == op!("===") {
116 return None;
117 }
118 if top_op == op!("||") && left_bin.op == op!("!==") {
119 return None;
120 }
121
122 match &*left_bin.right {
123 Expr::Ident(..) | Expr::Lit(..) => {}
124 Expr::Assign(AssignExpr {
125 left: AssignTarget::Simple(SimpleAssignTarget::Ident(_)),
126 ..
127 }) => (),
128 Expr::Member(MemberExpr {
129 obj,
130 prop: MemberProp::Ident(..),
131 ..
132 }) => {
133 if self.should_preserve_property_access(
134 obj,
135 super::unused::PropertyAccessOpts {
136 allow_getter: false,
137 only_ident: false,
138 },
139 ) {
140 return None;
141 }
142 }
143 _ => {
144 return None;
145 }
146 }
147
148 let right = match &mut *e_right {
149 Expr::Bin(right_bin) => {
150 if right_bin.op != left_bin.op {
151 return None;
152 }
153
154 let same_assign = if let (
155 Expr::Assign(AssignExpr {
156 left: AssignTarget::Simple(SimpleAssignTarget::Ident(l_id)),
157 ..
158 }),
159 Expr::Ident(r_id),
160 ) = (&*left_bin.right, &*right_bin.right)
161 {
162 l_id.id.eq_ignore_span(r_id)
163 } else {
164 false
165 };
166
167 if !(same_assign || right_bin.right.eq_ignore_span(&left_bin.right)) {
168 return None;
169 }
170
171 &mut *right_bin.left
172 }
173 _ => return None,
174 };
175
176 (
177 &mut left_bin.right,
178 left_bin.op,
179 &mut *left_bin.left,
180 &mut *right,
181 )
182 }
183 _ => return None,
184 };
185
186 let lt = left.get_type(self.ctx.expr_ctx);
187 let rt = right.get_type(self.ctx.expr_ctx);
188 if let (Value::Known(Type::Undefined), Value::Known(Type::Null))
189 | (Value::Known(Type::Null), Value::Known(Type::Undefined)) = (lt, rt)
190 {
191 if op == op!("===") {
192 report_change!("Reducing `!== null || !== undefined` check to `!= null`");
193 return Some(BinExpr {
194 span,
195 op: op!("=="),
196 left: cmp.take(),
197 right: Lit::Null(Null { span: DUMMY_SP }).into(),
198 });
199 } else {
200 report_change!("Reducing `=== null || === undefined` check to `== null`");
201 return Some(BinExpr {
202 span,
203 op: op!("!="),
204 left: cmp.take(),
205 right: Lit::Null(Null { span: DUMMY_SP }).into(),
206 });
207 }
208 }
209
210 None
211 }
212}