swc_ecma_minifier/compress/pure/
mod.rs

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    /// pass > 1
40    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    /// Visit `nodes`, maybe in parallel.
177    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        // Expression simplifier
331        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            // TODO: unify these two
799            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        // Many bundlers use this pattern
908        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                // binary expression would need an extra paren
1098                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    /// Noop.
1261    fn visit_mut_with_stmt(&mut self, _: &mut WithStmt) {}
1262}