swc_ecma_minifier/compress/pure/
sequences.rs1use swc_common::{util::take::Take, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_utils::{ExprFactory, StmtLike};
4
5use super::Pure;
6use crate::compress::util::is_pure_undefined;
7
8impl Pure<'_> {
9 pub(super) fn drop_useless_ident_ref_in_seq(&mut self, seq: &mut SeqExpr) {
10 if !self.options.collapse_vars {
11 return;
12 }
13
14 if seq.exprs.len() < 2 {
15 return;
16 }
17
18 if let (Expr::Assign(assign @ AssignExpr { op: op!("="), .. }), Expr::Ident(ident)) = (
19 &*seq.exprs[seq.exprs.len() - 2],
20 &*seq.exprs[seq.exprs.len() - 1],
21 ) {
22 if let AssignTarget::Simple(SimpleAssignTarget::Ident(left)) = &assign.left {
24 if left.id.sym == ident.sym && left.id.ctxt == ident.ctxt {
25 report_change!(
26 "drop_useless_ident_ref_in_seq: Dropping `{}` as it's useless",
27 left.id
28 );
29 self.changed = true;
30 seq.exprs.pop();
31 }
32 }
33 }
34 }
35
36 pub(super) fn shift_assignment(&mut self, e: &mut SeqExpr) {
39 if e.exprs.len() < 2 {
40 return;
41 }
42
43 if let Some(last) = e.exprs.last() {
44 let last_id = match &**last {
45 Expr::Ident(i) => i,
46 _ => return,
47 };
48
49 if let Expr::Assign(assign @ AssignExpr { op: op!("="), .. }) =
50 &*e.exprs[e.exprs.len() - 2]
51 {
52 if let Some(lhs) = assign.left.as_ident() {
53 if lhs.sym == last_id.sym && lhs.ctxt == last_id.ctxt {
54 e.exprs.pop();
55 self.changed = true;
56 report_change!("sequences: Shifting assignment");
57 }
58 };
59 }
60 }
61 }
62
63 pub(super) fn shift_void(&mut self, e: &mut SeqExpr) {
64 if e.exprs.len() < 2 {
65 return;
66 }
67
68 if let Expr::Unary(UnaryExpr {
69 op: op!("void"), ..
70 }) = &*e.exprs[e.exprs.len() - 2]
71 {
72 return;
73 }
74
75 if let Some(last) = e.exprs.last() {
76 if is_pure_undefined(self.expr_ctx, last) {
77 self.changed = true;
78 report_change!("sequences: Shifting void");
79
80 e.exprs.pop();
81 let last = e.exprs.last_mut().unwrap();
82
83 *last = UnaryExpr {
84 span: DUMMY_SP,
85 op: op!("void"),
86 arg: last.take(),
87 }
88 .into()
89 }
90 }
91 }
92
93 pub(super) fn break_assignments_in_seqs<T>(&mut self, stmts: &mut Vec<T>)
97 where
98 T: StmtLike,
99 {
100 if true {
102 return;
103 }
104 let need_work = stmts.iter().any(|stmt| match stmt.as_stmt() {
105 Some(Stmt::Expr(e)) => match &*e.expr {
106 Expr::Seq(seq) => {
107 seq.exprs.len() > 1
108 && seq.exprs.iter().all(|expr| {
109 matches!(&**expr, Expr::Assign(AssignExpr { op: op!("="), .. }))
110 })
111 }
112 _ => false,
113 },
114
115 _ => false,
116 });
117
118 if !need_work {
119 return;
120 }
121
122 let mut new_stmts = Vec::new();
123
124 for stmt in stmts.take() {
125 match stmt.try_into_stmt() {
126 Ok(stmt) => match stmt {
127 Stmt::Expr(es)
128 if match &*es.expr {
129 Expr::Seq(seq) => {
130 seq.exprs.len() > 1
131 && seq.exprs.iter().all(|expr| {
132 matches!(
133 &**expr,
134 Expr::Assign(AssignExpr { op: op!("="), .. })
135 )
136 })
137 }
138 _ => false,
139 } =>
140 {
141 let span = es.span;
142 let seq = es.expr.seq().unwrap();
143 new_stmts.extend(
144 seq.exprs
145 .into_iter()
146 .map(|expr| ExprStmt { span, expr })
147 .map(Stmt::Expr)
148 .map(T::from),
149 );
150 }
151
152 _ => {
153 new_stmts.push(T::from(stmt));
154 }
155 },
156 Err(stmt) => {
157 new_stmts.push(stmt);
158 }
159 }
160 }
161 self.changed = true;
162 report_change!(
163 "sequences: Splitted a sequence expression to multiple expression statements"
164 );
165 *stmts = new_stmts;
166 }
167
168 pub(super) fn lift_seqs_of_bin(&mut self, e: &mut Expr) {
171 let bin = match e {
172 Expr::Bin(b) => b,
173 _ => return,
174 };
175
176 if let Expr::Seq(left) = &mut *bin.left {
177 if left.exprs.is_empty() {
178 return;
179 }
180
181 self.changed = true;
182 report_change!("sequences: Lifting sequence in a binary expression");
183
184 let left_last = left.exprs.pop().unwrap();
185
186 let mut exprs = left.exprs.take();
187
188 exprs.push(
189 BinExpr {
190 span: left.span,
191 op: bin.op,
192 left: left_last,
193 right: bin.right.take(),
194 }
195 .into(),
196 );
197
198 *e = SeqExpr {
199 span: bin.span,
200 exprs,
201 }
202 .into()
203 }
204 }
205
206 pub(super) fn lift_seqs_of_cond_assign(&mut self, e: &mut Expr) {
210 if !self.options.sequences() {
211 return;
212 }
213
214 let assign = match e {
215 Expr::Assign(v @ AssignExpr { op: op!("="), .. }) => v,
216 _ => return,
217 };
218
219 let cond = match &mut *assign.right {
220 Expr::Cond(v) => v,
221 _ => return,
222 };
223
224 if let Expr::Seq(test) = &mut *cond.test {
225 if test.exprs.len() >= 2 {
227 let mut new_seq = Vec::new();
228 new_seq.extend(test.exprs.drain(..test.exprs.len() - 1));
229
230 self.changed = true;
231 report_change!("sequences: Lifting sequences in a assignment with cond expr");
232 let new_cond = CondExpr {
233 span: cond.span,
234 test: test.exprs.pop().unwrap(),
235 cons: cond.cons.take(),
236 alt: cond.alt.take(),
237 };
238
239 new_seq.push(
240 AssignExpr {
241 span: assign.span,
242 op: assign.op,
243 left: assign.left.take(),
244 right: Box::new(new_cond.into()),
245 }
246 .into(),
247 );
248
249 *e = SeqExpr {
250 span: assign.span,
251 exprs: new_seq,
252 }
253 .into();
254 }
255 }
256 }
257
258 pub(super) fn merge_seq_call(&mut self, e: &mut SeqExpr) {
262 if !self.options.sequences() {
263 return;
264 }
265
266 for idx in 0..e.exprs.len() {
267 let (e1, e2) = e.exprs.split_at_mut(idx);
268
269 let a = match e1.last_mut() {
270 Some(v) => &mut **v,
271 None => continue,
272 };
273
274 let b = match e2.first_mut() {
275 Some(v) => &mut **v,
276 None => continue,
277 };
278
279 if let (
280 Expr::Assign(a_assign @ AssignExpr { op: op!("="), .. }),
281 Expr::Call(CallExpr {
282 callee: Callee::Expr(b_callee),
283 args,
284 ..
285 }),
286 ) = (&mut *a, &mut *b)
287 {
288 let var_name = a_assign.left.as_ident();
289 let var_name = match var_name {
290 Some(v) => v,
291 None => continue,
292 };
293
294 match &mut **b_callee {
295 Expr::Member(MemberExpr {
296 obj: b_callee_obj,
297 prop,
298 ..
299 }) if prop.is_ident_with("apply") || prop.is_ident_with("call") => {
300 if let Expr::Ident(b_callee_obj) = &**b_callee_obj {
302 if b_callee_obj.to_id() != var_name.to_id() {
303 continue;
304 }
305 } else {
306 continue;
307 }
308
309 let span = a_assign.span;
310
311 let obj = Box::new(a.take());
312
313 let new = CallExpr {
314 span,
315 callee: MemberExpr {
316 span: DUMMY_SP,
317 obj,
318 prop: prop.take(),
319 }
320 .as_callee(),
321 args: args.take(),
322 ..Default::default()
323 }
324 .into();
325 b.take();
326 self.changed = true;
327 report_change!(
328 "sequences: Reducing `(a = foo, a.call())` to `((a = foo).call())`"
329 );
330
331 *a = new;
332 }
333 _ => (),
334 };
335 }
336 }
337 }
338}