1#![allow(clippy::needless_update)]
2
3use swc_common::{pass::Repeated, util::take::Take, SyntaxContext, DUMMY_SP};
4use swc_ecma_ast::*;
5use swc_ecma_transforms_optimization::{debug_assert_valid, simplify};
6use swc_ecma_usage_analyzer::marks::Marks;
7use swc_ecma_utils::{
8 parallel::{cpu_count, Parallel, ParallelExt},
9 ExprCtx,
10};
11use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};
12#[cfg(feature = "debug")]
13use tracing::Level;
14
15use self::{ctx::Ctx, misc::DropOpts};
16use crate::{debug::AssertValid, maybe_par, option::CompressOptions, util::ModuleItemExt};
17
18mod arrows;
19mod bools;
20mod conds;
21mod ctx;
22mod dead_code;
23mod drop_console;
24mod evaluate;
25mod if_return;
26mod loops;
27mod member_expr;
28mod misc;
29mod numbers;
30mod properties;
31mod sequences;
32mod strings;
33mod switches;
34mod unsafes;
35mod vars;
36
37#[derive(Debug, Clone, Copy)]
38pub(crate) struct PureOptimizerConfig {
39 pub enable_join_vars: bool,
41
42 pub force_str_for_tpl: bool,
43}
44
45#[allow(clippy::needless_lifetimes)]
46pub(crate) fn pure_optimizer<'a>(
47 options: &'a CompressOptions,
48 marks: Marks,
49 config: PureOptimizerConfig,
50) -> impl 'a + VisitMut + Repeated {
51 Pure {
52 options,
53 config,
54 marks,
55 expr_ctx: ExprCtx {
56 unresolved_ctxt: SyntaxContext::empty().apply_mark(marks.unresolved_mark),
57 is_unresolved_ref_safe: false,
58 in_strict: options.module,
59 remaining_depth: 6,
60 },
61 ctx: Default::default(),
62 changed: Default::default(),
63 }
64}
65
66struct Pure<'a> {
67 options: &'a CompressOptions,
68 config: PureOptimizerConfig,
69 marks: Marks,
70 expr_ctx: ExprCtx,
71
72 ctx: Ctx,
73 changed: bool,
74}
75
76impl Parallel for Pure<'_> {
77 fn create(&self) -> Self {
78 Self { ..*self }
79 }
80
81 fn merge(&mut self, other: Self) {
82 if other.changed {
83 self.changed = true;
84 }
85 }
86}
87
88impl Repeated for Pure<'_> {
89 fn changed(&self) -> bool {
90 self.changed
91 }
92
93 fn reset(&mut self) {
94 self.ctx = Default::default();
95 self.changed = false;
96 }
97}
98
99impl Pure<'_> {
100 fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
101 where
102 T: ModuleItemExt + Take,
103 Vec<T>: VisitWith<self::vars::VarWithOutInitCounter>
104 + VisitMutWith<self::vars::VarPrepender>
105 + VisitMutWith<self::vars::VarMover>
106 + VisitWith<AssertValid>,
107 {
108 self.optimize_const_if(stmts);
109
110 #[cfg(debug_assertions)]
111 {
112 stmts.visit_with(&mut AssertValid);
113 }
114
115 self.drop_unreachable_stmts(stmts);
116
117 #[cfg(debug_assertions)]
118 {
119 stmts.visit_with(&mut AssertValid);
120 }
121
122 self.drop_useless_blocks(stmts);
123
124 #[cfg(debug_assertions)]
125 {
126 stmts.visit_with(&mut AssertValid);
127 }
128
129 self.collapse_vars_without_init(stmts, VarDeclKind::Let);
130
131 #[cfg(debug_assertions)]
132 {
133 stmts.visit_with(&mut AssertValid);
134 }
135
136 self.collapse_vars_without_init(stmts, VarDeclKind::Var);
137
138 #[cfg(debug_assertions)]
139 {
140 stmts.visit_with(&mut AssertValid);
141 }
142
143 if self.config.enable_join_vars {
144 self.join_vars(stmts);
145
146 debug_assert_valid(stmts);
147 }
148
149 self.break_assignments_in_seqs(stmts);
150
151 debug_assert_valid(stmts);
152
153 stmts.retain(|s| !matches!(s.as_stmt(), Some(Stmt::Empty(..))));
154 }
155
156 fn optimize_fn_stmts(&mut self, stmts: &mut Vec<Stmt>) {
157 if !stmts.is_empty() {
158 if let Stmt::Expr(ExprStmt { expr, .. }) = &stmts[0] {
159 if let Expr::Lit(Lit::Str(v)) = &**expr {
160 if v.value == *"use asm" {
161 return;
162 }
163 }
164 }
165 }
166
167 self.remove_useless_return(stmts);
168
169 self.negate_if_terminate(stmts, true, false);
170
171 if let Some(last) = stmts.last_mut() {
172 self.drop_unused_stmt_at_end_of_fn(last);
173 }
174 }
175
176 fn visit_par<N>(&mut self, threshold_multiplier: usize, nodes: &mut Vec<N>)
178 where
179 N: for<'aa> VisitMutWith<Pure<'aa>> + Send + Sync,
180 {
181 self.maybe_par(cpu_count() * threshold_multiplier, nodes, |v, node| {
182 node.visit_mut_with(v);
183 });
184 }
185
186 fn visit_par_ref<N>(&mut self, nodes: &mut [&mut N])
187 where
188 N: for<'aa> VisitMutWith<Pure<'aa>> + Send + Sync,
189 {
190 self.maybe_par(0, nodes, |v, node| {
191 node.visit_mut_with(v);
192 });
193 }
194}
195
196impl VisitMut for Pure<'_> {
197 noop_visit_mut_type!(fail);
198
199 fn visit_mut_assign_expr(&mut self, e: &mut AssignExpr) {
200 self.do_inside_of_context(Ctx::IS_LHS_OF_ASSIGN, |this| {
201 e.left.visit_mut_children_with(this);
202 });
203
204 e.right.visit_mut_with(self);
205
206 self.compress_bin_assignment_to_left(e);
207 self.compress_bin_assignment_to_right(e);
208
209 if matches!(
210 e.op,
211 op!("-=")
212 | op!("*=")
213 | op!("/=")
214 | op!("%=")
215 | op!("**=")
216 | op!("&=")
217 | op!("|=")
218 | op!("^=")
219 | op!("<<=")
220 | op!(">>=")
221 | op!(">>>=")
222 ) {
223 self.optimize_expr_in_num_ctx(&mut e.right);
224 }
225 }
226
227 fn visit_mut_bin_expr(&mut self, e: &mut BinExpr) {
228 self.visit_mut_expr(&mut e.left);
229 self.visit_mut_expr(&mut e.right);
230
231 self.compress_cmp_with_long_op(e);
232
233 if e.op == op!(bin, "+") {
234 self.concat_tpl(&mut e.left, &mut e.right);
235 }
236
237 if matches!(
238 e.op,
239 op!(bin, "-")
240 | op!("*")
241 | op!("/")
242 | op!("%")
243 | op!("**")
244 | op!("&")
245 | op!("|")
246 | op!("^")
247 | op!("<<")
248 | op!(">>")
249 | op!(">>>")
250 ) {
251 self.optimize_expr_in_num_ctx(&mut e.left);
252 self.optimize_expr_in_num_ctx(&mut e.right);
253 }
254 }
255
256 fn visit_mut_block_stmt_or_expr(&mut self, body: &mut BlockStmtOrExpr) {
257 body.visit_mut_children_with(self);
258
259 match body {
260 BlockStmtOrExpr::BlockStmt(b) => self.optimize_fn_stmts(&mut b.stmts),
261 BlockStmtOrExpr::Expr(_) => {}
262 #[cfg(swc_ast_unknown)]
263 _ => panic!("unable to access unknown nodes"),
264 }
265
266 self.optimize_arrow_body(body);
267
268 if let BlockStmtOrExpr::Expr(e) = body {
269 self.make_bool_short(e, false, false);
270 }
271 }
272
273 fn visit_mut_call_expr(&mut self, e: &mut CallExpr) {
274 self.do_inside_of_context(Ctx::IS_CALLEE, |this| {
275 e.callee.visit_mut_with(this);
276 });
277
278 self.do_outside_of_context(Ctx::IS_CALLEE, |this| {
279 e.args.visit_mut_with(this);
280 });
281
282 self.eval_spread_array_in_args(&mut e.args);
283
284 self.drop_arguments_of_symbol_call(e);
285 }
286
287 fn visit_mut_class_member(&mut self, m: &mut ClassMember) {
288 m.visit_mut_children_with(self);
289
290 if let ClassMember::StaticBlock(sb) = m {
291 if sb.body.stmts.is_empty() {
292 *m = ClassMember::Empty(EmptyStmt { span: DUMMY_SP });
293 }
294 }
295 }
296
297 fn visit_mut_class_members(&mut self, m: &mut Vec<ClassMember>) {
298 self.visit_par(2, m);
299
300 m.retain(|m| {
301 if let ClassMember::Empty(..) = m {
302 return false;
303 }
304
305 true
306 });
307 }
308
309 fn visit_mut_cond_expr(&mut self, e: &mut CondExpr) {
310 e.visit_mut_children_with(self);
311
312 self.optimize_expr_in_bool_ctx(&mut e.test, false);
313 }
314
315 fn visit_mut_do_while_stmt(&mut self, s: &mut DoWhileStmt) {
316 s.test.visit_mut_with(self);
317
318 self.do_inside_of_context(Ctx::PRESERVE_BLOCK, |this| {
319 s.body.visit_mut_with(this);
320 });
321
322 self.make_bool_short(&mut s.test, true, false);
323 }
324
325 fn visit_mut_expr(&mut self, e: &mut Expr) {
326 self.handle_known_delete(e);
327
328 e.visit_mut_children_with(self);
329
330 match e {
332 Expr::Member(..) => {
333 if !self.ctx.intersects(
334 Ctx::IN_DELETE
335 .union(Ctx::IS_UPDATE_ARG)
336 .union(Ctx::IS_LHS_OF_ASSIGN)
337 .union(Ctx::IN_OPT_CHAIN),
338 ) {
339 let mut changed = false;
340 simplify::expr::optimize_member_expr(
341 self.expr_ctx,
342 e,
343 self.ctx.contains(Ctx::IS_CALLEE),
344 &mut changed,
345 );
346
347 if changed {
348 report_change!("expression simplifier simplified a member expression");
349 }
350 }
351 }
352
353 Expr::Unary(..) => {
354 let mut changed = false;
355 simplify::expr::optimize_unary_expr(self.expr_ctx, e, &mut changed);
356
357 if changed {
358 report_change!("expression simplifier simplified a unary expression");
359 self.changed = true;
360 }
361 }
362
363 Expr::Bin(..) => {
364 let mut changed = false;
365 simplify::expr::optimize_bin_expr(self.expr_ctx, e, &mut changed);
366
367 if changed {
368 report_change!("expression simplifier simplified a binary expression");
369 self.changed = true;
370 }
371 }
372
373 _ => {}
374 }
375
376 match e {
377 Expr::Seq(seq) => {
378 if seq.exprs.len() == 1 {
379 *e = *seq.exprs.pop().unwrap();
380 }
381 }
382 Expr::Invalid(..) | Expr::Lit(..) => return,
383
384 _ => {}
385 }
386
387 if e.is_seq() {
388 debug_assert_valid(e);
389 }
390
391 self.simplify_assign_expr(e);
392
393 if self.options.unused {
394 if let Expr::Unary(UnaryExpr {
395 span,
396 op: op!("void"),
397 arg,
398 }) = e
399 {
400 if !arg.is_lit() {
401 self.ignore_return_value(
402 arg,
403 DropOpts::DROP_GLOBAL_REFS_IF_UNUSED
404 .union(DropOpts::DROP_NUMBER)
405 .union(DropOpts::DROP_STR_LIT),
406 );
407 if arg.is_invalid() {
408 *e = *Expr::undefined(*span);
409 return;
410 }
411 }
412 }
413 }
414
415 if let Expr::Bin(bin) = e {
416 let expr = self.optimize_lit_cmp(bin);
417 if let Some(expr) = expr {
418 report_change!("Optimizing: Literal comparison");
419 self.changed = true;
420 *e = expr;
421 }
422 }
423
424 if e.is_seq() {
425 debug_assert_valid(e);
426 }
427
428 self.eval_nested_tpl(e);
429
430 if e.is_seq() {
431 debug_assert_valid(e);
432 }
433
434 self.eval_tpl_as_str(e);
435
436 if e.is_seq() {
437 debug_assert_valid(e);
438 }
439
440 self.eval_str_addition(e);
441
442 if self.changed {
443 self.remove_invalid(e);
444 }
445
446 let changed = self.drop_console(e);
447
448 if changed {
449 self.remove_invalid(e);
450 }
451
452 if let Expr::Seq(seq) = e {
453 if seq.exprs.is_empty() {
454 *e = Invalid { span: DUMMY_SP }.into();
455 return;
456 }
457 if seq.exprs.len() == 1 {
458 self.changed = true;
459 *e = *seq.exprs.take().into_iter().next().unwrap();
460 }
461 }
462
463 self.eval_array_spread(e);
464
465 self.compress_array_join(e);
466
467 if e.is_seq() {
468 debug_assert_valid(e);
469 }
470
471 self.unsafe_optimize_fn_as_arrow(e);
472
473 if e.is_seq() {
474 debug_assert_valid(e);
475 }
476
477 self.eval_opt_chain(e);
478
479 if e.is_seq() {
480 debug_assert_valid(e);
481 }
482
483 self.eval_number_call(e);
484
485 if e.is_seq() {
486 debug_assert_valid(e);
487 }
488
489 self.eval_arguments_member_access(e);
490
491 if e.is_seq() {
492 debug_assert_valid(e);
493 }
494
495 self.eval_number_method_call(e);
496
497 if e.is_seq() {
498 debug_assert_valid(e);
499 }
500
501 self.swap_bin_operands(e);
502
503 if e.is_seq() {
504 debug_assert_valid(e);
505 }
506
507 self.drop_logical_operands(e);
508
509 if e.is_seq() {
510 debug_assert_valid(e);
511 }
512
513 self.optimize_negate_eq(e);
514
515 self.lift_minus(e);
516 self.optimize_to_number(e);
517
518 if e.is_seq() {
519 debug_assert_valid(e);
520 }
521 self.convert_tpl_to_str(e);
522
523 if e.is_seq() {
524 debug_assert_valid(e);
525 }
526
527 self.drop_useless_addition_of_str(e);
528
529 if e.is_seq() {
530 debug_assert_valid(e);
531 }
532
533 self.compress_useless_deletes(e);
534
535 if e.is_seq() {
536 debug_assert_valid(e);
537 }
538
539 self.remove_useless_logical_rhs(e);
540
541 if e.is_seq() {
542 debug_assert_valid(e);
543 }
544
545 self.handle_negated_seq(e);
546
547 if e.is_seq() {
548 debug_assert_valid(e);
549 }
550
551 self.concat_str(e);
552
553 if e.is_seq() {
554 debug_assert_valid(e);
555 }
556
557 self.eval_array_method_call(e);
558
559 if e.is_seq() {
560 debug_assert_valid(e);
561 }
562 self.eval_fn_method_call(e);
563
564 if e.is_seq() {
565 debug_assert_valid(e);
566 }
567
568 self.eval_str_method_call(e);
569
570 if e.is_seq() {
571 debug_assert_valid(e);
572 }
573
574 self.optimize_const_cond(e);
575
576 self.compress_conds_as_logical(e);
577
578 if e.is_seq() {
579 debug_assert_valid(e);
580 }
581
582 self.compress_cond_with_logical_as_logical(e);
583
584 if e.is_seq() {
585 debug_assert_valid(e);
586 }
587
588 self.compress_conds_as_arithmetic(e);
589
590 self.eval_logical_expr(e);
591
592 if e.is_seq() {
593 debug_assert_valid(e);
594 }
595
596 self.lift_seqs_of_bin(e);
597
598 if e.is_seq() {
599 debug_assert_valid(e);
600 }
601
602 self.lift_seqs_of_cond_assign(e);
603
604 if e.is_seq() {
605 debug_assert_valid(e);
606 }
607
608 self.optimize_nullish_coalescing(e);
609
610 if e.is_seq() {
611 debug_assert_valid(e);
612 }
613
614 self.compress_negated_bin_eq(e);
615
616 if e.is_seq() {
617 debug_assert_valid(e);
618 }
619
620 self.compress_useless_cond_expr(e);
621
622 if e.is_seq() {
623 debug_assert_valid(e);
624 }
625
626 self.optimize_builtin_object(e);
627
628 if e.is_seq() {
629 debug_assert_valid(e);
630 }
631
632 self.optimize_opt_chain(e);
633
634 if e.is_seq() {
635 debug_assert_valid(e);
636 }
637
638 self.eval_member_expr(e);
639
640 self.optimize_to_int(e);
641 }
642
643 fn visit_mut_expr_or_spreads(&mut self, nodes: &mut Vec<ExprOrSpread>) {
644 self.visit_par(8, nodes);
645 }
646
647 fn visit_mut_expr_stmt(&mut self, s: &mut ExprStmt) {
648 s.visit_mut_children_with(self);
649
650 if s.expr.is_seq() {
651 debug_assert_valid(&s.expr);
652 }
653
654 self.ignore_return_value(
655 &mut s.expr,
656 DropOpts::DROP_NUMBER.union(DropOpts::DROP_GLOBAL_REFS_IF_UNUSED),
657 );
658
659 if s.expr.is_invalid() {
660 return;
661 }
662
663 debug_assert_valid(&s.expr);
664
665 self.make_bool_short(&mut s.expr, false, true);
666 }
667
668 fn visit_mut_exprs(&mut self, nodes: &mut Vec<Box<Expr>>) {
669 self.visit_par(16, nodes);
670 }
671
672 fn visit_mut_fn_decl(&mut self, n: &mut FnDecl) {
673 #[cfg(feature = "debug")]
674 let _tracing = tracing::span!(
675 Level::ERROR,
676 "visit_mut_fn_decl",
677 id = tracing::field::display(&n.ident)
678 )
679 .entered();
680
681 n.visit_mut_children_with(self);
682 }
683
684 fn visit_mut_for_head(&mut self, head: &mut ForHead) {
685 self.do_inside_of_context(Ctx::IS_UPDATE_ARG, |this| {
686 head.visit_mut_children_with(this)
687 });
688 }
689
690 fn visit_mut_for_in_stmt(&mut self, n: &mut ForInStmt) {
691 n.right.visit_mut_with(self);
692
693 n.left.visit_mut_with(self);
694
695 n.body.visit_mut_with(self);
696
697 if let Stmt::Block(body) = &mut *n.body {
698 self.negate_if_terminate(&mut body.stmts, false, true);
699 }
700
701 self.make_bool_short(&mut n.right, false, false);
702 }
703
704 fn visit_mut_for_of_stmt(&mut self, n: &mut ForOfStmt) {
705 n.right.visit_mut_with(self);
706
707 n.left.visit_mut_with(self);
708
709 n.body.visit_mut_with(self);
710
711 if let Stmt::Block(body) = &mut *n.body {
712 self.negate_if_terminate(&mut body.stmts, false, true);
713 }
714
715 self.make_bool_short(&mut n.right, false, false);
716 }
717
718 fn visit_mut_for_stmt(&mut self, s: &mut ForStmt) {
719 s.visit_mut_children_with(self);
720
721 self.optimize_for_init(&mut s.init);
722
723 self.optimize_for_update(&mut s.update);
724
725 self.optimize_for_if_break(s);
726
727 self.merge_for_if_break(s);
728
729 if let Some(test) = &mut s.test {
730 self.optimize_expr_in_bool_ctx(test, false);
731 }
732
733 if let Stmt::Block(body) = &mut *s.body {
734 self.negate_if_terminate(&mut body.stmts, false, true);
735 }
736 }
737
738 fn visit_mut_function(&mut self, f: &mut Function) {
739 self.do_outside_of_context(Ctx::IN_TRY_BLOCK, |this| f.visit_mut_children_with(this));
740
741 if let Some(body) = &mut f.body {
742 self.optimize_fn_stmts(&mut body.stmts)
743 }
744 }
745
746 fn visit_mut_if_stmt(&mut self, s: &mut IfStmt) {
747 s.test.visit_mut_with(self);
748
749 match &mut s.alt {
750 Some(alt) => {
751 self.do_outside_of_context(Ctx::PRESERVE_BLOCK, |this| {
752 this.visit_par_ref(&mut [&mut *s.cons, &mut **alt]);
753 });
754 }
755 None => {
756 self.do_outside_of_context(Ctx::PRESERVE_BLOCK, |this| {
757 s.cons.visit_mut_with(this);
758 });
759 }
760 }
761
762 self.optimize_expr_in_bool_ctx(&mut s.test, false);
763
764 self.merge_nested_if(s);
765
766 self.merge_else_if(s);
767
768 self.make_bool_short(&mut s.test, true, false);
769 }
770
771 fn visit_mut_key_value_prop(&mut self, p: &mut KeyValueProp) {
772 p.visit_mut_children_with(self);
773
774 self.make_bool_short(&mut p.value, false, false);
775 }
776
777 fn visit_mut_labeled_stmt(&mut self, s: &mut LabeledStmt) {
778 self.do_inside_of_context(Ctx::IS_LABEL_BODY, |this| {
779 s.body.visit_mut_with(this);
780 });
781 }
782
783 fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) {
784 self.do_outside_of_context(Ctx::IS_CALLEE.union(Ctx::IS_UPDATE_ARG), |this| {
785 e.obj.visit_mut_with(this);
786 });
787
788 if let MemberProp::Computed(c) = &mut e.prop {
789 self.do_outside_of_context(
790 Ctx::IS_CALLEE
791 .union(Ctx::IS_UPDATE_ARG)
792 .union(Ctx::IS_LHS_OF_ASSIGN),
793 |this| {
794 c.visit_mut_with(this);
795 },
796 );
797
798 if let Some(ident) = self.optimize_property_of_member_expr(Some(&e.obj), c) {
800 e.prop = MemberProp::Ident(ident);
801 return;
802 };
803
804 if let Some(ident) = self.handle_known_computed_member_expr(c) {
805 e.prop = MemberProp::Ident(ident)
806 };
807 }
808 }
809
810 fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
811 self.visit_par(1, items);
812
813 self.handle_stmt_likes(items);
814 }
815
816 fn visit_mut_new_expr(&mut self, e: &mut NewExpr) {
817 self.do_inside_of_context(Ctx::IS_CALLEE, |this| {
818 e.callee.visit_mut_with(this);
819 });
820
821 self.do_outside_of_context(Ctx::IS_CALLEE, |this| {
822 e.args.visit_mut_with(this);
823 });
824 }
825
826 fn visit_mut_object_lit(&mut self, e: &mut ObjectLit) {
827 e.visit_mut_children_with(self);
828
829 self.eval_spread_object(e);
830 }
831
832 fn visit_mut_object_pat(&mut self, p: &mut ObjectPat) {
833 p.visit_mut_children_with(self);
834 }
835
836 fn visit_mut_opt_call(&mut self, opt_call: &mut OptCall) {
837 self.do_inside_of_context(Ctx::IS_CALLEE, |this| {
838 opt_call.callee.visit_mut_with(this);
839 });
840
841 opt_call.args.visit_mut_with(self);
842
843 self.eval_spread_array_in_args(&mut opt_call.args);
844 }
845
846 fn visit_mut_opt_chain_expr(&mut self, e: &mut OptChainExpr) {
847 self.do_inside_of_context(Ctx::IN_OPT_CHAIN, |this| {
848 e.visit_mut_children_with(this);
849 });
850 }
851
852 fn visit_mut_opt_var_decl_or_expr(&mut self, n: &mut Option<VarDeclOrExpr>) {
853 n.visit_mut_children_with(self);
854
855 if self.options.side_effects {
856 if let Some(VarDeclOrExpr::Expr(e)) = n {
857 self.ignore_return_value(
858 e,
859 DropOpts::DROP_GLOBAL_REFS_IF_UNUSED
860 .union(DropOpts::DROP_NUMBER)
861 .union(DropOpts::DROP_STR_LIT),
862 );
863 if e.is_invalid() {
864 *n = None;
865 }
866 }
867 }
868
869 if let Some(VarDeclOrExpr::Expr(e)) = n {
870 if e.is_invalid() {
871 *n = None;
872 }
873 }
874 }
875
876 fn visit_mut_opt_vec_expr_or_spreads(&mut self, nodes: &mut Vec<Option<ExprOrSpread>>) {
877 self.visit_par(4, nodes);
878
879 self.eval_spread_array_in_array(nodes);
880 }
881
882 fn visit_mut_pat(&mut self, p: &mut Pat) {
883 p.visit_mut_children_with(self);
884
885 self.drop_neeedless_pat(p);
886 }
887
888 fn visit_mut_prop(&mut self, p: &mut Prop) {
889 p.visit_mut_children_with(self);
890
891 self.optimize_arrow_method_prop(p);
892
893 #[cfg(debug_assertions)]
894 {
895 p.visit_with(&mut AssertValid);
896 }
897 }
898
899 fn visit_mut_prop_name(&mut self, p: &mut PropName) {
900 p.visit_mut_children_with(self);
901
902 self.optimize_computed_prop_name_as_normal(p);
903 self.optimize_prop_name(p);
904 }
905
906 fn visit_mut_prop_or_spreads(&mut self, exprs: &mut Vec<PropOrSpread>) {
907 self.visit_par(2, exprs);
909 }
910
911 fn visit_mut_return_stmt(&mut self, s: &mut ReturnStmt) {
912 s.visit_mut_children_with(self);
913
914 self.drop_undefined_from_return_arg(s);
915
916 if let Some(e) = &mut s.arg {
917 self.make_bool_short(e, false, false);
918 }
919 }
920
921 fn visit_mut_seq_expr(&mut self, e: &mut SeqExpr) {
922 e.exprs.visit_mut_with(self);
923
924 self.shift_void(e);
925
926 self.shift_assignment(e);
927
928 let exprs = &e.exprs;
929 if maybe_par!(
930 exprs.iter().any(|e| e.is_seq()),
931 *crate::LIGHT_TASK_PARALLELS
932 ) {
933 let mut exprs = Vec::new();
934
935 for e in e.exprs.take() {
936 if let Expr::Seq(seq) = *e {
937 exprs.extend(seq.exprs);
938 } else {
939 exprs.push(e);
940 }
941 }
942
943 e.exprs = exprs;
944 }
945
946 e.exprs.retain(|e| {
947 if e.is_invalid() {
948 self.changed = true;
949 report_change!("Removing invalid expr in seq");
950 return false;
951 }
952
953 true
954 });
955
956 if e.exprs.is_empty() {
957 return;
958 }
959
960 self.eval_trivial_values_in_expr(e);
961
962 self.merge_seq_call(e);
963
964 let can_change_this =
965 !self.ctx.contains(Ctx::IS_CALLEE) || !e.exprs.last().unwrap().directness_matters();
966
967 let len = e.exprs.len();
968 for (idx, e) in e.exprs.iter_mut().enumerate() {
969 let is_last = idx == len - 1;
970
971 if !is_last {
972 self.ignore_return_value(e, DropOpts::DROP_NUMBER.union(DropOpts::DROP_STR_LIT));
973 }
974 }
975
976 e.exprs.retain(|e| !e.is_invalid());
977
978 if !can_change_this && e.exprs.len() == 1 {
979 e.exprs.insert(0, 0.into());
980 }
981
982 #[cfg(debug_assertions)]
983 {
984 e.exprs.visit_with(&mut AssertValid);
985 }
986 }
987
988 fn visit_mut_stmt(&mut self, s: &mut Stmt) {
989 self.do_outside_of_context(
990 Ctx::IS_UPDATE_ARG
991 .union(Ctx::IS_CALLEE)
992 .union(Ctx::IN_DELETE)
993 .union(Ctx::PRESERVE_BLOCK)
994 .union(Ctx::IS_LABEL_BODY),
995 |this| {
996 s.visit_mut_children_with(this);
997 },
998 );
999
1000 match s {
1001 Stmt::Expr(ExprStmt { expr, .. }) => {
1002 if expr.is_invalid() {
1003 *s = Stmt::dummy();
1004 return;
1005 }
1006
1007 self.ignore_return_value(expr, DropOpts::DROP_NUMBER);
1008
1009 if expr.is_invalid() {
1010 *s = Stmt::dummy();
1011 self.changed = true;
1012 report_change!("Dropping an invalid expression statement");
1013 return;
1014 }
1015 }
1016
1017 Stmt::Block(bs) => {
1018 if bs.stmts.is_empty() {
1019 *s = Stmt::dummy();
1020 self.changed = true;
1021 report_change!("Dropping an empty block statement");
1022 return;
1023 }
1024 }
1025 _ => {}
1026 }
1027
1028 debug_assert_valid(s);
1029
1030 if self.options.drop_debugger {
1031 if let Stmt::Debugger(..) = s {
1032 self.changed = true;
1033 *s = EmptyStmt { span: DUMMY_SP }.into();
1034 report_change!("drop_debugger: Dropped a debugger statement");
1035 return;
1036 }
1037 }
1038
1039 if !self.ctx.contains(Ctx::PRESERVE_BLOCK) {
1040 self.drop_needless_block(s);
1041
1042 debug_assert_valid(s);
1043 }
1044
1045 self.optimize_empty_try_stmt(s);
1046
1047 debug_assert_valid(s);
1048
1049 self.optimize_meaningless_try(s);
1050
1051 debug_assert_valid(s);
1052
1053 self.optimize_loops_with_constant_condition(s);
1054
1055 debug_assert_valid(s);
1056
1057 self.loop_to_for_stmt(s);
1058
1059 debug_assert_valid(s);
1060
1061 self.handle_instant_break(s);
1062
1063 debug_assert_valid(s);
1064
1065 self.optimize_labeled_stmt(s);
1066
1067 debug_assert_valid(s);
1068
1069 self.drop_useless_continue(s);
1070
1071 debug_assert_valid(s);
1072
1073 self.optimize_body_of_loop_stmt(s);
1074
1075 debug_assert_valid(s);
1076
1077 self.optimize_switch_stmt(s);
1078
1079 debug_assert_valid(s);
1080
1081 self.compress_if_stmt_as_expr(s);
1082
1083 debug_assert_valid(s);
1084
1085 if let Stmt::Expr(es) = s {
1086 if es.expr.is_invalid() {
1087 *s = EmptyStmt { span: DUMMY_SP }.into();
1088 return;
1089 }
1090 }
1091
1092 if let Stmt::Block(block) = s {
1093 let span = block.span;
1094 if let [Stmt::Expr(e), Stmt::Return(ReturnStmt { arg: None, .. })] =
1095 &mut block.stmts[..]
1096 {
1097 if !(e.expr.is_bin() || e.expr.is_assign() || e.expr.is_seq()) {
1099 self.changed = true;
1100 report_change!("sequences: Merge expression with return");
1101
1102 let e = e.expr.take();
1103
1104 *s = Stmt::Return(ReturnStmt {
1105 span,
1106 arg: Some(Box::new(Expr::Unary(UnaryExpr {
1107 span: DUMMY_SP,
1108 op: op!("void"),
1109 arg: e,
1110 }))),
1111 })
1112 }
1113 }
1114 }
1115
1116 debug_assert_valid(s);
1117 }
1118
1119 fn visit_mut_stmts(&mut self, items: &mut Vec<Stmt>) {
1120 if !items.is_empty() {
1121 if let Stmt::Expr(ExprStmt { expr, .. }) = &items[0] {
1122 if let Expr::Lit(Lit::Str(v)) = &**expr {
1123 if v.value == *"use asm" {
1124 return;
1125 }
1126 }
1127 }
1128 }
1129
1130 self.visit_par(1, items);
1131
1132 self.handle_stmt_likes(items);
1133
1134 #[cfg(debug_assertions)]
1135 {
1136 items.visit_with(&mut AssertValid);
1137 }
1138 }
1139
1140 fn visit_mut_super_prop_expr(&mut self, e: &mut SuperPropExpr) {
1141 if let SuperProp::Computed(c) = &mut e.prop {
1142 c.visit_mut_with(self);
1143
1144 if let Some(ident) = self.optimize_property_of_member_expr(None, c) {
1145 e.prop = SuperProp::Ident(ident);
1146 return;
1147 };
1148
1149 if let Some(ident) = self.handle_known_computed_member_expr(c) {
1150 e.prop = SuperProp::Ident(ident)
1151 };
1152 }
1153 }
1154
1155 fn visit_mut_switch_cases(&mut self, n: &mut Vec<SwitchCase>) {
1156 self.visit_par(4, n);
1157
1158 self.optimize_switch_cases(n);
1159 }
1160
1161 fn visit_mut_tagged_tpl(&mut self, n: &mut TaggedTpl) {
1162 self.do_inside_of_context(Ctx::IS_CALLEE, |this| {
1163 n.tag.visit_mut_with(this);
1164 });
1165
1166 self.do_outside_of_context(Ctx::IS_CALLEE, |this| {
1167 n.tpl.exprs.visit_mut_with(this);
1168 });
1169 }
1170
1171 fn visit_mut_throw_stmt(&mut self, s: &mut ThrowStmt) {
1172 s.visit_mut_children_with(self);
1173
1174 self.make_bool_short(&mut s.arg, false, false);
1175 }
1176
1177 fn visit_mut_tpl(&mut self, n: &mut Tpl) {
1178 self.do_outside_of_context(Ctx::IS_CALLEE, |this| {
1179 n.visit_mut_children_with(this);
1180 });
1181
1182 debug_assert_eq!(n.exprs.len() + 1, n.quasis.len());
1183
1184 self.compress_tpl(n);
1185
1186 debug_assert_eq!(
1187 n.exprs.len() + 1,
1188 n.quasis.len(),
1189 "tagged template literal compressor created an invalid template literal"
1190 );
1191 }
1192
1193 fn visit_mut_try_stmt(&mut self, n: &mut TryStmt) {
1194 self.do_inside_of_context(Ctx::IN_TRY_BLOCK, |this| {
1195 n.block.visit_mut_with(this);
1196 });
1197
1198 n.handler.visit_mut_with(self);
1199
1200 n.finalizer.visit_mut_with(self);
1201 }
1202
1203 fn visit_mut_unary_expr(&mut self, e: &mut UnaryExpr) {
1204 if e.op == op!("delete") {
1205 self.do_inside_of_context(Ctx::IN_DELETE, |this| {
1206 e.visit_mut_children_with(this);
1207 })
1208 } else {
1209 self.do_outside_of_context(Ctx::IN_DELETE, |this| {
1210 e.visit_mut_children_with(this);
1211 })
1212 }
1213
1214 match e.op {
1215 op!("!") => {
1216 self.optimize_expr_in_bool_ctx(&mut e.arg, false);
1217 }
1218
1219 op!(unary, "+") | op!(unary, "-") | op!("~") => {
1220 self.optimize_expr_in_num_ctx(&mut e.arg);
1221 }
1222 _ => {}
1223 }
1224 }
1225
1226 fn visit_mut_update_expr(&mut self, e: &mut UpdateExpr) {
1227 self.do_inside_of_context(Ctx::IS_UPDATE_ARG, |this| {
1228 e.visit_mut_children_with(this);
1229 });
1230 }
1231
1232 fn visit_mut_var_decl(&mut self, v: &mut VarDecl) {
1233 v.visit_mut_children_with(self);
1234
1235 if v.kind == VarDeclKind::Var {
1236 self.remove_duplicate_vars(&mut v.decls);
1237 }
1238 }
1239
1240 fn visit_mut_var_declarator(&mut self, v: &mut VarDeclarator) {
1241 v.visit_mut_children_with(self);
1242
1243 if let Some(init) = &mut v.init {
1244 self.make_bool_short(init, false, false);
1245 }
1246 }
1247
1248 fn visit_mut_var_declarators(&mut self, nodes: &mut Vec<VarDeclarator>) {
1249 self.visit_par(8, nodes);
1250 }
1251
1252 fn visit_mut_while_stmt(&mut self, s: &mut WhileStmt) {
1253 s.visit_mut_children_with(self);
1254
1255 self.optimize_expr_in_bool_ctx(&mut s.test, false);
1256
1257 self.make_bool_short(&mut s.test, true, false);
1258 }
1259
1260 fn visit_mut_with_stmt(&mut self, _: &mut WithStmt) {}
1262}