turbopack_ecmascript/analyzer/
graph.rs

1use std::{
2    iter,
3    mem::{replace, take},
4    sync::Arc,
5};
6
7use rustc_hash::{FxHashMap, FxHashSet};
8use swc_core::{
9    atoms::Atom,
10    common::{GLOBALS, Mark, Span, Spanned, SyntaxContext, comments::Comments, pass::AstNodePath},
11    ecma::{
12        ast::*,
13        atoms::atom,
14        utils::contains_ident_ref,
15        visit::{fields::*, *},
16    },
17};
18use turbo_rcstr::RcStr;
19use turbo_tasks::ResolvedVc;
20use turbopack_core::source::Source;
21
22use super::{
23    ConstantNumber, ConstantValue, ImportMap, JsValue, ObjectPart, WellKnownFunctionKind,
24    is_unresolved_id,
25};
26use crate::{
27    SpecifiedModuleType,
28    analyzer::{WellKnownObjectKind, is_unresolved},
29    utils::{AstPathRange, unparen},
30};
31
32#[derive(Debug, Clone)]
33pub struct EffectsBlock {
34    pub effects: Vec<Effect>,
35    pub range: AstPathRange,
36}
37
38impl EffectsBlock {
39    pub fn is_empty(&self) -> bool {
40        self.effects.is_empty()
41    }
42}
43
44#[derive(Debug, Clone)]
45pub enum ConditionalKind {
46    /// The blocks of an `if` statement without an `else` block.
47    If { then: Box<EffectsBlock> },
48    /// The blocks of an `if ... else` or `if { ... return ... } ...` statement.
49    IfElse {
50        then: Box<EffectsBlock>,
51        r#else: Box<EffectsBlock>,
52    },
53    /// The blocks of an `if ... else` statement.
54    Else { r#else: Box<EffectsBlock> },
55    /// The blocks of an `if { ... return ... } else { ... } ...` or `if { ... }
56    /// else { ... return ... } ...` statement.
57    IfElseMultiple {
58        then: Vec<Box<EffectsBlock>>,
59        r#else: Vec<Box<EffectsBlock>>,
60    },
61    /// The expressions on the right side of the `?:` operator.
62    Ternary {
63        then: Box<EffectsBlock>,
64        r#else: Box<EffectsBlock>,
65    },
66    /// The expression on the right side of the `&&` operator.
67    And { expr: Box<EffectsBlock> },
68    /// The expression on the right side of the `||` operator.
69    Or { expr: Box<EffectsBlock> },
70    /// The expression on the right side of the `??` operator.
71    NullishCoalescing { expr: Box<EffectsBlock> },
72    /// The expression on the right side of a labeled statement.
73    Labeled { body: Box<EffectsBlock> },
74}
75
76impl ConditionalKind {
77    /// Normalizes all contained values.
78    pub fn normalize(&mut self) {
79        match self {
80            ConditionalKind::If { then: block }
81            | ConditionalKind::Else { r#else: block }
82            | ConditionalKind::And { expr: block, .. }
83            | ConditionalKind::Or { expr: block, .. }
84            | ConditionalKind::NullishCoalescing { expr: block, .. } => {
85                for effect in &mut block.effects {
86                    effect.normalize();
87                }
88            }
89            ConditionalKind::IfElse { then, r#else, .. }
90            | ConditionalKind::Ternary { then, r#else, .. } => {
91                for effect in &mut then.effects {
92                    effect.normalize();
93                }
94                for effect in &mut r#else.effects {
95                    effect.normalize();
96                }
97            }
98            ConditionalKind::IfElseMultiple { then, r#else, .. } => {
99                for block in then.iter_mut().chain(r#else.iter_mut()) {
100                    for effect in &mut block.effects {
101                        effect.normalize();
102                    }
103                }
104            }
105            ConditionalKind::Labeled { body } => {
106                for effect in &mut body.effects {
107                    effect.normalize();
108                }
109            }
110        }
111    }
112}
113
114#[derive(Debug, Clone)]
115pub enum EffectArg {
116    Value(JsValue),
117    Closure(JsValue, Box<EffectsBlock>),
118    Spread,
119}
120
121impl EffectArg {
122    /// Normalizes all contained values.
123    pub fn normalize(&mut self) {
124        match self {
125            EffectArg::Value(value) => value.normalize(),
126            EffectArg::Closure(value, effects) => {
127                value.normalize();
128                for effect in &mut effects.effects {
129                    effect.normalize();
130                }
131            }
132            EffectArg::Spread => {}
133        }
134    }
135}
136
137#[derive(Debug, Clone)]
138pub enum Effect {
139    /// Some condition which affects which effects might be executed. If the
140    /// condition evaluates to some compile-time constant, we can use that
141    /// to determine which effects are executed and remove the others.
142    Conditional {
143        condition: Box<JsValue>,
144        kind: Box<ConditionalKind>,
145        /// The ast path to the condition.
146        ast_path: Vec<AstParentKind>,
147        span: Span,
148        in_try: bool,
149    },
150    /// A function call or a new call of a function.
151    Call {
152        func: Box<JsValue>,
153        args: Vec<EffectArg>,
154        ast_path: Vec<AstParentKind>,
155        span: Span,
156        in_try: bool,
157        new: bool,
158    },
159    /// A function call or a new call of a property of an object.
160    MemberCall {
161        obj: Box<JsValue>,
162        prop: Box<JsValue>,
163        args: Vec<EffectArg>,
164        ast_path: Vec<AstParentKind>,
165        span: Span,
166        in_try: bool,
167        new: bool,
168    },
169    /// A property access.
170    Member {
171        obj: Box<JsValue>,
172        prop: Box<JsValue>,
173        ast_path: Vec<AstParentKind>,
174        span: Span,
175        in_try: bool,
176    },
177    /// A reference to an imported binding.
178    ImportedBinding {
179        esm_reference_index: usize,
180        export: Option<RcStr>,
181        ast_path: Vec<AstParentKind>,
182        span: Span,
183        in_try: bool,
184    },
185    /// A reference to a free var access.
186    FreeVar {
187        var: Box<JsValue>,
188        ast_path: Vec<AstParentKind>,
189        span: Span,
190        in_try: bool,
191    },
192    /// A typeof expression
193    TypeOf {
194        arg: Box<JsValue>,
195        ast_path: Vec<AstParentKind>,
196        span: Span,
197    },
198    // TODO ImportMeta should be replaced with Member
199    /// A reference to `import.meta`.
200    ImportMeta {
201        ast_path: Vec<AstParentKind>,
202        span: Span,
203        in_try: bool,
204    },
205    /// Unreachable code, e.g. after a `return` statement.
206    Unreachable { start_ast_path: Vec<AstParentKind> },
207}
208
209impl Effect {
210    /// Normalizes all contained values.
211    pub fn normalize(&mut self) {
212        match self {
213            Effect::Conditional {
214                condition, kind, ..
215            } => {
216                condition.normalize();
217                kind.normalize();
218            }
219            Effect::Call { func, args, .. } => {
220                func.normalize();
221                for arg in args.iter_mut() {
222                    arg.normalize();
223                }
224            }
225            Effect::MemberCall {
226                obj, prop, args, ..
227            } => {
228                obj.normalize();
229                prop.normalize();
230                for arg in args.iter_mut() {
231                    arg.normalize();
232                }
233            }
234            Effect::Member { obj, prop, .. } => {
235                obj.normalize();
236                prop.normalize();
237            }
238            Effect::FreeVar { var, .. } => {
239                var.normalize();
240            }
241            Effect::ImportedBinding { .. } => {}
242            Effect::TypeOf { arg, .. } => {
243                arg.normalize();
244            }
245            Effect::ImportMeta { .. } => {}
246            Effect::Unreachable { .. } => {}
247        }
248    }
249}
250
251#[derive(Debug)]
252pub struct VarGraph {
253    pub values: FxHashMap<Id, JsValue>,
254    /// Map FreeVar names to their Id to facilitate lookups into [values]
255    pub free_var_ids: FxHashMap<Atom, Id>,
256
257    pub effects: Vec<Effect>,
258}
259
260impl VarGraph {
261    pub fn normalize(&mut self) {
262        for value in self.values.values_mut() {
263            value.normalize();
264        }
265        for effect in self.effects.iter_mut() {
266            effect.normalize();
267        }
268    }
269}
270
271/// You should use same [Mark] for this function and
272/// [swc_ecma_transforms_base::resolver::resolver_with_mark]
273pub fn create_graph(m: &Program, eval_context: &EvalContext) -> VarGraph {
274    let mut graph = VarGraph {
275        values: Default::default(),
276        free_var_ids: Default::default(),
277        effects: Default::default(),
278    };
279
280    m.visit_with_ast_path(
281        &mut Analyzer {
282            data: &mut graph,
283            eval_context,
284            effects: Default::default(),
285            hoisted_effects: Default::default(),
286            early_return_stack: Default::default(),
287            var_decl_kind: Default::default(),
288            current_value: Default::default(),
289            cur_fn_return_values: Default::default(),
290            cur_fn_ident: Default::default(),
291        },
292        &mut Default::default(),
293    );
294
295    graph.normalize();
296
297    graph
298}
299
300/// A context used for assembling the evaluation graph.
301#[derive(Debug)]
302pub struct EvalContext {
303    pub(crate) unresolved_mark: Mark,
304    pub(crate) top_level_mark: Mark,
305    pub(crate) imports: ImportMap,
306    pub(crate) force_free_values: Arc<FxHashSet<Id>>,
307}
308
309impl EvalContext {
310    /// Produce a new [EvalContext] from a [Program]. If you wish to support
311    /// webpackIgnore or turbopackIgnore comments, you must pass those in,
312    /// since the AST does not include comments by default.
313    pub fn new(
314        module: &Program,
315        unresolved_mark: Mark,
316        top_level_mark: Mark,
317        force_free_values: Arc<FxHashSet<Id>>,
318        comments: Option<&dyn Comments>,
319        source: Option<ResolvedVc<Box<dyn Source>>>,
320    ) -> Self {
321        Self {
322            unresolved_mark,
323            top_level_mark,
324            imports: ImportMap::analyze(module, source, comments),
325            force_free_values,
326        }
327    }
328
329    pub fn is_esm(&self, specified_type: SpecifiedModuleType) -> bool {
330        self.imports.is_esm(specified_type)
331    }
332
333    fn eval_prop_name(&self, prop: &PropName) -> JsValue {
334        match prop {
335            PropName::Ident(ident) => ident.sym.clone().into(),
336            PropName::Str(str) => str.value.clone().into(),
337            PropName::Num(num) => num.value.into(),
338            PropName::Computed(ComputedPropName { expr, .. }) => self.eval(expr),
339            PropName::BigInt(bigint) => (*bigint.value.clone()).into(),
340        }
341    }
342
343    fn eval_member_prop(&self, prop: &MemberProp) -> Option<JsValue> {
344        match prop {
345            MemberProp::Ident(ident) => Some(ident.sym.clone().into()),
346            MemberProp::Computed(ComputedPropName { expr, .. }) => Some(self.eval(expr)),
347            MemberProp::PrivateName(_) => None,
348        }
349    }
350
351    fn eval_tpl(&self, e: &Tpl, raw: bool) -> JsValue {
352        debug_assert!(e.quasis.len() == e.exprs.len() + 1);
353
354        let mut values = vec![];
355
356        for idx in 0..(e.quasis.len() + e.exprs.len()) {
357            if idx % 2 == 0 {
358                let idx = idx / 2;
359                let e = &e.quasis[idx];
360
361                if raw {
362                    values.push(JsValue::from(e.raw.clone()));
363                } else {
364                    match &e.cooked {
365                        Some(v) => {
366                            values.push(JsValue::from(v.clone()));
367                        }
368                        // This is actually unreachable
369                        None => return JsValue::unknown_empty(true, ""),
370                    }
371                }
372            } else {
373                let idx = idx / 2;
374                let e = &e.exprs[idx];
375
376                values.push(self.eval(e));
377            }
378        }
379
380        if values.len() == 1 {
381            return values.into_iter().next().unwrap();
382        }
383
384        JsValue::concat(values)
385    }
386
387    fn eval_ident(&self, i: &Ident) -> JsValue {
388        let id = i.to_id();
389        if let Some(imported) = self.imports.get_import(&id) {
390            return imported;
391        }
392        if is_unresolved(i, self.unresolved_mark) || self.force_free_values.contains(&id) {
393            JsValue::FreeVar(i.sym.clone())
394        } else {
395            JsValue::Variable(id)
396        }
397    }
398
399    pub fn eval(&self, e: &Expr) -> JsValue {
400        debug_assert!(
401            GLOBALS.is_set(),
402            "Eval requires globals from its parsed result"
403        );
404        match e {
405            Expr::Paren(e) => self.eval(&e.expr),
406            Expr::Lit(e) => JsValue::Constant(e.clone().into()),
407            Expr::Ident(i) => self.eval_ident(i),
408
409            Expr::Unary(UnaryExpr {
410                op: op!("!"), arg, ..
411            }) => {
412                let arg = self.eval(arg);
413
414                JsValue::logical_not(Box::new(arg))
415            }
416
417            Expr::Unary(UnaryExpr {
418                op: op!("typeof"),
419                arg,
420                ..
421            }) => {
422                let arg = self.eval(arg);
423
424                JsValue::type_of(Box::new(arg))
425            }
426
427            Expr::Bin(BinExpr {
428                op: op!(bin, "+"),
429                left,
430                right,
431                ..
432            }) => {
433                let l = self.eval(left);
434                let r = self.eval(right);
435
436                match (l, r) {
437                    (JsValue::Add(c, l), r) => JsValue::Add(
438                        c + r.total_nodes(),
439                        l.into_iter().chain(iter::once(r)).collect(),
440                    ),
441                    (l, r) => JsValue::add(vec![l, r]),
442                }
443            }
444
445            Expr::Bin(BinExpr {
446                op: op!("&&"),
447                left,
448                right,
449                ..
450            }) => JsValue::logical_and(vec![self.eval(left), self.eval(right)]),
451
452            Expr::Bin(BinExpr {
453                op: op!("||"),
454                left,
455                right,
456                ..
457            }) => JsValue::logical_or(vec![self.eval(left), self.eval(right)]),
458
459            Expr::Bin(BinExpr {
460                op: op!("??"),
461                left,
462                right,
463                ..
464            }) => JsValue::nullish_coalescing(vec![self.eval(left), self.eval(right)]),
465
466            Expr::Bin(BinExpr {
467                op: op!("=="),
468                left,
469                right,
470                ..
471            }) => JsValue::equal(Box::new(self.eval(left)), Box::new(self.eval(right))),
472
473            Expr::Bin(BinExpr {
474                op: op!("!="),
475                left,
476                right,
477                ..
478            }) => JsValue::not_equal(Box::new(self.eval(left)), Box::new(self.eval(right))),
479
480            Expr::Bin(BinExpr {
481                op: op!("==="),
482                left,
483                right,
484                ..
485            }) => JsValue::strict_equal(Box::new(self.eval(left)), Box::new(self.eval(right))),
486
487            Expr::Bin(BinExpr {
488                op: op!("!=="),
489                left,
490                right,
491                ..
492            }) => JsValue::strict_not_equal(Box::new(self.eval(left)), Box::new(self.eval(right))),
493
494            &Expr::Cond(CondExpr {
495                box ref cons,
496                box ref alt,
497                box ref test,
498                ..
499            }) => {
500                let test = self.eval(test);
501                if let Some(truthy) = test.is_truthy() {
502                    if truthy {
503                        self.eval(cons)
504                    } else {
505                        self.eval(alt)
506                    }
507                } else {
508                    JsValue::tenary(
509                        Box::new(test),
510                        Box::new(self.eval(cons)),
511                        Box::new(self.eval(alt)),
512                    )
513                }
514            }
515
516            Expr::Tpl(e) => self.eval_tpl(e, false),
517
518            Expr::TaggedTpl(TaggedTpl {
519                tag:
520                    box Expr::Member(MemberExpr {
521                        obj: box Expr::Ident(tag_obj),
522                        prop: MemberProp::Ident(tag_prop),
523                        ..
524                    }),
525                tpl,
526                ..
527            }) => {
528                if &*tag_obj.sym == "String"
529                    && &*tag_prop.sym == "raw"
530                    && is_unresolved(tag_obj, self.unresolved_mark)
531                {
532                    self.eval_tpl(tpl, true)
533                } else {
534                    JsValue::unknown_empty(true, "tagged template literal is not supported yet")
535                }
536            }
537
538            Expr::Fn(expr) => {
539                if let Some(ident) = &expr.ident {
540                    JsValue::Variable(ident.to_id())
541                } else {
542                    JsValue::Variable((
543                        format!("*anonymous function {}*", expr.function.span.lo.0).into(),
544                        SyntaxContext::empty(),
545                    ))
546                }
547            }
548            Expr::Arrow(expr) => JsValue::Variable((
549                format!("*arrow function {}*", expr.span.lo.0).into(),
550                SyntaxContext::empty(),
551            )),
552
553            Expr::Await(AwaitExpr { arg, .. }) => JsValue::awaited(Box::new(self.eval(arg))),
554
555            Expr::Seq(e) => {
556                let mut seq = e.exprs.iter().map(|e| self.eval(e)).peekable();
557                let mut side_effects = false;
558                let mut last = seq.next().unwrap();
559                for e in seq {
560                    side_effects |= last.has_side_effects();
561                    last = e;
562                }
563                if side_effects {
564                    last.make_unknown(true, "sequence with side effects");
565                }
566                last
567            }
568
569            Expr::Member(MemberExpr {
570                obj,
571                prop: MemberProp::Ident(prop),
572                ..
573            }) => {
574                let obj = self.eval(obj);
575                JsValue::member(Box::new(obj), Box::new(prop.sym.clone().into()))
576            }
577
578            Expr::Member(MemberExpr {
579                obj,
580                prop: MemberProp::Computed(computed),
581                ..
582            }) => {
583                let obj = self.eval(obj);
584                let prop = self.eval(&computed.expr);
585                JsValue::member(Box::new(obj), Box::new(prop))
586            }
587
588            Expr::New(NewExpr {
589                callee: box callee,
590                args,
591                ..
592            }) => {
593                // We currently do not handle spreads.
594                if args.iter().flatten().any(|arg| arg.spread.is_some()) {
595                    return JsValue::unknown_empty(true, "spread in new calls is not supported");
596                }
597
598                let args: Vec<_> = args
599                    .iter()
600                    .flatten()
601                    .map(|arg| self.eval(&arg.expr))
602                    .collect();
603                let callee = Box::new(self.eval(callee));
604
605                JsValue::new(callee, args)
606            }
607
608            Expr::Call(CallExpr {
609                callee: Callee::Expr(box callee),
610                args,
611                ..
612            }) => {
613                // We currently do not handle spreads.
614                if args.iter().any(|arg| arg.spread.is_some()) {
615                    return JsValue::unknown_empty(
616                        true,
617                        "spread in function calls is not supported",
618                    );
619                }
620
621                let args = args.iter().map(|arg| self.eval(&arg.expr)).collect();
622                if let Expr::Member(MemberExpr { obj, prop, .. }) = unparen(callee) {
623                    let obj = Box::new(self.eval(obj));
624                    let prop = Box::new(match prop {
625                        // TODO avoid clone
626                        MemberProp::Ident(i) => i.sym.clone().into(),
627                        MemberProp::PrivateName(_) => {
628                            return JsValue::unknown_empty(
629                                false,
630                                "private names in function calls is not supported",
631                            );
632                        }
633                        MemberProp::Computed(ComputedPropName { expr, .. }) => self.eval(expr),
634                    });
635                    JsValue::member_call(obj, prop, args)
636                } else {
637                    let callee = Box::new(self.eval(callee));
638
639                    JsValue::call(callee, args)
640                }
641            }
642
643            Expr::Call(CallExpr {
644                callee: Callee::Super(_),
645                args,
646                ..
647            }) => {
648                // We currently do not handle spreads.
649                if args.iter().any(|arg| arg.spread.is_some()) {
650                    return JsValue::unknown_empty(
651                        true,
652                        "spread in function calls is not supported",
653                    );
654                }
655
656                let args = args.iter().map(|arg| self.eval(&arg.expr)).collect();
657
658                JsValue::super_call(args)
659            }
660
661            Expr::Call(CallExpr {
662                callee: Callee::Import(_),
663                args,
664                ..
665            }) => {
666                // We currently do not handle spreads.
667                if args.iter().any(|arg| arg.spread.is_some()) {
668                    return JsValue::unknown_empty(true, "spread in import() is not supported");
669                }
670                let args = args.iter().map(|arg| self.eval(&arg.expr)).collect();
671
672                let callee = Box::new(JsValue::FreeVar(atom!("import")));
673
674                JsValue::call(callee, args)
675            }
676
677            Expr::Array(arr) => {
678                if arr.elems.iter().flatten().any(|v| v.spread.is_some()) {
679                    return JsValue::unknown_empty(true, "spread is not supported");
680                }
681
682                let arr = arr
683                    .elems
684                    .iter()
685                    .map(|e| match e {
686                        Some(e) => self.eval(&e.expr),
687                        _ => JsValue::FreeVar(atom!("undefined")),
688                    })
689                    .collect();
690                JsValue::array(arr)
691            }
692
693            Expr::Object(obj) => JsValue::object(
694                obj.props
695                    .iter()
696                    .map(|prop| match prop {
697                        PropOrSpread::Spread(SpreadElement { expr, .. }) => {
698                            ObjectPart::Spread(self.eval(expr))
699                        }
700                        PropOrSpread::Prop(box Prop::KeyValue(KeyValueProp { key, box value })) => {
701                            ObjectPart::KeyValue(self.eval_prop_name(key), self.eval(value))
702                        }
703                        PropOrSpread::Prop(box Prop::Shorthand(ident)) => ObjectPart::KeyValue(
704                            ident.sym.clone().into(),
705                            self.eval(&Expr::Ident(ident.clone())),
706                        ),
707                        _ => ObjectPart::Spread(JsValue::unknown_empty(
708                            true,
709                            "unsupported object part",
710                        )),
711                    })
712                    .collect(),
713            ),
714
715            Expr::MetaProp(MetaPropExpr {
716                kind: MetaPropKind::ImportMeta,
717                ..
718            }) => JsValue::WellKnownObject(WellKnownObjectKind::ImportMeta),
719
720            _ => JsValue::unknown_empty(true, "unsupported expression"),
721        }
722    }
723}
724
725enum EarlyReturn {
726    Always {
727        prev_effects: Vec<Effect>,
728        start_ast_path: Vec<AstParentKind>,
729    },
730    Conditional {
731        prev_effects: Vec<Effect>,
732        start_ast_path: Vec<AstParentKind>,
733
734        condition: Box<JsValue>,
735        then: Option<Box<EffectsBlock>>,
736        r#else: Option<Box<EffectsBlock>>,
737        /// The ast path to the condition.
738        condition_ast_path: Vec<AstParentKind>,
739        span: Span,
740        in_try: bool,
741
742        early_return_condition_value: bool,
743    },
744}
745
746pub fn as_parent_path_skip(
747    ast_path: &AstNodePath<AstParentNodeRef<'_>>,
748    skip: usize,
749) -> Vec<AstParentKind> {
750    ast_path
751        .iter()
752        .take(ast_path.len() - skip)
753        .map(|n| n.kind())
754        .collect()
755}
756
757struct Analyzer<'a> {
758    data: &'a mut VarGraph,
759
760    effects: Vec<Effect>,
761    hoisted_effects: Vec<Effect>,
762    early_return_stack: Vec<EarlyReturn>,
763
764    eval_context: &'a EvalContext,
765
766    var_decl_kind: Option<VarDeclKind>,
767
768    /// Used for patterns
769    current_value: Option<JsValue>,
770
771    /// Return values of the current function.
772    ///
773    /// This is configured to [Some] by function handlers and filled by the
774    /// return statement handler.
775    cur_fn_return_values: Option<Vec<JsValue>>,
776
777    cur_fn_ident: u32,
778}
779
780pub fn as_parent_path(ast_path: &AstNodePath<AstParentNodeRef<'_>>) -> Vec<AstParentKind> {
781    ast_path.iter().map(|n| n.kind()).collect()
782}
783
784pub fn as_parent_path_with(
785    ast_path: &AstNodePath<AstParentNodeRef<'_>>,
786    additional: AstParentKind,
787) -> Vec<AstParentKind> {
788    ast_path
789        .iter()
790        .map(|n| n.kind())
791        .chain([additional])
792        .collect()
793}
794
795pub fn is_in_try(ast_path: &AstNodePath<AstParentNodeRef<'_>>) -> bool {
796    ast_path
797        .iter()
798        .rev()
799        .find_map(|ast_ref| match ast_ref.kind() {
800            AstParentKind::ArrowExpr(ArrowExprField::Body) => Some(false),
801            AstParentKind::Function(FunctionField::Body) => Some(false),
802            AstParentKind::TryStmt(TryStmtField::Block) => Some(true),
803            _ => None,
804        })
805        .unwrap_or(false)
806}
807
808enum CallOrNewExpr<'ast> {
809    Call(&'ast CallExpr),
810    New(&'ast NewExpr),
811}
812impl CallOrNewExpr<'_> {
813    fn as_call(&self) -> Option<&CallExpr> {
814        match *self {
815            CallOrNewExpr::Call(n) => Some(n),
816            CallOrNewExpr::New(_) => None,
817        }
818    }
819    fn as_new(&self) -> Option<&NewExpr> {
820        match *self {
821            CallOrNewExpr::Call(_) => None,
822            CallOrNewExpr::New(n) => Some(n),
823        }
824    }
825}
826
827impl Analyzer<'_> {
828    fn add_value(&mut self, id: Id, value: JsValue) {
829        if is_unresolved_id(&id, self.eval_context.unresolved_mark) {
830            self.data.free_var_ids.insert(id.0.clone(), id.clone());
831        }
832
833        if let Some(prev) = self.data.values.get_mut(&id) {
834            prev.add_alt(value);
835        } else {
836            self.data.values.insert(id, value);
837        }
838        // TODO(kdy1): We may need to report an error for this.
839        // Variables declared with `var` are hoisted, but using undefined as its
840        // value does not seem like a good idea.
841    }
842
843    fn add_value_from_expr(&mut self, id: Id, value: &Expr) {
844        let value = self.eval_context.eval(value);
845
846        self.add_value(id, value);
847    }
848
849    fn add_effect(&mut self, effect: Effect) {
850        self.effects.push(effect);
851    }
852
853    fn check_iife<'ast: 'r, 'r>(
854        &mut self,
855        n: &'ast CallExpr,
856        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
857    ) -> bool {
858        fn unparen<'ast: 'r, 'r, T>(
859            expr: &'ast Expr,
860            ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
861            f: impl FnOnce(&'ast Expr, &mut AstNodePath<AstParentNodeRef<'r>>) -> T,
862        ) -> T {
863            if let Some(inner_expr) = expr.as_paren() {
864                let mut ast_path =
865                    ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Paren));
866                let mut ast_path = ast_path.with_guard(AstParentNodeRef::ParenExpr(
867                    inner_expr,
868                    ParenExprField::Expr,
869                ));
870                unparen(&inner_expr.expr, &mut ast_path, f)
871            } else {
872                f(expr, ast_path)
873            }
874        }
875
876        if n.args.iter().any(|arg| arg.spread.is_some()) {
877            return false;
878        }
879
880        let Some(expr) = n.callee.as_expr() else {
881            return false;
882        };
883
884        let fn_expr = {
885            let mut ast_path =
886                ast_path.with_guard(AstParentNodeRef::CallExpr(n, CallExprField::Callee));
887            let mut ast_path =
888                ast_path.with_guard(AstParentNodeRef::Callee(&n.callee, CalleeField::Expr));
889            unparen(expr, &mut ast_path, |expr, ast_path| match expr {
890                Expr::Fn(fn_expr @ FnExpr { function, ident }) => {
891                    let mut ast_path =
892                        ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Fn));
893                    {
894                        let mut ast_path = ast_path
895                            .with_guard(AstParentNodeRef::FnExpr(fn_expr, FnExprField::Ident));
896                        self.visit_opt_ident(ident, &mut ast_path);
897
898                        // We cannot analyze recursive IIFE
899                        if let Some(ident) = ident {
900                            if contains_ident_ref(&function.body, &ident.to_id()) {
901                                return false;
902                            }
903                        }
904                    }
905
906                    {
907                        let mut ast_path = ast_path
908                            .with_guard(AstParentNodeRef::FnExpr(fn_expr, FnExprField::Function));
909                        self.handle_iife_function(function, &mut ast_path, &n.args);
910                    }
911
912                    true
913                }
914
915                Expr::Arrow(arrow_expr) => {
916                    let mut ast_path =
917                        ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Arrow));
918                    let args = &n.args;
919                    self.handle_iife_arrow(arrow_expr, args, &mut ast_path);
920                    true
921                }
922                _ => false,
923            })
924        };
925
926        if !fn_expr {
927            return false;
928        }
929
930        let mut ast_path = ast_path.with_guard(AstParentNodeRef::CallExpr(
931            n,
932            CallExprField::Args(usize::MAX),
933        ));
934
935        self.visit_expr_or_spreads(&n.args, &mut ast_path);
936
937        true
938    }
939
940    fn handle_iife_arrow<'ast: 'r, 'r>(
941        &mut self,
942        arrow_expr: &'ast ArrowExpr,
943        args: &[ExprOrSpread],
944        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
945    ) {
946        let ArrowExpr {
947            params,
948            body,
949            is_async: _,
950            is_generator: _,
951            return_type,
952            span: _,
953            type_params,
954            ctxt: _,
955        } = arrow_expr;
956        let mut iter = args.iter();
957        for (i, param) in params.iter().enumerate() {
958            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
959                arrow_expr,
960                ArrowExprField::Params(i),
961            ));
962            if let Some(arg) = iter.next() {
963                self.current_value = Some(self.eval_context.eval(&arg.expr));
964                self.visit_pat(param, &mut ast_path);
965                self.current_value = None;
966            } else {
967                self.visit_pat(param, &mut ast_path);
968            }
969        }
970        {
971            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
972                arrow_expr,
973                ArrowExprField::Body,
974            ));
975            self.visit_block_stmt_or_expr(body, &mut ast_path);
976        }
977
978        {
979            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
980                arrow_expr,
981                ArrowExprField::ReturnType,
982            ));
983            self.visit_opt_ts_type_ann(return_type, &mut ast_path);
984        }
985
986        {
987            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
988                arrow_expr,
989                ArrowExprField::TypeParams,
990            ));
991            self.visit_opt_ts_type_param_decl(type_params, &mut ast_path);
992        }
993    }
994
995    fn handle_iife_function<'ast: 'r, 'r>(
996        &mut self,
997        function: &'ast Function,
998        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
999        args: &[ExprOrSpread],
1000    ) {
1001        let mut iter = args.iter();
1002        let Function {
1003            body,
1004            decorators,
1005            is_async: _,
1006            is_generator: _,
1007            params,
1008            return_type,
1009            span: _,
1010            type_params,
1011            ctxt: _,
1012        } = function;
1013        for (i, param) in params.iter().enumerate() {
1014            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1015                function,
1016                FunctionField::Params(i),
1017            ));
1018            if let Some(arg) = iter.next() {
1019                self.current_value = Some(self.eval_context.eval(&arg.expr));
1020                self.visit_param(param, &mut ast_path);
1021                self.current_value = None;
1022            } else {
1023                self.visit_param(param, &mut ast_path);
1024            }
1025        }
1026
1027        {
1028            let mut ast_path =
1029                ast_path.with_guard(AstParentNodeRef::Function(function, FunctionField::Body));
1030
1031            self.visit_opt_block_stmt(body, &mut ast_path);
1032        }
1033
1034        {
1035            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1036                function,
1037                FunctionField::Decorators(usize::MAX),
1038            ));
1039
1040            self.visit_decorators(decorators, &mut ast_path);
1041        }
1042
1043        {
1044            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1045                function,
1046                FunctionField::ReturnType,
1047            ));
1048
1049            self.visit_opt_ts_type_ann(return_type, &mut ast_path);
1050        }
1051
1052        {
1053            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1054                function,
1055                FunctionField::TypeParams,
1056            ));
1057
1058            self.visit_opt_ts_type_param_decl(type_params, &mut ast_path);
1059        }
1060    }
1061
1062    fn check_call_expr_for_effects<'ast: 'r, 'n, 'r>(
1063        &mut self,
1064        callee: &'n Callee,
1065        args: impl Iterator<Item = &'ast ExprOrSpread>,
1066        span: Span,
1067        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1068        n: CallOrNewExpr<'ast>,
1069    ) {
1070        let new = n.as_new().is_some();
1071        let args = args
1072            .enumerate()
1073            .map(|(i, arg)| {
1074                let mut ast_path = ast_path.with_guard(match n {
1075                    CallOrNewExpr::Call(n) => AstParentNodeRef::CallExpr(n, CallExprField::Args(i)),
1076                    CallOrNewExpr::New(n) => AstParentNodeRef::NewExpr(n, NewExprField::Args(i)),
1077                });
1078                if arg.spread.is_none() {
1079                    let value = self.eval_context.eval(&arg.expr);
1080
1081                    let block_path = match &*arg.expr {
1082                        Expr::Fn(FnExpr { .. }) => {
1083                            let mut path = as_parent_path(&ast_path);
1084                            path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1085                            path.push(AstParentKind::Expr(ExprField::Fn));
1086                            path.push(AstParentKind::FnExpr(FnExprField::Function));
1087                            path.push(AstParentKind::Function(FunctionField::Body));
1088                            Some(path)
1089                        }
1090                        Expr::Arrow(ArrowExpr {
1091                            body: box BlockStmtOrExpr::BlockStmt(_),
1092                            ..
1093                        }) => {
1094                            let mut path = as_parent_path(&ast_path);
1095                            path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1096                            path.push(AstParentKind::Expr(ExprField::Arrow));
1097                            path.push(AstParentKind::ArrowExpr(ArrowExprField::Body));
1098                            path.push(AstParentKind::BlockStmtOrExpr(
1099                                BlockStmtOrExprField::BlockStmt,
1100                            ));
1101                            Some(path)
1102                        }
1103                        Expr::Arrow(ArrowExpr {
1104                            body: box BlockStmtOrExpr::Expr(_),
1105                            ..
1106                        }) => {
1107                            let mut path = as_parent_path(&ast_path);
1108                            path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1109                            path.push(AstParentKind::Expr(ExprField::Arrow));
1110                            path.push(AstParentKind::ArrowExpr(ArrowExprField::Body));
1111                            path.push(AstParentKind::BlockStmtOrExpr(BlockStmtOrExprField::Expr));
1112                            Some(path)
1113                        }
1114                        _ => None,
1115                    };
1116                    if let Some(path) = block_path {
1117                        let old_effects = take(&mut self.effects);
1118                        arg.visit_with_ast_path(self, &mut ast_path);
1119                        let effects = replace(&mut self.effects, old_effects);
1120                        EffectArg::Closure(
1121                            value,
1122                            Box::new(EffectsBlock {
1123                                effects,
1124                                range: AstPathRange::Exact(path),
1125                            }),
1126                        )
1127                    } else {
1128                        arg.visit_with_ast_path(self, &mut ast_path);
1129                        EffectArg::Value(value)
1130                    }
1131                } else {
1132                    arg.visit_with_ast_path(self, &mut ast_path);
1133                    EffectArg::Spread
1134                }
1135            })
1136            .collect();
1137
1138        match callee {
1139            Callee::Import(_) => {
1140                self.add_effect(Effect::Call {
1141                    func: Box::new(JsValue::FreeVar(atom!("import"))),
1142                    args,
1143                    ast_path: as_parent_path(ast_path),
1144                    span,
1145                    in_try: is_in_try(ast_path),
1146                    new,
1147                });
1148            }
1149            Callee::Expr(box expr) => {
1150                if let Expr::Member(MemberExpr { obj, prop, .. }) = unparen(expr) {
1151                    let obj_value = Box::new(self.eval_context.eval(obj));
1152                    let prop_value = match prop {
1153                        // TODO avoid clone
1154                        MemberProp::Ident(i) => Box::new(i.sym.clone().into()),
1155                        MemberProp::PrivateName(_) => Box::new(JsValue::unknown_empty(
1156                            false,
1157                            "private names in member expressions are not supported",
1158                        )),
1159                        MemberProp::Computed(ComputedPropName { expr, .. }) => {
1160                            Box::new(self.eval_context.eval(expr))
1161                        }
1162                    };
1163                    self.add_effect(Effect::MemberCall {
1164                        obj: obj_value,
1165                        prop: prop_value,
1166                        args,
1167                        ast_path: as_parent_path(ast_path),
1168                        span,
1169                        in_try: is_in_try(ast_path),
1170                        new,
1171                    });
1172                } else {
1173                    let fn_value = Box::new(self.eval_context.eval(expr));
1174                    self.add_effect(Effect::Call {
1175                        func: fn_value,
1176                        args,
1177                        ast_path: as_parent_path(ast_path),
1178                        span,
1179                        in_try: is_in_try(ast_path),
1180                        new,
1181                    });
1182                }
1183            }
1184            Callee::Super(_) => self.add_effect(Effect::Call {
1185                func: Box::new(
1186                    self.eval_context
1187                        // Unwrap because `new super(..)` isn't valid anyway
1188                        .eval(&Expr::Call(n.as_call().unwrap().clone())),
1189                ),
1190                args,
1191                ast_path: as_parent_path(ast_path),
1192                span,
1193                in_try: is_in_try(ast_path),
1194                new,
1195            }),
1196        }
1197    }
1198
1199    fn check_member_expr_for_effects<'ast: 'r, 'r>(
1200        &mut self,
1201        member_expr: &'ast MemberExpr,
1202        ast_path: &AstNodePath<AstParentNodeRef<'r>>,
1203    ) {
1204        let obj_value = Box::new(self.eval_context.eval(&member_expr.obj));
1205        let prop_value = match &member_expr.prop {
1206            // TODO avoid clone
1207            MemberProp::Ident(i) => Box::new(i.sym.clone().into()),
1208            MemberProp::PrivateName(_) => {
1209                return;
1210            }
1211            MemberProp::Computed(ComputedPropName { expr, .. }) => {
1212                Box::new(self.eval_context.eval(expr))
1213            }
1214        };
1215        self.add_effect(Effect::Member {
1216            obj: obj_value,
1217            prop: prop_value,
1218            ast_path: as_parent_path(ast_path),
1219            span: member_expr.span(),
1220            in_try: is_in_try(ast_path),
1221        });
1222    }
1223
1224    fn take_return_values(&mut self) -> Box<JsValue> {
1225        let values = self.cur_fn_return_values.take().unwrap();
1226
1227        Box::new(match values.len() {
1228            0 => JsValue::FreeVar(atom!("undefined")),
1229            1 => values.into_iter().next().unwrap(),
1230            _ => JsValue::alternatives(values),
1231        })
1232    }
1233
1234    /// Ends a conditional block. All early returns are integrated into the
1235    /// effects. Returns true if the whole block always early returns.
1236    fn end_early_return_block(&mut self) -> bool {
1237        let mut always_returns = false;
1238        while let Some(early_return) = self.early_return_stack.pop() {
1239            match early_return {
1240                EarlyReturn::Always {
1241                    prev_effects,
1242                    start_ast_path,
1243                } => {
1244                    self.effects = prev_effects;
1245                    self.effects.push(Effect::Unreachable { start_ast_path });
1246                    always_returns = true;
1247                }
1248                EarlyReturn::Conditional {
1249                    prev_effects,
1250                    start_ast_path,
1251                    condition,
1252                    then,
1253                    r#else,
1254                    condition_ast_path,
1255                    span,
1256                    in_try,
1257                    early_return_condition_value,
1258                } => {
1259                    let block = Box::new(EffectsBlock {
1260                        effects: take(&mut self.effects),
1261                        range: AstPathRange::StartAfter(start_ast_path),
1262                    });
1263                    self.effects = prev_effects;
1264                    let kind = match (then, r#else, early_return_condition_value) {
1265                        (None, None, false) => ConditionalKind::If { then: block },
1266                        (None, None, true) => ConditionalKind::IfElseMultiple {
1267                            then: vec![block],
1268                            r#else: vec![],
1269                        },
1270                        (Some(then), None, false) => ConditionalKind::IfElseMultiple {
1271                            then: vec![then, block],
1272                            r#else: vec![],
1273                        },
1274                        (Some(then), None, true) => ConditionalKind::IfElse {
1275                            then,
1276                            r#else: block,
1277                        },
1278                        (Some(then), Some(r#else), false) => ConditionalKind::IfElseMultiple {
1279                            then: vec![then, block],
1280                            r#else: vec![r#else],
1281                        },
1282                        (Some(then), Some(r#else), true) => ConditionalKind::IfElseMultiple {
1283                            then: vec![then],
1284                            r#else: vec![r#else, block],
1285                        },
1286                        (None, Some(r#else), false) => ConditionalKind::IfElse {
1287                            then: block,
1288                            r#else,
1289                        },
1290                        (None, Some(r#else), true) => ConditionalKind::IfElseMultiple {
1291                            then: vec![],
1292                            r#else: vec![r#else, block],
1293                        },
1294                    };
1295                    self.effects.push(Effect::Conditional {
1296                        condition,
1297                        kind: Box::new(kind),
1298                        ast_path: condition_ast_path,
1299                        span,
1300                        in_try,
1301                    })
1302                }
1303            }
1304        }
1305        always_returns
1306    }
1307}
1308
1309impl VisitAstPath for Analyzer<'_> {
1310    fn visit_assign_expr<'ast: 'r, 'r>(
1311        &mut self,
1312        n: &'ast AssignExpr,
1313        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1314    ) {
1315        {
1316            let mut ast_path =
1317                ast_path.with_guard(AstParentNodeRef::AssignExpr(n, AssignExprField::Left));
1318
1319            match n.op {
1320                AssignOp::Assign => {
1321                    self.current_value = Some(self.eval_context.eval(&n.right));
1322                    n.left.visit_children_with_ast_path(self, &mut ast_path);
1323                    self.current_value = None;
1324                }
1325
1326                _ => {
1327                    if let Some(key) = n.left.as_ident() {
1328                        let value = match n.op {
1329                            AssignOp::AndAssign | AssignOp::OrAssign | AssignOp::NullishAssign => {
1330                                let right = self.eval_context.eval(&n.right);
1331                                // We can handle the right value as alternative to the existing
1332                                // value
1333                                Some(right)
1334                            }
1335                            AssignOp::AddAssign => {
1336                                let left = self.eval_context.eval(&Expr::Ident(key.clone().into()));
1337
1338                                let right = self.eval_context.eval(&n.right);
1339
1340                                Some(JsValue::add(vec![left, right]))
1341                            }
1342                            _ => Some(JsValue::unknown_empty(true, "unsupported assign operation")),
1343                        };
1344                        if let Some(value) = value {
1345                            // We should visit this to handle `+=` like
1346                            //
1347                            // clientComponentLoadTimes += performance.now() - startTime
1348
1349                            self.current_value = Some(value);
1350                            n.left.visit_children_with_ast_path(self, &mut ast_path);
1351                            self.current_value = None;
1352                        }
1353                    }
1354
1355                    if n.left.as_ident().is_none() {
1356                        n.left.visit_children_with_ast_path(self, &mut ast_path);
1357                    }
1358                }
1359            }
1360        }
1361
1362        {
1363            let mut ast_path =
1364                ast_path.with_guard(AstParentNodeRef::AssignExpr(n, AssignExprField::Right));
1365            self.visit_expr(&n.right, &mut ast_path);
1366        }
1367    }
1368
1369    fn visit_update_expr<'ast: 'r, 'r>(
1370        &mut self,
1371        n: &'ast UpdateExpr,
1372        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1373    ) {
1374        if let Some(key) = n.arg.as_ident() {
1375            self.add_value(
1376                key.to_id(),
1377                JsValue::unknown_empty(true, "updated with update expression"),
1378            );
1379        }
1380
1381        let mut ast_path =
1382            ast_path.with_guard(AstParentNodeRef::UpdateExpr(n, UpdateExprField::Arg));
1383        self.visit_expr(&n.arg, &mut ast_path);
1384    }
1385
1386    fn visit_call_expr<'ast: 'r, 'r>(
1387        &mut self,
1388        n: &'ast CallExpr,
1389        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1390    ) {
1391        // We handle `define(function (require) {})` here.
1392        if let Callee::Expr(callee) = &n.callee {
1393            if n.args.len() == 1 {
1394                if let Some(require_var_id) = extract_var_from_umd_factory(callee, &n.args) {
1395                    self.add_value(
1396                        require_var_id,
1397                        JsValue::unknown_if(
1398                            self.eval_context
1399                                .imports
1400                                .get_attributes(n.callee.span())
1401                                .ignore,
1402                            JsValue::WellKnownFunction(WellKnownFunctionKind::Require),
1403                            true,
1404                            "ignored require",
1405                        ),
1406                    );
1407                }
1408            }
1409        }
1410
1411        if self.check_iife(n, ast_path) {
1412            return;
1413        }
1414
1415        // special behavior of IIFEs
1416        {
1417            let mut ast_path =
1418                ast_path.with_guard(AstParentNodeRef::CallExpr(n, CallExprField::Callee));
1419            n.callee.visit_with_ast_path(self, &mut ast_path);
1420        }
1421
1422        self.check_call_expr_for_effects(
1423            &n.callee,
1424            n.args.iter(),
1425            n.span(),
1426            ast_path,
1427            CallOrNewExpr::Call(n),
1428        );
1429    }
1430
1431    fn visit_new_expr<'ast: 'r, 'r>(
1432        &mut self,
1433        n: &'ast NewExpr,
1434        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1435    ) {
1436        {
1437            let mut ast_path =
1438                ast_path.with_guard(AstParentNodeRef::NewExpr(n, NewExprField::Callee));
1439            n.callee.visit_with_ast_path(self, &mut ast_path);
1440        }
1441
1442        self.check_call_expr_for_effects(
1443            &Callee::Expr(n.callee.clone()),
1444            n.args.iter().flatten(),
1445            n.span(),
1446            ast_path,
1447            CallOrNewExpr::New(n),
1448        );
1449    }
1450
1451    fn visit_member_expr<'ast: 'r, 'r>(
1452        &mut self,
1453        member_expr: &'ast MemberExpr,
1454        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1455    ) {
1456        self.check_member_expr_for_effects(member_expr, ast_path);
1457        member_expr.visit_children_with_ast_path(self, ast_path);
1458    }
1459
1460    fn visit_expr<'ast: 'r, 'r>(
1461        &mut self,
1462        n: &'ast Expr,
1463        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1464    ) {
1465        let old = self.var_decl_kind;
1466        self.var_decl_kind = None;
1467        n.visit_children_with_ast_path(self, ast_path);
1468        self.var_decl_kind = old;
1469    }
1470
1471    fn visit_params<'ast: 'r, 'r>(
1472        &mut self,
1473        n: &'ast [Param],
1474        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1475    ) {
1476        let value = self.current_value.take();
1477        for (index, p) in n.iter().enumerate() {
1478            self.current_value = Some(JsValue::Argument(self.cur_fn_ident, index));
1479            let mut ast_path = ast_path.with_index_guard(index);
1480            p.visit_with_ast_path(self, &mut ast_path);
1481        }
1482        self.current_value = value;
1483    }
1484
1485    fn visit_param<'ast: 'r, 'r>(
1486        &mut self,
1487        n: &'ast Param,
1488        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1489    ) {
1490        let old = self.var_decl_kind;
1491        let Param {
1492            decorators,
1493            pat,
1494            span: _,
1495        } = n;
1496        self.var_decl_kind = None;
1497        let value = self.current_value.take();
1498        {
1499            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param(
1500                n,
1501                ParamField::Decorators(usize::MAX),
1502            ));
1503            self.visit_decorators(decorators, &mut ast_path);
1504        }
1505        self.current_value = value;
1506        {
1507            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param(n, ParamField::Pat));
1508            self.visit_pat(pat, &mut ast_path);
1509        }
1510        self.current_value = None;
1511        self.var_decl_kind = old;
1512    }
1513
1514    fn visit_fn_decl<'ast: 'r, 'r>(
1515        &mut self,
1516        decl: &'ast FnDecl,
1517        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1518    ) {
1519        let old = self
1520            .cur_fn_return_values
1521            .replace(get_fn_init_return_vals(decl.function.body.as_ref()));
1522        let old_ident = self.cur_fn_ident;
1523        self.cur_fn_ident = decl.function.span.lo.0;
1524        decl.visit_children_with_ast_path(self, ast_path);
1525        let return_value = self.take_return_values();
1526        self.hoisted_effects.append(&mut self.effects);
1527
1528        self.add_value(
1529            decl.ident.to_id(),
1530            JsValue::function(self.cur_fn_ident, return_value),
1531        );
1532
1533        self.cur_fn_ident = old_ident;
1534        self.cur_fn_return_values = old;
1535    }
1536
1537    fn visit_fn_expr<'ast: 'r, 'r>(
1538        &mut self,
1539        expr: &'ast FnExpr,
1540        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1541    ) {
1542        let old = self
1543            .cur_fn_return_values
1544            .replace(get_fn_init_return_vals(expr.function.body.as_ref()));
1545        let old_ident = self.cur_fn_ident;
1546        self.cur_fn_ident = expr.function.span.lo.0;
1547        expr.visit_children_with_ast_path(self, ast_path);
1548        let return_value = self.take_return_values();
1549
1550        if let Some(ident) = &expr.ident {
1551            self.add_value(
1552                ident.to_id(),
1553                JsValue::function(self.cur_fn_ident, return_value),
1554            );
1555        } else {
1556            self.add_value(
1557                (
1558                    format!("*anonymous function {}*", expr.function.span.lo.0).into(),
1559                    SyntaxContext::empty(),
1560                ),
1561                JsValue::function(self.cur_fn_ident, return_value),
1562            );
1563        }
1564
1565        self.cur_fn_ident = old_ident;
1566        self.cur_fn_return_values = old;
1567    }
1568
1569    fn visit_arrow_expr<'ast: 'r, 'r>(
1570        &mut self,
1571        expr: &'ast ArrowExpr,
1572        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1573    ) {
1574        let old_return_values = replace(
1575            &mut self.cur_fn_return_values,
1576            expr.body
1577                .as_block_stmt()
1578                .map(|block| get_fn_init_return_vals(Some(block))),
1579        );
1580        let old_ident = self.cur_fn_ident;
1581        self.cur_fn_ident = expr.span.lo.0;
1582
1583        let value = self.current_value.take();
1584        for (index, p) in expr.params.iter().enumerate() {
1585            self.current_value = Some(JsValue::Argument(self.cur_fn_ident, index));
1586            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
1587                expr,
1588                ArrowExprField::Params(index),
1589            ));
1590            p.visit_with_ast_path(self, &mut ast_path);
1591        }
1592        self.current_value = value;
1593
1594        {
1595            let mut ast_path =
1596                ast_path.with_guard(AstParentNodeRef::ArrowExpr(expr, ArrowExprField::Body));
1597            expr.body.visit_with_ast_path(self, &mut ast_path);
1598        }
1599
1600        let return_value = match &*expr.body {
1601            BlockStmtOrExpr::BlockStmt(_) => self.take_return_values(),
1602            BlockStmtOrExpr::Expr(inner_expr) => Box::new(self.eval_context.eval(inner_expr)),
1603        };
1604
1605        self.add_value(
1606            (
1607                format!("*arrow function {}*", expr.span.lo.0).into(),
1608                SyntaxContext::empty(),
1609            ),
1610            JsValue::function(self.cur_fn_ident, return_value),
1611        );
1612
1613        self.cur_fn_ident = old_ident;
1614        self.cur_fn_return_values = old_return_values;
1615    }
1616
1617    fn visit_class_decl<'ast: 'r, 'r>(
1618        &mut self,
1619        decl: &'ast ClassDecl,
1620        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1621    ) {
1622        self.add_value_from_expr(
1623            decl.ident.to_id(),
1624            // TODO avoid clone
1625            &Expr::Class(ClassExpr {
1626                ident: Some(decl.ident.clone()),
1627                class: decl.class.clone(),
1628            }),
1629        );
1630        decl.visit_children_with_ast_path(self, ast_path);
1631    }
1632
1633    fn visit_var_decl<'ast: 'r, 'r>(
1634        &mut self,
1635        n: &'ast VarDecl,
1636        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1637    ) {
1638        let old = self.var_decl_kind;
1639        self.var_decl_kind = Some(n.kind);
1640        n.visit_children_with_ast_path(self, ast_path);
1641        self.var_decl_kind = old;
1642    }
1643
1644    fn visit_var_declarator<'ast: 'r, 'r>(
1645        &mut self,
1646        n: &'ast VarDeclarator,
1647        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1648    ) {
1649        if self.var_decl_kind.is_some() {
1650            if let Some(init) = &n.init {
1651                // For case like
1652                //
1653                // if (shouldRun()) {
1654                //   var x = true;
1655                // }
1656                // if (x) {
1657                // }
1658                //
1659                // The variable `x` is undefined
1660
1661                let should_include_undefined = matches!(self.var_decl_kind, Some(VarDeclKind::Var))
1662                    && is_lexically_block_scope(ast_path);
1663                let init_value = self.eval_context.eval(init);
1664                self.current_value = Some(if should_include_undefined {
1665                    JsValue::alternatives(vec![
1666                        init_value,
1667                        JsValue::Constant(ConstantValue::Undefined),
1668                    ])
1669                } else {
1670                    init_value
1671                });
1672            }
1673        }
1674        {
1675            let mut ast_path =
1676                ast_path.with_guard(AstParentNodeRef::VarDeclarator(n, VarDeclaratorField::Name));
1677
1678            self.visit_pat(&n.name, &mut ast_path);
1679        }
1680        self.current_value = None;
1681        {
1682            let mut ast_path =
1683                ast_path.with_guard(AstParentNodeRef::VarDeclarator(n, VarDeclaratorField::Init));
1684
1685            self.visit_opt_expr(&n.init, &mut ast_path);
1686        }
1687    }
1688
1689    fn visit_for_of_stmt<'ast: 'r, 'r>(
1690        &mut self,
1691        n: &'ast ForOfStmt,
1692        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
1693    ) {
1694        {
1695            let mut ast_path =
1696                ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Right));
1697            self.current_value = None;
1698            self.visit_expr(&n.right, &mut ast_path);
1699        }
1700
1701        let array = self.eval_context.eval(&n.right);
1702
1703        {
1704            let mut ast_path =
1705                ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Left));
1706            self.current_value = Some(JsValue::iterated(Box::new(array)));
1707            self.visit_for_head(&n.left, &mut ast_path);
1708        }
1709
1710        let mut ast_path =
1711            ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Body));
1712
1713        self.visit_stmt(&n.body, &mut ast_path);
1714    }
1715
1716    fn visit_simple_assign_target<'ast: 'r, 'r>(
1717        &mut self,
1718        n: &'ast SimpleAssignTarget,
1719        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
1720    ) {
1721        let value = self.current_value.take();
1722        if let SimpleAssignTarget::Ident(i) = n {
1723            n.visit_children_with_ast_path(self, ast_path);
1724
1725            self.add_value(
1726                i.to_id(),
1727                value.unwrap_or_else(|| {
1728                    JsValue::unknown(JsValue::Variable(i.to_id()), false, "pattern without value")
1729                }),
1730            );
1731            return;
1732        }
1733
1734        n.visit_children_with_ast_path(self, ast_path);
1735    }
1736
1737    fn visit_pat<'ast: 'r, 'r>(
1738        &mut self,
1739        pat: &'ast Pat,
1740        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1741    ) {
1742        let value = self.current_value.take();
1743        match pat {
1744            Pat::Ident(i) => {
1745                self.add_value(
1746                    i.to_id(),
1747                    value.unwrap_or_else(|| {
1748                        JsValue::unknown(
1749                            JsValue::Variable(i.to_id()),
1750                            false,
1751                            "pattern without value",
1752                        )
1753                    }),
1754                );
1755            }
1756
1757            Pat::Array(arr) => {
1758                match &value {
1759                    Some(JsValue::Array { items, .. }) => {
1760                        let mut ast_path =
1761                            ast_path.with_guard(AstParentNodeRef::Pat(pat, PatField::Array));
1762                        for (idx, elem) in arr.elems.iter().enumerate() {
1763                            self.current_value = items.get(idx).cloned();
1764                            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrayPat(
1765                                arr,
1766                                ArrayPatField::Elems(idx),
1767                            ));
1768                            elem.visit_with_ast_path(self, &mut ast_path);
1769                        }
1770
1771                        // We should not call visit_children_with
1772                        return;
1773                    }
1774
1775                    Some(value) => {
1776                        let mut ast_path =
1777                            ast_path.with_guard(AstParentNodeRef::Pat(pat, PatField::Array));
1778                        for (idx, elem) in arr.elems.iter().enumerate() {
1779                            self.current_value = Some(JsValue::member(
1780                                Box::new(value.clone()),
1781                                Box::new(JsValue::Constant(ConstantValue::Num(ConstantNumber(
1782                                    idx as f64,
1783                                )))),
1784                            ));
1785                            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrayPat(
1786                                arr,
1787                                ArrayPatField::Elems(idx),
1788                            ));
1789                            elem.visit_with_ast_path(self, &mut ast_path);
1790                        }
1791                        // We should not call visit_children_with
1792                        return;
1793                    }
1794
1795                    None => {}
1796                }
1797            }
1798
1799            Pat::Object(obj) => {
1800                let value =
1801                    value.unwrap_or_else(|| JsValue::unknown_empty(false, "pattern without value"));
1802
1803                self.visit_pat_with_value(pat, obj, value, ast_path);
1804
1805                // We should not call visit_children_with
1806                return;
1807            }
1808
1809            _ => {}
1810        }
1811        pat.visit_children_with_ast_path(self, ast_path);
1812    }
1813
1814    fn visit_return_stmt<'ast: 'r, 'r>(
1815        &mut self,
1816        stmt: &'ast ReturnStmt,
1817        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1818    ) {
1819        stmt.visit_children_with_ast_path(self, ast_path);
1820
1821        if let Some(values) = &mut self.cur_fn_return_values {
1822            let return_value = stmt
1823                .arg
1824                .as_deref()
1825                .map(|e| self.eval_context.eval(e))
1826                .unwrap_or(JsValue::FreeVar(atom!("undefined")));
1827
1828            values.push(return_value);
1829        }
1830
1831        self.early_return_stack.push(EarlyReturn::Always {
1832            prev_effects: take(&mut self.effects),
1833            start_ast_path: as_parent_path(ast_path),
1834        });
1835    }
1836
1837    fn visit_ident<'ast: 'r, 'r>(
1838        &mut self,
1839        ident: &'ast Ident,
1840        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1841    ) {
1842        if !(matches!(
1843            ast_path.last(),
1844            Some(AstParentNodeRef::Expr(_, ExprField::Ident))
1845                | Some(AstParentNodeRef::Prop(_, PropField::Shorthand))
1846        ) || matches!(
1847            ast_path.get(ast_path.len() - 2),
1848            Some(AstParentNodeRef::SimpleAssignTarget(
1849                _,
1850                SimpleAssignTargetField::Ident,
1851            ))
1852        )) {
1853            return;
1854        }
1855
1856        if let Some((esm_reference_index, export)) =
1857            self.eval_context.imports.get_binding(&ident.to_id())
1858        {
1859            if export.is_none()
1860                && !self
1861                    .eval_context
1862                    .imports
1863                    .should_import_all(esm_reference_index)
1864            {
1865                // export.is_none() checks for a namespace import.
1866
1867                // Note: This is optimization that can be applied if we don't need to
1868                // import all bindings
1869                if let Some(AstParentNodeRef::MemberExpr(member, MemberExprField::Obj)) =
1870                    ast_path.get(ast_path.len() - 2)
1871                {
1872                    // Skip if it's on the LHS of assignment
1873                    let is_lhs = matches!(
1874                        ast_path.get(ast_path.len() - 3),
1875                        Some(AstParentNodeRef::SimpleAssignTarget(
1876                            _,
1877                            SimpleAssignTargetField::Member
1878                        ))
1879                    );
1880
1881                    if !is_lhs {
1882                        if let Some(prop) = self.eval_context.eval_member_prop(&member.prop) {
1883                            if let Some(prop_str) = prop.as_str() {
1884                                // a namespace member access like
1885                                // `import * as ns from "..."; ns.exportName`
1886                                self.add_effect(Effect::ImportedBinding {
1887                                    esm_reference_index,
1888                                    export: Some(prop_str.into()),
1889                                    ast_path: as_parent_path_skip(ast_path, 1),
1890                                    span: member.span(),
1891                                    in_try: is_in_try(ast_path),
1892                                });
1893                                return;
1894                            }
1895                        }
1896                    }
1897                }
1898            }
1899
1900            self.add_effect(Effect::ImportedBinding {
1901                esm_reference_index,
1902                export,
1903                ast_path: as_parent_path(ast_path),
1904                span: ident.span(),
1905                in_try: is_in_try(ast_path),
1906            })
1907        } else if is_unresolved(ident, self.eval_context.unresolved_mark)
1908            || self.eval_context.force_free_values.contains(&ident.to_id())
1909        {
1910            self.add_effect(Effect::FreeVar {
1911                var: Box::new(JsValue::FreeVar(ident.sym.clone())),
1912                ast_path: as_parent_path(ast_path),
1913                span: ident.span(),
1914                in_try: is_in_try(ast_path),
1915            })
1916        }
1917    }
1918
1919    fn visit_meta_prop_expr<'ast: 'r, 'r>(
1920        &mut self,
1921        expr: &'ast MetaPropExpr,
1922        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1923    ) {
1924        if expr.kind == MetaPropKind::ImportMeta {
1925            // MetaPropExpr also covers `new.target`. Only consider `import.meta`
1926            // an effect.
1927            self.add_effect(Effect::ImportMeta {
1928                span: expr.span,
1929                ast_path: as_parent_path(ast_path),
1930                in_try: is_in_try(ast_path),
1931            })
1932        }
1933    }
1934
1935    fn visit_program<'ast: 'r, 'r>(
1936        &mut self,
1937        program: &'ast Program,
1938        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1939    ) {
1940        self.effects = take(&mut self.data.effects);
1941        program.visit_children_with_ast_path(self, ast_path);
1942        self.end_early_return_block();
1943        self.effects.append(&mut self.hoisted_effects);
1944        self.data.effects = take(&mut self.effects);
1945    }
1946
1947    fn visit_cond_expr<'ast: 'r, 'r>(
1948        &mut self,
1949        expr: &'ast CondExpr,
1950        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1951    ) {
1952        {
1953            let mut ast_path =
1954                ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Test));
1955            expr.test.visit_with_ast_path(self, &mut ast_path);
1956        }
1957
1958        let prev_effects = take(&mut self.effects);
1959        let then = {
1960            let mut ast_path =
1961                ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Cons));
1962            expr.cons.visit_with_ast_path(self, &mut ast_path);
1963            Box::new(EffectsBlock {
1964                effects: take(&mut self.effects),
1965                range: AstPathRange::Exact(as_parent_path(&ast_path)),
1966            })
1967        };
1968        let r#else = {
1969            let mut ast_path =
1970                ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Alt));
1971            expr.alt.visit_with_ast_path(self, &mut ast_path);
1972            Box::new(EffectsBlock {
1973                effects: take(&mut self.effects),
1974                range: AstPathRange::Exact(as_parent_path(&ast_path)),
1975            })
1976        };
1977        self.effects = prev_effects;
1978
1979        self.add_conditional_effect(
1980            &expr.test,
1981            ast_path,
1982            AstParentKind::CondExpr(CondExprField::Test),
1983            expr.span(),
1984            ConditionalKind::Ternary { then, r#else },
1985        );
1986    }
1987
1988    fn visit_if_stmt<'ast: 'r, 'r>(
1989        &mut self,
1990        stmt: &'ast IfStmt,
1991        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1992    ) {
1993        {
1994            let mut ast_path =
1995                ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Test));
1996            stmt.test.visit_with_ast_path(self, &mut ast_path);
1997        }
1998        let prev_effects = take(&mut self.effects);
1999        let prev_early_return_stack = take(&mut self.early_return_stack);
2000        let then_returning;
2001        let then = {
2002            let mut ast_path =
2003                ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Cons));
2004            stmt.cons.visit_with_ast_path(self, &mut ast_path);
2005            then_returning = self.end_early_return_block();
2006            Box::new(EffectsBlock {
2007                effects: take(&mut self.effects),
2008                range: AstPathRange::Exact(as_parent_path(&ast_path)),
2009            })
2010        };
2011        let mut else_returning = false;
2012        let r#else = stmt.alt.as_ref().map(|alt| {
2013            let mut ast_path =
2014                ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Alt));
2015            alt.visit_with_ast_path(self, &mut ast_path);
2016            else_returning = self.end_early_return_block();
2017            Box::new(EffectsBlock {
2018                effects: take(&mut self.effects),
2019                range: AstPathRange::Exact(as_parent_path(&ast_path)),
2020            })
2021        });
2022        self.early_return_stack = prev_early_return_stack;
2023        self.effects = prev_effects;
2024        self.add_conditional_if_effect_with_early_return(
2025            &stmt.test,
2026            ast_path,
2027            AstParentKind::IfStmt(IfStmtField::Test),
2028            stmt.span(),
2029            (!then.is_empty()).then_some(then),
2030            r#else.and_then(|block| (!block.is_empty()).then_some(block)),
2031            then_returning,
2032            else_returning,
2033        );
2034    }
2035
2036    fn visit_try_stmt<'ast: 'r, 'r>(
2037        &mut self,
2038        stmt: &'ast TryStmt,
2039        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2040    ) {
2041        let prev_effects = take(&mut self.effects);
2042        let prev_early_return_stack = take(&mut self.early_return_stack);
2043        let mut block = {
2044            let mut ast_path =
2045                ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Block));
2046            stmt.block.visit_with_ast_path(self, &mut ast_path);
2047            self.end_early_return_block();
2048            take(&mut self.effects)
2049        };
2050        let mut handler = if let Some(handler) = stmt.handler.as_ref() {
2051            let mut ast_path =
2052                ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Handler));
2053            handler.visit_with_ast_path(self, &mut ast_path);
2054            self.end_early_return_block();
2055            take(&mut self.effects)
2056        } else {
2057            vec![]
2058        };
2059        self.early_return_stack = prev_early_return_stack;
2060        self.effects = prev_effects;
2061        self.effects.append(&mut block);
2062        self.effects.append(&mut handler);
2063        if let Some(finalizer) = stmt.finalizer.as_ref() {
2064            let mut ast_path =
2065                ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Finalizer));
2066            finalizer.visit_with_ast_path(self, &mut ast_path);
2067        };
2068    }
2069
2070    fn visit_switch_case<'ast: 'r, 'r>(
2071        &mut self,
2072        case: &'ast SwitchCase,
2073        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2074    ) {
2075        let prev_effects = take(&mut self.effects);
2076        let prev_early_return_stack = take(&mut self.early_return_stack);
2077        case.visit_children_with_ast_path(self, ast_path);
2078        self.end_early_return_block();
2079        let mut effects = take(&mut self.effects);
2080        self.early_return_stack = prev_early_return_stack;
2081        self.effects = prev_effects;
2082        self.effects.append(&mut effects);
2083    }
2084
2085    fn visit_block_stmt<'ast: 'r, 'r>(
2086        &mut self,
2087        n: &'ast BlockStmt,
2088        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2089    ) {
2090        let block_type = if ast_path.len() < 2 {
2091            Some(false)
2092        } else if matches!(
2093            &ast_path[ast_path.len() - 2..],
2094            [
2095                AstParentNodeRef::IfStmt(_, IfStmtField::Cons),
2096                AstParentNodeRef::Stmt(_, StmtField::Block)
2097            ] | [
2098                AstParentNodeRef::IfStmt(_, IfStmtField::Alt),
2099                AstParentNodeRef::Stmt(_, StmtField::Block)
2100            ] | [_, AstParentNodeRef::TryStmt(_, TryStmtField::Block,)]
2101                | [
2102                    AstParentNodeRef::TryStmt(_, TryStmtField::Handler),
2103                    AstParentNodeRef::CatchClause(_, CatchClauseField::Body)
2104                ]
2105                | [
2106                    AstParentNodeRef::LabeledStmt(_, LabeledStmtField::Body),
2107                    AstParentNodeRef::Stmt(_, StmtField::Block)
2108                ]
2109        ) {
2110            None
2111        } else if matches!(
2112            &ast_path[ast_path.len() - 2..],
2113            [_, AstParentNodeRef::Function(_, FunctionField::Body)]
2114                | [
2115                    AstParentNodeRef::ArrowExpr(_, ArrowExprField::Body),
2116                    AstParentNodeRef::BlockStmtOrExpr(_, BlockStmtOrExprField::BlockStmt)
2117                ]
2118                | [_, AstParentNodeRef::GetterProp(_, GetterPropField::Body)]
2119                | [_, AstParentNodeRef::SetterProp(_, SetterPropField::Body)]
2120                | [_, AstParentNodeRef::Constructor(_, ConstructorField::Body)]
2121        ) {
2122            Some(true)
2123        } else {
2124            Some(false)
2125        };
2126        match block_type {
2127            Some(true) => {
2128                let early_return_stack = take(&mut self.early_return_stack);
2129                let mut effects = take(&mut self.effects);
2130                let hoisted_effects = take(&mut self.hoisted_effects);
2131                n.visit_children_with_ast_path(self, ast_path);
2132                self.end_early_return_block();
2133                self.effects.append(&mut self.hoisted_effects);
2134                effects.append(&mut self.effects);
2135                self.hoisted_effects = hoisted_effects;
2136                self.effects = effects;
2137                self.early_return_stack = early_return_stack;
2138            }
2139            Some(false) => {
2140                n.visit_children_with_ast_path(self, ast_path);
2141                if self.end_early_return_block() {
2142                    self.early_return_stack.push(EarlyReturn::Always {
2143                        prev_effects: take(&mut self.effects),
2144                        start_ast_path: as_parent_path(ast_path),
2145                    });
2146                }
2147            }
2148            None => {
2149                n.visit_children_with_ast_path(self, ast_path);
2150            }
2151        }
2152    }
2153
2154    fn visit_unary_expr<'ast: 'r, 'r>(
2155        &mut self,
2156        n: &'ast UnaryExpr,
2157        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2158    ) {
2159        if n.op == UnaryOp::TypeOf {
2160            let arg_value = Box::new(self.eval_context.eval(&n.arg));
2161            self.add_effect(Effect::TypeOf {
2162                arg: arg_value,
2163                ast_path: as_parent_path(ast_path),
2164                span: n.span(),
2165            });
2166        }
2167
2168        n.visit_children_with_ast_path(self, ast_path);
2169    }
2170
2171    fn visit_labeled_stmt<'ast: 'r, 'r>(
2172        &mut self,
2173        stmt: &'ast LabeledStmt,
2174        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2175    ) {
2176        let mut prev_effects = take(&mut self.effects);
2177        let prev_early_return_stack = take(&mut self.early_return_stack);
2178
2179        stmt.visit_children_with_ast_path(self, ast_path);
2180
2181        self.end_early_return_block();
2182
2183        let effects = take(&mut self.effects);
2184
2185        prev_effects.push(Effect::Conditional {
2186            condition: Box::new(JsValue::unknown_empty(true, "labeled statement")),
2187            kind: Box::new(ConditionalKind::Labeled {
2188                body: Box::new(EffectsBlock {
2189                    effects,
2190                    range: AstPathRange::Exact(as_parent_path_with(
2191                        ast_path,
2192                        AstParentKind::LabeledStmt(LabeledStmtField::Body),
2193                    )),
2194                }),
2195            }),
2196            ast_path: as_parent_path(ast_path),
2197            span: stmt.span,
2198            in_try: is_in_try(ast_path),
2199        });
2200
2201        self.effects = prev_effects;
2202        self.early_return_stack = prev_early_return_stack;
2203    }
2204}
2205
2206fn is_lexically_block_scope(ast_path: &mut AstNodePath<AstParentNodeRef>) -> bool {
2207    let mut iter = ast_path.iter().rev().peekable();
2208
2209    while let Some(cur) = iter.next() {
2210        // If it's a block statement, we need to check if it's Function#body
2211        if matches!(cur.kind(), AstParentKind::BlockStmt(..)) {
2212            if let Some(next) = iter.peek() {
2213                return !matches!(next.kind(), AstParentKind::Function(FunctionField::Body));
2214            }
2215            return false;
2216        }
2217    }
2218
2219    // This `var` is not in a block scope
2220    false
2221}
2222
2223impl Analyzer<'_> {
2224    fn add_conditional_if_effect_with_early_return(
2225        &mut self,
2226        test: &Expr,
2227        ast_path: &AstNodePath<AstParentNodeRef<'_>>,
2228        condition_ast_kind: AstParentKind,
2229        span: Span,
2230        then: Option<Box<EffectsBlock>>,
2231        r#else: Option<Box<EffectsBlock>>,
2232        early_return_when_true: bool,
2233        early_return_when_false: bool,
2234    ) {
2235        if then.is_none() && r#else.is_none() && !early_return_when_false && !early_return_when_true
2236        {
2237            return;
2238        }
2239        let condition = Box::new(self.eval_context.eval(test));
2240        if condition.is_unknown() {
2241            if let Some(mut then) = then {
2242                self.effects.append(&mut then.effects);
2243            }
2244            if let Some(mut r#else) = r#else {
2245                self.effects.append(&mut r#else.effects);
2246            }
2247            return;
2248        }
2249        match (early_return_when_true, early_return_when_false) {
2250            (true, false) => {
2251                self.early_return_stack.push(EarlyReturn::Conditional {
2252                    prev_effects: take(&mut self.effects),
2253                    start_ast_path: as_parent_path(ast_path),
2254                    condition,
2255                    then,
2256                    r#else,
2257                    condition_ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2258                    span,
2259                    in_try: is_in_try(ast_path),
2260                    early_return_condition_value: true,
2261                });
2262            }
2263            (false, true) => {
2264                self.early_return_stack.push(EarlyReturn::Conditional {
2265                    prev_effects: take(&mut self.effects),
2266                    start_ast_path: as_parent_path(ast_path),
2267                    condition,
2268                    then,
2269                    r#else,
2270                    condition_ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2271                    span,
2272                    in_try: is_in_try(ast_path),
2273                    early_return_condition_value: false,
2274                });
2275            }
2276            (false, false) | (true, true) => {
2277                let kind = match (then, r#else) {
2278                    (Some(then), Some(r#else)) => ConditionalKind::IfElse { then, r#else },
2279                    (Some(then), None) => ConditionalKind::If { then },
2280                    (None, Some(r#else)) => ConditionalKind::Else { r#else },
2281                    (None, None) => unreachable!(),
2282                };
2283                self.add_effect(Effect::Conditional {
2284                    condition,
2285                    kind: Box::new(kind),
2286                    ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2287                    span,
2288                    in_try: is_in_try(ast_path),
2289                });
2290                if early_return_when_false && early_return_when_true {
2291                    self.early_return_stack.push(EarlyReturn::Always {
2292                        prev_effects: take(&mut self.effects),
2293                        start_ast_path: as_parent_path(ast_path),
2294                    });
2295                }
2296            }
2297        }
2298    }
2299
2300    fn add_conditional_effect(
2301        &mut self,
2302        test: &Expr,
2303        ast_path: &AstNodePath<AstParentNodeRef<'_>>,
2304        ast_kind: AstParentKind,
2305        span: Span,
2306        mut cond_kind: ConditionalKind,
2307    ) {
2308        let condition = Box::new(self.eval_context.eval(test));
2309        if condition.is_unknown() {
2310            match &mut cond_kind {
2311                ConditionalKind::If { then } => {
2312                    self.effects.append(&mut then.effects);
2313                }
2314                ConditionalKind::Else { r#else } => {
2315                    self.effects.append(&mut r#else.effects);
2316                }
2317                ConditionalKind::IfElse { then, r#else }
2318                | ConditionalKind::Ternary { then, r#else } => {
2319                    self.effects.append(&mut then.effects);
2320                    self.effects.append(&mut r#else.effects);
2321                }
2322                ConditionalKind::IfElseMultiple { then, r#else } => {
2323                    for block in then {
2324                        self.effects.append(&mut block.effects);
2325                    }
2326                    for block in r#else {
2327                        self.effects.append(&mut block.effects);
2328                    }
2329                }
2330                ConditionalKind::And { expr }
2331                | ConditionalKind::Or { expr }
2332                | ConditionalKind::NullishCoalescing { expr } => {
2333                    self.effects.append(&mut expr.effects);
2334                }
2335                ConditionalKind::Labeled { body } => {
2336                    self.effects.append(&mut body.effects);
2337                }
2338            }
2339        } else {
2340            self.add_effect(Effect::Conditional {
2341                condition,
2342                kind: Box::new(cond_kind),
2343                ast_path: as_parent_path_with(ast_path, ast_kind),
2344                span,
2345                in_try: is_in_try(ast_path),
2346            });
2347        }
2348    }
2349
2350    fn visit_pat_with_value<'ast: 'r, 'r>(
2351        &mut self,
2352        pat: &'ast Pat,
2353        obj: &'ast ObjectPat,
2354        current_value: JsValue,
2355        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2356    ) {
2357        let mut ast_path = ast_path.with_guard(AstParentNodeRef::Pat(pat, PatField::Object));
2358        for (i, prop) in obj.props.iter().enumerate() {
2359            let mut ast_path =
2360                ast_path.with_guard(AstParentNodeRef::ObjectPat(obj, ObjectPatField::Props(i)));
2361            match prop {
2362                ObjectPatProp::KeyValue(kv) => {
2363                    let mut ast_path = ast_path.with_guard(AstParentNodeRef::ObjectPatProp(
2364                        prop,
2365                        ObjectPatPropField::KeyValue,
2366                    ));
2367                    let KeyValuePatProp { key, value } = kv;
2368                    let key_value = self.eval_context.eval_prop_name(key);
2369                    {
2370                        let mut ast_path = ast_path.with_guard(AstParentNodeRef::KeyValuePatProp(
2371                            kv,
2372                            KeyValuePatPropField::Key,
2373                        ));
2374                        key.visit_with_ast_path(self, &mut ast_path);
2375                    }
2376                    self.current_value = Some(JsValue::member(
2377                        Box::new(current_value.clone()),
2378                        Box::new(key_value),
2379                    ));
2380                    {
2381                        let mut ast_path = ast_path.with_guard(AstParentNodeRef::KeyValuePatProp(
2382                            kv,
2383                            KeyValuePatPropField::Value,
2384                        ));
2385                        value.visit_with_ast_path(self, &mut ast_path);
2386                    }
2387                }
2388                ObjectPatProp::Assign(assign) => {
2389                    let mut ast_path = ast_path.with_guard(AstParentNodeRef::ObjectPatProp(
2390                        prop,
2391                        ObjectPatPropField::Assign,
2392                    ));
2393                    let AssignPatProp { key, value, .. } = assign;
2394                    let key_value = key.sym.clone().into();
2395                    {
2396                        let mut ast_path = ast_path.with_guard(AstParentNodeRef::AssignPatProp(
2397                            assign,
2398                            AssignPatPropField::Key,
2399                        ));
2400                        key.visit_with_ast_path(self, &mut ast_path);
2401                    }
2402                    self.add_value(
2403                        key.to_id(),
2404                        if let Some(box value) = value {
2405                            let value = self.eval_context.eval(value);
2406                            JsValue::alternatives(vec![
2407                                JsValue::member(
2408                                    Box::new(current_value.clone()),
2409                                    Box::new(key_value),
2410                                ),
2411                                value,
2412                            ])
2413                        } else {
2414                            JsValue::member(Box::new(current_value.clone()), Box::new(key_value))
2415                        },
2416                    );
2417                    {
2418                        let mut ast_path = ast_path.with_guard(AstParentNodeRef::AssignPatProp(
2419                            assign,
2420                            AssignPatPropField::Value,
2421                        ));
2422                        value.visit_with_ast_path(self, &mut ast_path);
2423                    }
2424                }
2425
2426                _ => prop.visit_with_ast_path(self, &mut ast_path),
2427            }
2428        }
2429    }
2430}
2431
2432fn extract_var_from_umd_factory(callee: &Expr, args: &[ExprOrSpread]) -> Option<Id> {
2433    match unparen(callee) {
2434        Expr::Ident(Ident { sym, .. }) => {
2435            if &**sym == "define" {
2436                if let Expr::Fn(FnExpr { function, .. }) = &*args[0].expr {
2437                    let params = &*function.params;
2438                    if params.len() == 1 {
2439                        if let Pat::Ident(param) = &params[0].pat {
2440                            if &*param.id.sym == "require" {
2441                                return Some(param.to_id());
2442                            }
2443                        }
2444                    }
2445                }
2446            }
2447        }
2448
2449        // umd may use (function (factory){
2450        //   // Somewhere, define(['require', 'exports'], factory)
2451        // }(function (require, exports){}))
2452        //
2453        // In all module system which has `require`, `require` in the factory function can be
2454        // treated as a well-known require.
2455        Expr::Fn(FnExpr { function, .. }) => {
2456            let params = &*function.params;
2457            if params.len() == 1 {
2458                if let Some(FnExpr { function, .. }) =
2459                    args.first().and_then(|arg| arg.expr.as_fn_expr())
2460                {
2461                    let params = &*function.params;
2462                    if !params.is_empty() {
2463                        if let Pat::Ident(param) = &params[0].pat {
2464                            if &*param.id.sym == "require" {
2465                                return Some(param.to_id());
2466                            }
2467                        }
2468                    }
2469                }
2470            }
2471        }
2472
2473        _ => {}
2474    }
2475
2476    None
2477}
2478
2479fn get_fn_init_return_vals(fn_body_stmts: Option<&BlockStmt>) -> Vec<JsValue> {
2480    let has_final_return_val = match fn_body_stmts {
2481        Some(fn_body_stmts) => {
2482            matches!(
2483                fn_body_stmts.stmts.last(),
2484                Some(Stmt::Return(ReturnStmt { arg: Some(_), .. }))
2485            )
2486        }
2487        None => false,
2488    };
2489
2490    if has_final_return_val {
2491        vec![]
2492    } else {
2493        vec![JsValue::Constant(ConstantValue::Undefined)]
2494    }
2495}