turbopack_ecmascript/analyzer/
graph.rs

1use std::{
2    iter,
3    mem::{replace, take},
4    sync::Arc,
5};
6
7use anyhow::{Ok, Result};
8use rustc_hash::{FxHashMap, FxHashSet};
9use swc_core::{
10    atoms::Atom,
11    base::try_with_handler,
12    common::{
13        GLOBALS, Mark, SourceMap, Span, Spanned, SyntaxContext, comments::Comments,
14        pass::AstNodePath, sync::Lrc,
15    },
16    ecma::{
17        ast::*,
18        atoms::atom,
19        utils::contains_ident_ref,
20        visit::{fields::*, *},
21    },
22};
23use turbo_rcstr::{RcStr, rcstr};
24use turbo_tasks::ResolvedVc;
25use turbopack_core::source::Source;
26
27use super::{
28    ConstantNumber, ConstantValue, ImportMap, JsValue, ObjectPart, WellKnownFunctionKind,
29    is_unresolved_id,
30};
31use crate::{
32    AnalyzeMode, SpecifiedModuleType,
33    analyzer::{WellKnownObjectKind, is_unresolved},
34    references::{constant_value::parse_single_expr_lit, for_each_ident_in_pat},
35    utils::{AstPathRange, unparen},
36};
37
38#[derive(Debug)]
39pub struct EffectsBlock {
40    pub effects: Vec<Effect>,
41    pub range: AstPathRange,
42}
43
44impl EffectsBlock {
45    pub fn is_empty(&self) -> bool {
46        self.effects.is_empty()
47    }
48}
49
50#[derive(Debug)]
51pub enum ConditionalKind {
52    /// The blocks of an `if` statement without an `else` block.
53    If { then: Box<EffectsBlock> },
54    /// The blocks of an `if ... else` or `if { ... return ... } ...` statement.
55    IfElse {
56        then: Box<EffectsBlock>,
57        r#else: Box<EffectsBlock>,
58    },
59    /// The blocks of an `if ... else` statement.
60    Else { r#else: Box<EffectsBlock> },
61    /// The blocks of an `if { ... return ... } else { ... } ...` or `if { ... }
62    /// else { ... return ... } ...` statement.
63    IfElseMultiple {
64        then: Vec<Box<EffectsBlock>>,
65        r#else: Vec<Box<EffectsBlock>>,
66    },
67    /// The expressions on the right side of the `?:` operator.
68    Ternary {
69        then: Box<EffectsBlock>,
70        r#else: Box<EffectsBlock>,
71    },
72    /// The expression on the right side of the `&&` operator.
73    And { expr: Box<EffectsBlock> },
74    /// The expression on the right side of the `||` operator.
75    Or { expr: Box<EffectsBlock> },
76    /// The expression on the right side of the `??` operator.
77    NullishCoalescing { expr: Box<EffectsBlock> },
78    /// The expression on the right side of a labeled statement.
79    Labeled { body: Box<EffectsBlock> },
80}
81
82impl ConditionalKind {
83    /// Normalizes all contained values.
84    pub fn normalize(&mut self) {
85        match self {
86            ConditionalKind::If { then: block }
87            | ConditionalKind::Else { r#else: block }
88            | ConditionalKind::And { expr: block, .. }
89            | ConditionalKind::Or { expr: block, .. }
90            | ConditionalKind::NullishCoalescing { expr: block, .. } => {
91                for effect in &mut block.effects {
92                    effect.normalize();
93                }
94            }
95            ConditionalKind::IfElse { then, r#else, .. }
96            | ConditionalKind::Ternary { then, r#else, .. } => {
97                for effect in &mut then.effects {
98                    effect.normalize();
99                }
100                for effect in &mut r#else.effects {
101                    effect.normalize();
102                }
103            }
104            ConditionalKind::IfElseMultiple { then, r#else, .. } => {
105                for block in then.iter_mut().chain(r#else.iter_mut()) {
106                    for effect in &mut block.effects {
107                        effect.normalize();
108                    }
109                }
110            }
111            ConditionalKind::Labeled { body } => {
112                for effect in &mut body.effects {
113                    effect.normalize();
114                }
115            }
116        }
117    }
118}
119
120#[derive(Debug)]
121pub enum EffectArg {
122    Value(JsValue),
123    Closure(JsValue, Box<EffectsBlock>),
124    Spread,
125}
126
127impl EffectArg {
128    /// Normalizes all contained values.
129    pub fn normalize(&mut self) {
130        match self {
131            EffectArg::Value(value) => value.normalize(),
132            EffectArg::Closure(value, effects) => {
133                value.normalize();
134                for effect in &mut effects.effects {
135                    effect.normalize();
136                }
137            }
138            EffectArg::Spread => {}
139        }
140    }
141}
142
143#[derive(Debug)]
144pub enum Effect {
145    /// Some condition which affects which effects might be executed. If the
146    /// condition evaluates to some compile-time constant, we can use that
147    /// to determine which effects are executed and remove the others.
148    Conditional {
149        condition: Box<JsValue>,
150        kind: Box<ConditionalKind>,
151        /// The ast path to the condition.
152        ast_path: Vec<AstParentKind>,
153        span: Span,
154    },
155    /// A function call or a new call of a function.
156    Call {
157        func: Box<JsValue>,
158        args: Vec<EffectArg>,
159        ast_path: Vec<AstParentKind>,
160        span: Span,
161        in_try: bool,
162        new: bool,
163    },
164    /// A function call or a new call of a property of an object.
165    MemberCall {
166        obj: Box<JsValue>,
167        prop: Box<JsValue>,
168        args: Vec<EffectArg>,
169        ast_path: Vec<AstParentKind>,
170        span: Span,
171        in_try: bool,
172        new: bool,
173    },
174    /// A property access.
175    Member {
176        obj: Box<JsValue>,
177        prop: Box<JsValue>,
178        ast_path: Vec<AstParentKind>,
179        span: Span,
180    },
181    /// A reference to an imported binding.
182    ImportedBinding {
183        esm_reference_index: usize,
184        export: Option<RcStr>,
185        ast_path: Vec<AstParentKind>,
186        span: Span,
187    },
188    /// A reference to a free var access.
189    FreeVar {
190        var: Atom,
191        ast_path: Vec<AstParentKind>,
192        span: Span,
193    },
194    /// A typeof expression
195    TypeOf {
196        arg: Box<JsValue>,
197        ast_path: Vec<AstParentKind>,
198        span: Span,
199    },
200    // TODO ImportMeta should be replaced with Member
201    /// A reference to `import.meta`.
202    ImportMeta {
203        ast_path: Vec<AstParentKind>,
204        span: Span,
205    },
206    /// Unreachable code, e.g. after a `return` statement.
207    Unreachable { start_ast_path: Vec<AstParentKind> },
208}
209
210impl Effect {
211    /// Normalizes all contained values.
212    pub fn normalize(&mut self) {
213        match self {
214            Effect::Conditional {
215                condition, kind, ..
216            } => {
217                condition.normalize();
218                kind.normalize();
219            }
220            Effect::Call { func, args, .. } => {
221                func.normalize();
222                for arg in args.iter_mut() {
223                    arg.normalize();
224                }
225            }
226            Effect::MemberCall {
227                obj, prop, args, ..
228            } => {
229                obj.normalize();
230                prop.normalize();
231                for arg in args.iter_mut() {
232                    arg.normalize();
233                }
234            }
235            Effect::Member { obj, prop, .. } => {
236                obj.normalize();
237                prop.normalize();
238            }
239            Effect::ImportedBinding { .. } => {}
240            Effect::TypeOf { arg, .. } => {
241                arg.normalize();
242            }
243            Effect::FreeVar { .. } => {}
244            Effect::ImportMeta { .. } => {}
245            Effect::Unreachable { .. } => {}
246        }
247    }
248}
249
250pub enum AssignmentScope {
251    ModuleEval,
252    Function,
253}
254
255#[derive(Debug, Copy, Clone, PartialEq, Eq)]
256pub enum AssignmentScopes {
257    AllInModuleEvalScope,
258    AllInFunctionScopes,
259    Mixed,
260}
261impl AssignmentScopes {
262    fn new(initial: AssignmentScope) -> Self {
263        match initial {
264            AssignmentScope::ModuleEval => AssignmentScopes::AllInModuleEvalScope,
265            AssignmentScope::Function => AssignmentScopes::AllInFunctionScopes,
266        }
267    }
268
269    fn merge(self, other: AssignmentScope) -> Self {
270        // If the other assignment kind is the same as the current one, return the current one.
271        if self == Self::new(other) {
272            self
273        } else {
274            AssignmentScopes::Mixed
275        }
276    }
277}
278
279#[derive(Debug, Clone)]
280pub struct VarMeta {
281    pub value: JsValue,
282    /// Tracks the locations where this was assigned to:
283    /// - [`AssignmentScopes::AllInModuleEvalScope`] if it was assigned only in the root scope
284    /// - [`AssignmentScopes::AllInFunctionScopes`] if it was assigned in any set of function
285    ///   scopes
286    /// - [`AssignmentScopes::Mixed`] if it was assigned in both
287    ///
288    /// This is used to track the _liveness_ of exports.
289    pub assignment_scopes: AssignmentScopes,
290}
291
292impl VarMeta {
293    pub fn new(value: JsValue, kind: AssignmentScope) -> Self {
294        Self {
295            value,
296            assignment_scopes: AssignmentScopes::new(kind),
297        }
298    }
299
300    pub fn normalize(&mut self) {
301        self.value.normalize();
302    }
303
304    fn add_alt(&mut self, value: JsValue, kind: AssignmentScope) {
305        self.value.add_alt(value);
306        self.assignment_scopes = self.assignment_scopes.merge(kind);
307    }
308}
309
310#[derive(Clone, Debug)]
311pub enum DeclUsage {
312    SideEffects,
313    Bindings(FxHashSet<Id>),
314}
315impl Default for DeclUsage {
316    fn default() -> Self {
317        DeclUsage::Bindings(Default::default())
318    }
319}
320impl DeclUsage {
321    fn add_usage(&mut self, user: &Id) {
322        match self {
323            Self::Bindings(set) => {
324                set.insert(user.clone());
325            }
326            Self::SideEffects => {}
327        }
328    }
329    fn make_side_effects(&mut self) {
330        *self = Self::SideEffects;
331    }
332}
333
334#[derive(Debug)]
335pub struct VarGraph {
336    pub values: FxHashMap<Id, VarMeta>,
337    /// Map FreeVar names to their Id to facilitate lookups into [values]
338    /// Doesn't necessarily contain every FreeVar, just those who have non trivial values.
339    pub free_var_ids: FxHashMap<Atom, Id>,
340
341    pub effects: Vec<Effect>,
342
343    // ident -> immediate usage (top level decl)
344    pub decl_usages: FxHashMap<Id, DeclUsage>,
345    // import -> immediate usage (top level decl)
346    pub import_usages: FxHashMap<usize, DeclUsage>,
347    // export name -> top level decl
348    pub exports: FxHashMap<Atom, Id>,
349}
350
351impl VarGraph {
352    pub fn normalize(&mut self) {
353        for value in self.values.values_mut() {
354            value.normalize();
355        }
356        for effect in self.effects.iter_mut() {
357            effect.normalize();
358        }
359    }
360}
361
362/// You should use same [Mark] for this function and
363/// [swc_ecma_transforms_base::resolver::resolver_with_mark]
364pub fn create_graph(
365    m: &Program,
366    eval_context: &EvalContext,
367    analyze_mode: AnalyzeMode,
368) -> VarGraph {
369    let mut graph = VarGraph {
370        values: Default::default(),
371        free_var_ids: Default::default(),
372        effects: Default::default(),
373        decl_usages: Default::default(),
374        import_usages: Default::default(),
375        exports: Default::default(),
376    };
377
378    m.visit_with_ast_path(
379        &mut Analyzer {
380            analyze_mode,
381            data: &mut graph,
382            eval_context,
383            state: Default::default(),
384            effects: Default::default(),
385            hoisted_effects: Default::default(),
386        },
387        &mut Default::default(),
388    );
389
390    graph.normalize();
391
392    graph
393}
394
395/// A context used for assembling the evaluation graph.
396#[derive(Debug)]
397pub struct EvalContext {
398    pub(crate) unresolved_mark: Mark,
399    pub(crate) top_level_mark: Mark,
400    pub(crate) imports: ImportMap,
401    pub(crate) force_free_values: Arc<FxHashSet<Id>>,
402}
403
404impl EvalContext {
405    /// Produce a new [EvalContext] from a [Program]. If you wish to support
406    /// webpackIgnore or turbopackIgnore comments, you must pass those in,
407    /// since the AST does not include comments by default.
408    pub fn new(
409        module: Option<&Program>,
410        unresolved_mark: Mark,
411        top_level_mark: Mark,
412        force_free_values: Arc<FxHashSet<Id>>,
413        comments: Option<&dyn Comments>,
414        source: Option<ResolvedVc<Box<dyn Source>>>,
415    ) -> Self {
416        Self {
417            unresolved_mark,
418            top_level_mark,
419            imports: module.map_or(ImportMap::default(), |m| {
420                ImportMap::analyze(m, source, comments)
421            }),
422            force_free_values,
423        }
424    }
425
426    pub fn is_esm(&self, specified_type: SpecifiedModuleType) -> bool {
427        self.imports.is_esm(specified_type)
428    }
429
430    fn eval_prop_name(&self, prop: &PropName) -> JsValue {
431        match prop {
432            PropName::Ident(ident) => ident.sym.clone().into(),
433            PropName::Str(str) => str.value.clone().to_atom_lossy().into_owned().into(),
434            PropName::Num(num) => num.value.into(),
435            PropName::Computed(ComputedPropName { expr, .. }) => self.eval(expr),
436            PropName::BigInt(bigint) => (*bigint.value.clone()).into(),
437        }
438    }
439
440    fn eval_member_prop(&self, prop: &MemberProp) -> Option<JsValue> {
441        match prop {
442            MemberProp::Ident(ident) => Some(ident.sym.clone().into()),
443            MemberProp::Computed(ComputedPropName { expr, .. }) => Some(self.eval(expr)),
444            MemberProp::PrivateName(_) => None,
445        }
446    }
447
448    fn eval_tpl(&self, e: &Tpl, raw: bool) -> JsValue {
449        debug_assert!(e.quasis.len() == e.exprs.len() + 1);
450
451        let mut values = vec![];
452
453        for idx in 0..(e.quasis.len() + e.exprs.len()) {
454            if idx.is_multiple_of(2) {
455                let idx = idx / 2;
456                let e = &e.quasis[idx];
457                if raw {
458                    // Ignore empty strings quasis, happens frequently with e.g. after the
459                    // placeholder in `something${v}`.
460                    if !e.raw.is_empty() {
461                        values.push(JsValue::from(e.raw.clone()));
462                    }
463                } else {
464                    match &e.cooked {
465                        Some(v) => {
466                            if !v.is_empty() {
467                                values.push(JsValue::from(v.clone().to_atom_lossy().into_owned()));
468                            }
469                        }
470                        // This is actually unreachable
471                        None => return JsValue::unknown_empty(true, ""),
472                    }
473                }
474            } else {
475                let idx = idx / 2;
476                let e = &e.exprs[idx];
477
478                values.push(self.eval(e));
479            }
480        }
481
482        match values.len() {
483            0 => JsValue::Constant(ConstantValue::Str(rcstr!("").into())),
484            1 => values.into_iter().next().unwrap(),
485            _ => JsValue::concat(values),
486        }
487    }
488
489    fn eval_ident(&self, i: &Ident) -> JsValue {
490        let id = i.to_id();
491        if let Some(imported) = self.imports.get_import(&id) {
492            return imported;
493        }
494        if is_unresolved(i, self.unresolved_mark) || self.force_free_values.contains(&id) {
495            // These are special globals that we shouldn't consider to be free variables and we can
496            // model their values mostly useful for truthy/falsy checks.
497            match i.sym.as_str() {
498                "undefined" => JsValue::Constant(ConstantValue::Undefined),
499                "NaN" => JsValue::Constant(ConstantValue::Num(ConstantNumber(f64::NAN))),
500                "Infinity" => JsValue::Constant(ConstantValue::Num(ConstantNumber(f64::INFINITY))),
501                _ => JsValue::FreeVar(i.sym.clone()),
502            }
503        } else {
504            JsValue::Variable(id)
505        }
506    }
507
508    pub fn eval(&self, e: &Expr) -> JsValue {
509        debug_assert!(
510            GLOBALS.is_set(),
511            "Eval requires globals from its parsed result"
512        );
513        match e {
514            Expr::Paren(e) => self.eval(&e.expr),
515            Expr::Lit(e) => JsValue::Constant(e.clone().into()),
516            Expr::Ident(i) => self.eval_ident(i),
517
518            Expr::Unary(UnaryExpr {
519                op: op!("void"),
520                // Only treat literals as constant undefined, allowing arbitrary values inside here
521                // would mean that they can have sideeffects, and `JsValue::Constant` can't model
522                // that.
523                arg: box Expr::Lit(_),
524                ..
525            }) => JsValue::Constant(ConstantValue::Undefined),
526
527            Expr::Unary(UnaryExpr {
528                op: op!("!"), arg, ..
529            }) => {
530                let arg = self.eval(arg);
531
532                JsValue::logical_not(Box::new(arg))
533            }
534
535            Expr::Unary(UnaryExpr {
536                op: op!("typeof"),
537                arg,
538                ..
539            }) => {
540                let arg = self.eval(arg);
541
542                JsValue::type_of(Box::new(arg))
543            }
544
545            Expr::Bin(BinExpr {
546                op: op!(bin, "+"),
547                left,
548                right,
549                ..
550            }) => {
551                let l = self.eval(left);
552                let r = self.eval(right);
553
554                match (l, r) {
555                    (JsValue::Add(c, l), r) => JsValue::Add(
556                        c + r.total_nodes(),
557                        l.into_iter().chain(iter::once(r)).collect(),
558                    ),
559                    (l, r) => JsValue::add(vec![l, r]),
560                }
561            }
562
563            Expr::Bin(BinExpr {
564                op: op!("&&"),
565                left,
566                right,
567                ..
568            }) => JsValue::logical_and(vec![self.eval(left), self.eval(right)]),
569
570            Expr::Bin(BinExpr {
571                op: op!("||"),
572                left,
573                right,
574                ..
575            }) => JsValue::logical_or(vec![self.eval(left), self.eval(right)]),
576
577            Expr::Bin(BinExpr {
578                op: op!("??"),
579                left,
580                right,
581                ..
582            }) => JsValue::nullish_coalescing(vec![self.eval(left), self.eval(right)]),
583
584            Expr::Bin(BinExpr {
585                op: op!("=="),
586                left,
587                right,
588                ..
589            }) => JsValue::equal(Box::new(self.eval(left)), Box::new(self.eval(right))),
590
591            Expr::Bin(BinExpr {
592                op: op!("!="),
593                left,
594                right,
595                ..
596            }) => JsValue::not_equal(Box::new(self.eval(left)), Box::new(self.eval(right))),
597
598            Expr::Bin(BinExpr {
599                op: op!("==="),
600                left,
601                right,
602                ..
603            }) => JsValue::strict_equal(Box::new(self.eval(left)), Box::new(self.eval(right))),
604
605            Expr::Bin(BinExpr {
606                op: op!("!=="),
607                left,
608                right,
609                ..
610            }) => JsValue::strict_not_equal(Box::new(self.eval(left)), Box::new(self.eval(right))),
611
612            &Expr::Cond(CondExpr {
613                box ref cons,
614                box ref alt,
615                box ref test,
616                ..
617            }) => {
618                let test = self.eval(test);
619                if let Some(truthy) = test.is_truthy() {
620                    if truthy {
621                        self.eval(cons)
622                    } else {
623                        self.eval(alt)
624                    }
625                } else {
626                    JsValue::tenary(
627                        Box::new(test),
628                        Box::new(self.eval(cons)),
629                        Box::new(self.eval(alt)),
630                    )
631                }
632            }
633
634            Expr::Tpl(e) => self.eval_tpl(e, false),
635
636            Expr::TaggedTpl(TaggedTpl {
637                tag:
638                    box Expr::Member(MemberExpr {
639                        obj: box Expr::Ident(tag_obj),
640                        prop: MemberProp::Ident(tag_prop),
641                        ..
642                    }),
643                tpl,
644                ..
645            }) => {
646                if &*tag_obj.sym == "String"
647                    && &*tag_prop.sym == "raw"
648                    && is_unresolved(tag_obj, self.unresolved_mark)
649                {
650                    self.eval_tpl(tpl, true)
651                } else {
652                    JsValue::unknown_empty(true, "tagged template literal is not supported yet")
653                }
654            }
655
656            Expr::Fn(expr) => {
657                if let Some(ident) = &expr.ident {
658                    JsValue::Variable(ident.to_id())
659                } else {
660                    JsValue::Variable((
661                        format!("*anonymous function {}*", expr.function.span.lo.0).into(),
662                        SyntaxContext::empty(),
663                    ))
664                }
665            }
666            Expr::Arrow(expr) => JsValue::Variable((
667                format!("*arrow function {}*", expr.span.lo.0).into(),
668                SyntaxContext::empty(),
669            )),
670
671            Expr::Await(AwaitExpr { arg, .. }) => JsValue::awaited(Box::new(self.eval(arg))),
672
673            Expr::Seq(e) => {
674                let mut seq = e.exprs.iter().map(|e| self.eval(e)).peekable();
675                let mut side_effects = false;
676                let mut last = seq.next().unwrap();
677                for e in seq {
678                    side_effects |= last.has_side_effects();
679                    last = e;
680                }
681                if side_effects {
682                    last.make_unknown(true, "sequence with side effects");
683                }
684                last
685            }
686
687            Expr::Member(MemberExpr {
688                obj,
689                prop: MemberProp::Ident(prop),
690                ..
691            }) => {
692                let obj = self.eval(obj);
693                JsValue::member(Box::new(obj), Box::new(prop.sym.clone().into()))
694            }
695
696            Expr::Member(MemberExpr {
697                obj,
698                prop: MemberProp::Computed(computed),
699                ..
700            }) => {
701                let obj = self.eval(obj);
702                let prop = self.eval(&computed.expr);
703                JsValue::member(Box::new(obj), Box::new(prop))
704            }
705
706            Expr::New(NewExpr {
707                callee: box callee,
708                args,
709                ..
710            }) => {
711                // We currently do not handle spreads.
712                if args.iter().flatten().any(|arg| arg.spread.is_some()) {
713                    return JsValue::unknown_empty(true, "spread in new calls is not supported");
714                }
715
716                let args: Vec<_> = args
717                    .iter()
718                    .flatten()
719                    .map(|arg| self.eval(&arg.expr))
720                    .collect();
721                let callee = Box::new(self.eval(callee));
722
723                JsValue::new(callee, args)
724            }
725
726            Expr::Call(CallExpr {
727                callee: Callee::Expr(box callee),
728                args,
729                ..
730            }) => {
731                // We currently do not handle spreads.
732                if args.iter().any(|arg| arg.spread.is_some()) {
733                    return JsValue::unknown_empty(
734                        true,
735                        "spread in function calls is not supported",
736                    );
737                }
738
739                let args = args.iter().map(|arg| self.eval(&arg.expr)).collect();
740                if let Expr::Member(MemberExpr { obj, prop, .. }) = unparen(callee) {
741                    let obj = Box::new(self.eval(obj));
742                    let prop = Box::new(match prop {
743                        MemberProp::Ident(i) => i.sym.clone().into(),
744                        MemberProp::PrivateName(_) => {
745                            return JsValue::unknown_empty(
746                                false,
747                                "private names in function calls is not supported",
748                            );
749                        }
750                        MemberProp::Computed(ComputedPropName { expr, .. }) => self.eval(expr),
751                    });
752                    JsValue::member_call(obj, prop, args)
753                } else {
754                    let callee = Box::new(self.eval(callee));
755
756                    JsValue::call(callee, args)
757                }
758            }
759
760            Expr::Call(CallExpr {
761                callee: Callee::Super(_),
762                args,
763                ..
764            }) => {
765                // We currently do not handle spreads.
766                if args.iter().any(|arg| arg.spread.is_some()) {
767                    return JsValue::unknown_empty(
768                        true,
769                        "spread in function calls is not supported",
770                    );
771                }
772
773                let args = args.iter().map(|arg| self.eval(&arg.expr)).collect();
774
775                JsValue::super_call(args)
776            }
777
778            Expr::Call(CallExpr {
779                callee: Callee::Import(_),
780                args,
781                ..
782            }) => {
783                // We currently do not handle spreads.
784                if args.iter().any(|arg| arg.spread.is_some()) {
785                    return JsValue::unknown_empty(true, "spread in import() is not supported");
786                }
787                let args = args.iter().map(|arg| self.eval(&arg.expr)).collect();
788
789                let callee = Box::new(JsValue::FreeVar(atom!("import")));
790
791                JsValue::call(callee, args)
792            }
793
794            Expr::Array(arr) => {
795                if arr.elems.iter().flatten().any(|v| v.spread.is_some()) {
796                    return JsValue::unknown_empty(true, "spread is not supported");
797                }
798
799                let arr = arr
800                    .elems
801                    .iter()
802                    .map(|e| match e {
803                        Some(e) => self.eval(&e.expr),
804                        _ => JsValue::Constant(ConstantValue::Undefined),
805                    })
806                    .collect();
807                JsValue::array(arr)
808            }
809
810            Expr::Object(obj) => JsValue::object(
811                obj.props
812                    .iter()
813                    .map(|prop| match prop {
814                        PropOrSpread::Spread(SpreadElement { expr, .. }) => {
815                            ObjectPart::Spread(self.eval(expr))
816                        }
817                        PropOrSpread::Prop(box Prop::KeyValue(KeyValueProp { key, box value })) => {
818                            ObjectPart::KeyValue(self.eval_prop_name(key), self.eval(value))
819                        }
820                        PropOrSpread::Prop(box Prop::Shorthand(ident)) => ObjectPart::KeyValue(
821                            ident.sym.clone().into(),
822                            self.eval(&Expr::Ident(ident.clone())),
823                        ),
824                        _ => ObjectPart::Spread(JsValue::unknown_empty(
825                            true,
826                            "unsupported object part",
827                        )),
828                    })
829                    .collect(),
830            ),
831
832            Expr::MetaProp(MetaPropExpr {
833                kind: MetaPropKind::ImportMeta,
834                ..
835            }) => JsValue::WellKnownObject(WellKnownObjectKind::ImportMeta),
836
837            Expr::Assign(AssignExpr { op, .. }) => match op {
838                // TODO: `self.eval(right)` would be the value, but we need to handle the side
839                // effect of that expression
840                AssignOp::Assign => JsValue::unknown_empty(true, "assignment expression"),
841                _ => JsValue::unknown_empty(true, "compound assignment expression"),
842            },
843
844            _ => JsValue::unknown_empty(true, "unsupported expression"),
845        }
846    }
847
848    pub fn eval_single_expr_lit(expr_lit: RcStr) -> Result<JsValue> {
849        let cm = Lrc::new(SourceMap::default());
850
851        let js_value = try_with_handler(cm, Default::default(), |_| {
852            GLOBALS.set(&Default::default(), || {
853                let expr = parse_single_expr_lit(expr_lit);
854                let eval_context = EvalContext::new(
855                    None,
856                    Mark::new(),
857                    Mark::new(),
858                    Default::default(),
859                    None,
860                    None,
861                );
862
863                Ok(eval_context.eval(&expr))
864            })
865        })
866        .map_err(|e| e.to_pretty_error())?;
867
868        Ok(js_value)
869    }
870}
871
872enum EarlyReturn {
873    Always {
874        prev_effects: Vec<Effect>,
875        start_ast_path: Vec<AstParentKind>,
876    },
877    Conditional {
878        prev_effects: Vec<Effect>,
879        start_ast_path: Vec<AstParentKind>,
880
881        condition: Box<JsValue>,
882        then: Option<Box<EffectsBlock>>,
883        r#else: Option<Box<EffectsBlock>>,
884        /// The ast path to the condition.
885        condition_ast_path: Vec<AstParentKind>,
886        span: Span,
887
888        early_return_condition_value: bool,
889    },
890}
891
892pub fn as_parent_path_skip(
893    ast_path: &AstNodePath<AstParentNodeRef<'_>>,
894    skip: usize,
895) -> Vec<AstParentKind> {
896    ast_path
897        .iter()
898        .take(ast_path.len() - skip)
899        .map(|n| n.kind())
900        .collect()
901}
902
903struct Analyzer<'a> {
904    analyze_mode: AnalyzeMode,
905
906    data: &'a mut VarGraph,
907    state: analyzer_state::AnalyzerState,
908
909    effects: Vec<Effect>,
910    /// Effects collected from hoisted declarations. See https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
911    /// Tracked separately so we can preserve effects from hoisted declarations even when we don't
912    /// collect effects from the declaring context.
913    hoisted_effects: Vec<Effect>,
914
915    eval_context: &'a EvalContext,
916}
917
918trait FunctionLike {
919    fn is_async(&self) -> bool {
920        false
921    }
922    fn is_generator(&self) -> bool {
923        false
924    }
925    fn span(&self) -> Span;
926    fn binds_this(&self) -> bool {
927        true
928    }
929}
930
931impl FunctionLike for Function {
932    fn is_async(&self) -> bool {
933        self.is_async
934    }
935    fn is_generator(&self) -> bool {
936        self.is_generator
937    }
938    fn span(&self) -> Span {
939        self.span
940    }
941}
942impl FunctionLike for ArrowExpr {
943    fn is_async(&self) -> bool {
944        self.is_async
945    }
946    fn is_generator(&self) -> bool {
947        self.is_generator
948    }
949    fn span(&self) -> Span {
950        self.span
951    }
952    fn binds_this(&self) -> bool {
953        false
954    }
955}
956
957impl FunctionLike for Constructor {
958    fn span(&self) -> Span {
959        self.span
960    }
961}
962impl FunctionLike for GetterProp {
963    fn span(&self) -> Span {
964        self.span
965    }
966}
967impl FunctionLike for SetterProp {
968    fn span(&self) -> Span {
969        self.span
970    }
971}
972
973#[derive(PartialEq, Eq, Debug, Copy, Clone)]
974enum LexicalContext {
975    // In the root of a function scope
976    Function { id: u32, binds_this: bool },
977    // A placeholder for identify anonymous blocks
978    // If we have Block->Block then we are in an anonymous block
979    // If we have Function->Block or ControlFlow->Block then we are just in a function root
980    Block,
981    // In some kind of control flow
982    ControlFlow { is_try: bool },
983
984    // Class bodies do rebind `this` and are in many ways like a function
985    ClassBody,
986}
987
988mod analyzer_state {
989    use super::*;
990
991    /// Contains fields of `Analyzer` that should only be modified using helper methods. These are
992    /// intentionally private to the rest of the `Analyzer` implementation.
993    #[derive(Default)]
994    pub struct AnalyzerState {
995        pat_value: Option<JsValue>,
996        /// Return values of the current function.
997        ///
998        /// This is configured to [Some] by function handlers and filled by the
999        /// return statement handler.
1000        cur_fn_return_values: Option<Vec<JsValue>>,
1001        /// Stack of early returns for control flow analysis.
1002        early_return_stack: Vec<EarlyReturn>,
1003        lexical_stack: Vec<LexicalContext>,
1004        var_decl_kind: Option<VarDeclKind>,
1005
1006        cur_top_level_decl_name: Option<Id>,
1007    }
1008
1009    impl AnalyzerState {
1010        /// Returns the identifier of the current top level declaration.
1011        pub(super) fn cur_top_level_decl_name(&self) -> &Option<Id> {
1012            &self.cur_top_level_decl_name
1013        }
1014    }
1015
1016    impl Analyzer<'_> {
1017        /// Returns true if we are in a function. False if we are in the root scope.
1018        pub(super) fn is_in_fn(&self) -> bool {
1019            self.state
1020                .lexical_stack
1021                .iter()
1022                .any(|b| matches!(b, LexicalContext::Function { .. }))
1023        }
1024
1025        pub(super) fn is_in_try(&self) -> bool {
1026            self.state
1027                .lexical_stack
1028                .iter()
1029                .rev()
1030                .take_while(|b| !matches!(b, LexicalContext::Function { .. }))
1031                .any(|b| *b == LexicalContext::ControlFlow { is_try: true })
1032        }
1033
1034        /// Returns true if we are currently in a block scope that isn't at the root of a function
1035        /// or a module.
1036        pub(super) fn is_in_nested_block_scope(&self) -> bool {
1037            match &self.state.lexical_stack[self.state.lexical_stack.len().saturating_sub(2)..] {
1038                [LexicalContext::Block]
1039                | [LexicalContext::Function { .. }, LexicalContext::Block] => false,
1040                [] => {
1041                    unreachable!()
1042                }
1043
1044                _ => true,
1045            }
1046        }
1047
1048        pub(super) fn cur_lexical_context(&self) -> LexicalContext {
1049            *self.state.lexical_stack.last().unwrap()
1050        }
1051
1052        /// Returns the identifier of the current function.
1053        /// must be called only if `is_in_fn` is true
1054        pub(super) fn cur_fn_ident(&self) -> u32 {
1055            *self
1056                .state
1057                .lexical_stack
1058                .iter()
1059                .rev()
1060                .filter_map(|b| {
1061                    if let LexicalContext::Function { id, .. } = b {
1062                        Some(id)
1063                    } else {
1064                        None
1065                    }
1066                })
1067                .next()
1068                .expect("not in a function")
1069        }
1070
1071        /// Returns true if `this` is bound in any active scope
1072        pub(super) fn is_this_bound(&self) -> bool {
1073            self.state.lexical_stack.iter().rev().any(|b| {
1074                matches!(
1075                    b,
1076                    LexicalContext::Function {
1077                        id: _,
1078                        binds_this: true
1079                    } | LexicalContext::ClassBody
1080                )
1081            })
1082        }
1083
1084        /// Adds a return value to the current function.
1085        /// Panics if we are not in a function scope
1086        pub(super) fn add_return_value(&mut self, value: JsValue) {
1087            self.state
1088                .cur_fn_return_values
1089                .as_mut()
1090                .expect("not in a function")
1091                .push(value);
1092        }
1093
1094        /// The RHS (or some part of it) of an pattern or assignment (e.g. `PatAssignTarget`,
1095        /// `SimpleAssignTarget`, function arguments, etc.), read by the individual parts of LHS
1096        /// (target).
1097        ///
1098        /// Consumes the value, setting it to `None`, and returning the previous value. This avoids
1099        /// extra clones.
1100        pub(super) fn take_pat_value(&mut self) -> Option<JsValue> {
1101            self.state.pat_value.take()
1102        }
1103
1104        // Runs `func` (usually something that visits children) with the given
1105        // [`Analyzer::take_pat_value`], restoring the value back to the previous value (usually
1106        // `None`) afterwards.
1107        pub(super) fn with_pat_value<T>(
1108            &mut self,
1109            value: Option<JsValue>,
1110            func: impl FnOnce(&mut Self) -> T,
1111        ) -> T {
1112            let prev_value = replace(&mut self.state.pat_value, value);
1113            let out = func(self);
1114            self.state.pat_value = prev_value;
1115            out
1116        }
1117
1118        /// Runs `func` with the given variable declaration kind, restoring the previous kind
1119        /// afterwards.
1120        pub(super) fn with_decl_kind<T>(
1121            &mut self,
1122            kind: Option<VarDeclKind>,
1123            func: impl FnOnce(&mut Self) -> T,
1124        ) -> T {
1125            let prev_kind = replace(&mut self.state.var_decl_kind, kind);
1126            let out = func(self);
1127            self.state.var_decl_kind = prev_kind;
1128            out
1129        }
1130
1131        /// Returns the current variable declaration kind.
1132        pub(super) fn var_decl_kind(&self) -> Option<VarDeclKind> {
1133            self.state.var_decl_kind
1134        }
1135
1136        /// Runs `func` with the current function identifier and return values initialized for the
1137        /// block.
1138        pub(super) fn enter_fn(
1139            &mut self,
1140            function: &impl FunctionLike,
1141            visitor: impl FnOnce(&mut Self),
1142        ) -> JsValue {
1143            let fn_id = function.span().lo.0;
1144            let prev_return_values = self.state.cur_fn_return_values.replace(vec![]);
1145
1146            self.with_block(
1147                LexicalContext::Function {
1148                    id: fn_id,
1149                    binds_this: function.binds_this(),
1150                },
1151                |this| visitor(this),
1152            );
1153            let return_values = self.state.cur_fn_return_values.take().unwrap();
1154            self.state.cur_fn_return_values = prev_return_values;
1155
1156            JsValue::function(
1157                fn_id,
1158                function.is_async(),
1159                function.is_generator(),
1160                match return_values.len() {
1161                    0 => JsValue::Constant(ConstantValue::Undefined),
1162                    1 => return_values.into_iter().next().unwrap(),
1163                    _ => JsValue::alternatives(return_values),
1164                },
1165            )
1166        }
1167
1168        /// Helper to access the early_return_stack mutably (for push operations)
1169        pub(super) fn early_return_stack_mut(&mut self) -> &mut Vec<EarlyReturn> {
1170            &mut self.state.early_return_stack
1171        }
1172
1173        /// Records an unconditional early return (return, throw, or finally block that always
1174        /// returns). Takes ownership of current effects and pushes them onto the early return
1175        /// stack.
1176        pub(super) fn add_early_return_always(
1177            &mut self,
1178            ast_path: &AstNodePath<AstParentNodeRef<'_>>,
1179        ) {
1180            let early_return = EarlyReturn::Always {
1181                prev_effects: take(&mut self.effects),
1182                start_ast_path: as_parent_path(ast_path),
1183            };
1184            self.early_return_stack_mut().push(early_return);
1185        }
1186
1187        /// Runs `func` with a fresh early return stack, restoring the previous stack afterwards.
1188        /// Returns the result of `func` and whether the block always returns (from
1189        /// `end_early_return_block`).
1190        pub(super) fn enter_control_flow<T>(
1191            &mut self,
1192            func: impl FnOnce(&mut Self) -> T,
1193        ) -> (T, bool) {
1194            self.enter_block(LexicalContext::ControlFlow { is_try: false }, |this| {
1195                func(this)
1196            })
1197        }
1198        /// Runs `func` with a fresh early return stack, restoring the previous stack afterwards.
1199        /// Returns the result of `func` and whether the block always returns (from
1200        /// `end_early_return_block`).
1201        pub(super) fn enter_try<T>(&mut self, func: impl FnOnce(&mut Self) -> T) -> (T, bool) {
1202            self.enter_block(LexicalContext::ControlFlow { is_try: true }, |this| {
1203                func(this)
1204            })
1205        }
1206
1207        /// Runs `func` with a fresh early return stack, restoring the previous stack afterwards.
1208        /// Returns the result of `func` and whether the block always returns (from
1209        /// `end_early_return_block`).
1210        pub(super) fn enter_block<T>(
1211            &mut self,
1212            block_kind: LexicalContext,
1213            func: impl FnOnce(&mut Self) -> T,
1214        ) -> (T, bool) {
1215            let prev_early_return_stack = take(&mut self.state.early_return_stack);
1216            let result = self.with_block(block_kind, func);
1217            let always_returns = self.end_early_return_block();
1218            self.state.early_return_stack = prev_early_return_stack;
1219            (result, always_returns)
1220        }
1221
1222        /// Pushes a block onto the stack without performing early return logic.
1223        pub(super) fn with_block<T>(
1224            &mut self,
1225            block_kind: LexicalContext,
1226            func: impl FnOnce(&mut Self) -> T,
1227        ) -> T {
1228            self.state.lexical_stack.push(block_kind);
1229            let result = func(self);
1230            let old = self.state.lexical_stack.pop();
1231            debug_assert_eq!(old, Some(block_kind));
1232            result
1233        }
1234
1235        /// Ends a conditional block. All early returns are integrated into the
1236        /// effects. Returns true if the whole block always early returns.
1237        fn end_early_return_block(&mut self) -> bool {
1238            let mut always_returns = false;
1239            while let Some(early_return) = self.state.early_return_stack.pop() {
1240                match early_return {
1241                    EarlyReturn::Always {
1242                        prev_effects,
1243                        start_ast_path,
1244                    } => {
1245                        self.effects = prev_effects;
1246                        if self.analyze_mode.is_code_gen() {
1247                            self.effects.push(Effect::Unreachable { start_ast_path });
1248                        }
1249                        always_returns = true;
1250                    }
1251                    EarlyReturn::Conditional {
1252                        prev_effects,
1253                        start_ast_path,
1254                        condition,
1255                        then,
1256                        r#else,
1257                        condition_ast_path,
1258                        span,
1259                        early_return_condition_value,
1260                    } => {
1261                        let block = Box::new(EffectsBlock {
1262                            effects: take(&mut self.effects),
1263                            range: AstPathRange::StartAfter(start_ast_path),
1264                        });
1265                        self.effects = prev_effects;
1266                        let kind = match (then, r#else, early_return_condition_value) {
1267                            (None, None, false) => ConditionalKind::If { then: block },
1268                            (None, None, true) => ConditionalKind::IfElseMultiple {
1269                                then: vec![block],
1270                                r#else: vec![],
1271                            },
1272                            (Some(then), None, false) => ConditionalKind::IfElseMultiple {
1273                                then: vec![then, block],
1274                                r#else: vec![],
1275                            },
1276                            (Some(then), None, true) => ConditionalKind::IfElse {
1277                                then,
1278                                r#else: block,
1279                            },
1280                            (Some(then), Some(r#else), false) => ConditionalKind::IfElseMultiple {
1281                                then: vec![then, block],
1282                                r#else: vec![r#else],
1283                            },
1284                            (Some(then), Some(r#else), true) => ConditionalKind::IfElseMultiple {
1285                                then: vec![then],
1286                                r#else: vec![r#else, block],
1287                            },
1288                            (None, Some(r#else), false) => ConditionalKind::IfElse {
1289                                then: block,
1290                                r#else,
1291                            },
1292                            (None, Some(r#else), true) => ConditionalKind::IfElseMultiple {
1293                                then: vec![],
1294                                r#else: vec![r#else, block],
1295                            },
1296                        };
1297                        self.effects.push(Effect::Conditional {
1298                            condition,
1299                            kind: Box::new(kind),
1300                            ast_path: condition_ast_path,
1301                            span,
1302                        })
1303                    }
1304                }
1305            }
1306            always_returns
1307        }
1308
1309        /// Runs `visitor` with the current top level declaration identifier
1310        pub(super) fn enter_top_level_decl<T>(
1311            &mut self,
1312            name: &Ident,
1313            visitor: impl FnOnce(&mut Self) -> T,
1314        ) -> T {
1315            let is_top_level_fn = self.state.cur_top_level_decl_name.is_none();
1316            if is_top_level_fn {
1317                self.state.cur_top_level_decl_name = Some(name.to_id());
1318            }
1319            let result = visitor(self);
1320            if is_top_level_fn {
1321                self.state.cur_top_level_decl_name = None;
1322            }
1323            result
1324        }
1325    }
1326}
1327
1328pub fn as_parent_path(ast_path: &AstNodePath<AstParentNodeRef<'_>>) -> Vec<AstParentKind> {
1329    ast_path.iter().map(|n| n.kind()).collect()
1330}
1331
1332pub fn as_parent_path_with(
1333    ast_path: &AstNodePath<AstParentNodeRef<'_>>,
1334    additional: AstParentKind,
1335) -> Vec<AstParentKind> {
1336    ast_path
1337        .iter()
1338        .map(|n| n.kind())
1339        .chain([additional])
1340        .collect()
1341}
1342
1343enum CallOrNewExpr<'ast> {
1344    Call(&'ast CallExpr),
1345    New(&'ast NewExpr),
1346}
1347impl CallOrNewExpr<'_> {
1348    fn as_call(&self) -> Option<&CallExpr> {
1349        match *self {
1350            CallOrNewExpr::Call(n) => Some(n),
1351            CallOrNewExpr::New(_) => None,
1352        }
1353    }
1354    fn as_new(&self) -> Option<&NewExpr> {
1355        match *self {
1356            CallOrNewExpr::Call(_) => None,
1357            CallOrNewExpr::New(n) => Some(n),
1358        }
1359    }
1360}
1361
1362impl Analyzer<'_> {
1363    fn add_value(&mut self, id: Id, value: JsValue) {
1364        if is_unresolved_id(&id, self.eval_context.unresolved_mark) {
1365            self.data.free_var_ids.insert(id.0.clone(), id.clone());
1366        }
1367
1368        let kind = if self.is_in_fn() {
1369            AssignmentScope::Function
1370        } else {
1371            AssignmentScope::ModuleEval
1372        };
1373        if let Some(prev) = self.data.values.get_mut(&id) {
1374            prev.add_alt(value, kind);
1375        } else {
1376            self.data.values.insert(id, VarMeta::new(value, kind));
1377        }
1378        // TODO(kdy1): We may need to report an error for this.
1379        // Variables declared with `var` are hoisted, but using undefined as its
1380        // value does not seem like a good idea.
1381    }
1382
1383    fn add_value_from_expr(&mut self, id: Id, value: &Expr) {
1384        let value = self.eval_context.eval(value);
1385
1386        self.add_value(id, value);
1387    }
1388
1389    fn add_effect(&mut self, effect: Effect) {
1390        self.effects.push(effect);
1391    }
1392
1393    fn check_iife<'ast: 'r, 'r>(
1394        &mut self,
1395        n: &'ast CallExpr,
1396        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1397    ) -> bool {
1398        fn unparen<'ast: 'r, 'r, T>(
1399            expr: &'ast Expr,
1400            ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1401            f: impl FnOnce(&'ast Expr, &mut AstNodePath<AstParentNodeRef<'r>>) -> T,
1402        ) -> T {
1403            if let Some(inner_expr) = expr.as_paren() {
1404                let mut ast_path =
1405                    ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Paren));
1406                let mut ast_path = ast_path.with_guard(AstParentNodeRef::ParenExpr(
1407                    inner_expr,
1408                    ParenExprField::Expr,
1409                ));
1410                unparen(&inner_expr.expr, &mut ast_path, f)
1411            } else {
1412                f(expr, ast_path)
1413            }
1414        }
1415
1416        if n.args.iter().any(|arg| arg.spread.is_some()) {
1417            return false;
1418        }
1419
1420        let Some(expr) = n.callee.as_expr() else {
1421            return false;
1422        };
1423
1424        let fn_expr = {
1425            let mut ast_path =
1426                ast_path.with_guard(AstParentNodeRef::CallExpr(n, CallExprField::Callee));
1427            let mut ast_path =
1428                ast_path.with_guard(AstParentNodeRef::Callee(&n.callee, CalleeField::Expr));
1429            unparen(expr, &mut ast_path, |expr, ast_path| match expr {
1430                Expr::Fn(fn_expr @ FnExpr { function, ident }) => {
1431                    let mut ast_path =
1432                        ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Fn));
1433                    {
1434                        let mut ast_path = ast_path
1435                            .with_guard(AstParentNodeRef::FnExpr(fn_expr, FnExprField::Ident));
1436                        self.visit_opt_ident(ident, &mut ast_path);
1437
1438                        // We cannot analyze recursive IIFE
1439                        if let Some(ident) = ident
1440                            && contains_ident_ref(&function.body, ident)
1441                        {
1442                            return false;
1443                        }
1444                    }
1445
1446                    {
1447                        let mut ast_path = ast_path
1448                            .with_guard(AstParentNodeRef::FnExpr(fn_expr, FnExprField::Function));
1449                        // We don't handle the value of the function here, though we could to better
1450                        // model the value of this 'call'
1451                        self.enter_fn(&**function, |this| {
1452                            this.handle_iife_function(function, &mut ast_path, &n.args);
1453                        });
1454                    }
1455
1456                    true
1457                }
1458
1459                Expr::Arrow(arrow_expr) => {
1460                    let mut ast_path =
1461                        ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Arrow));
1462                    let args = &n.args;
1463                    // We don't handle the value of the function here, though we could to better
1464                    // model the value of this 'call'
1465                    self.enter_fn(arrow_expr, |this| {
1466                        this.handle_iife_arrow(arrow_expr, args, &mut ast_path);
1467                    });
1468                    true
1469                }
1470                _ => false,
1471            })
1472        };
1473
1474        if !fn_expr {
1475            return false;
1476        }
1477
1478        let mut ast_path = ast_path.with_guard(AstParentNodeRef::CallExpr(
1479            n,
1480            CallExprField::Args(usize::MAX),
1481        ));
1482
1483        self.visit_expr_or_spreads(&n.args, &mut ast_path);
1484
1485        true
1486    }
1487
1488    fn handle_iife_arrow<'ast: 'r, 'r>(
1489        &mut self,
1490        arrow_expr: &'ast ArrowExpr,
1491        args: &[ExprOrSpread],
1492        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1493    ) {
1494        let ArrowExpr {
1495            params,
1496            body,
1497            is_async: _,
1498            is_generator: _,
1499            return_type,
1500            span: _,
1501            type_params,
1502            ctxt: _,
1503        } = arrow_expr;
1504        let mut iter = args.iter();
1505        for (i, param) in params.iter().enumerate() {
1506            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
1507                arrow_expr,
1508                ArrowExprField::Params(i),
1509            ));
1510            let pat_value = iter.next().map(|arg| self.eval_context.eval(&arg.expr));
1511            self.with_pat_value(pat_value, |this| this.visit_pat(param, &mut ast_path));
1512        }
1513        {
1514            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
1515                arrow_expr,
1516                ArrowExprField::Body,
1517            ));
1518            self.visit_block_stmt_or_expr(body, &mut ast_path);
1519        }
1520
1521        {
1522            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
1523                arrow_expr,
1524                ArrowExprField::ReturnType,
1525            ));
1526            self.visit_opt_ts_type_ann(return_type, &mut ast_path);
1527        }
1528
1529        {
1530            let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
1531                arrow_expr,
1532                ArrowExprField::TypeParams,
1533            ));
1534            self.visit_opt_ts_type_param_decl(type_params, &mut ast_path);
1535        }
1536    }
1537
1538    fn handle_iife_function<'ast: 'r, 'r>(
1539        &mut self,
1540        function: &'ast Function,
1541        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1542        args: &[ExprOrSpread],
1543    ) {
1544        let mut iter = args.iter();
1545        let Function {
1546            body,
1547            decorators,
1548            is_async: _,
1549            is_generator: _,
1550            params,
1551            return_type,
1552            span: _,
1553            type_params,
1554            ctxt: _,
1555        } = function;
1556        for (i, param) in params.iter().enumerate() {
1557            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1558                function,
1559                FunctionField::Params(i),
1560            ));
1561            if let Some(arg) = iter.next() {
1562                self.with_pat_value(Some(self.eval_context.eval(&arg.expr)), |this| {
1563                    this.visit_param(param, &mut ast_path)
1564                });
1565            } else {
1566                self.visit_param(param, &mut ast_path);
1567            }
1568        }
1569
1570        {
1571            let mut ast_path =
1572                ast_path.with_guard(AstParentNodeRef::Function(function, FunctionField::Body));
1573
1574            self.visit_opt_block_stmt(body, &mut ast_path);
1575        }
1576
1577        {
1578            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1579                function,
1580                FunctionField::Decorators(usize::MAX),
1581            ));
1582
1583            self.visit_decorators(decorators, &mut ast_path);
1584        }
1585
1586        {
1587            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1588                function,
1589                FunctionField::ReturnType,
1590            ));
1591
1592            self.visit_opt_ts_type_ann(return_type, &mut ast_path);
1593        }
1594
1595        {
1596            let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1597                function,
1598                FunctionField::TypeParams,
1599            ));
1600
1601            self.visit_opt_ts_type_param_decl(type_params, &mut ast_path);
1602        }
1603    }
1604
1605    fn check_call_expr_for_effects<'ast: 'r, 'n, 'r>(
1606        &mut self,
1607        callee: &'n Callee,
1608        args: impl Iterator<Item = &'ast ExprOrSpread>,
1609        span: Span,
1610        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1611        n: CallOrNewExpr<'ast>,
1612    ) {
1613        let new = n.as_new().is_some();
1614        let args = args
1615            .enumerate()
1616            .map(|(i, arg)| {
1617                let mut ast_path = ast_path.with_guard(match n {
1618                    CallOrNewExpr::Call(n) => AstParentNodeRef::CallExpr(n, CallExprField::Args(i)),
1619                    CallOrNewExpr::New(n) => AstParentNodeRef::NewExpr(n, NewExprField::Args(i)),
1620                });
1621                if arg.spread.is_none() {
1622                    let value = self.eval_context.eval(&arg.expr);
1623
1624                    let block_path = match &*arg.expr {
1625                        Expr::Fn(FnExpr { .. }) => {
1626                            let mut path = as_parent_path(&ast_path);
1627                            path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1628                            path.push(AstParentKind::Expr(ExprField::Fn));
1629                            path.push(AstParentKind::FnExpr(FnExprField::Function));
1630                            path.push(AstParentKind::Function(FunctionField::Body));
1631                            Some(path)
1632                        }
1633                        Expr::Arrow(ArrowExpr {
1634                            body: box BlockStmtOrExpr::BlockStmt(_),
1635                            ..
1636                        }) => {
1637                            let mut path = as_parent_path(&ast_path);
1638                            path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1639                            path.push(AstParentKind::Expr(ExprField::Arrow));
1640                            path.push(AstParentKind::ArrowExpr(ArrowExprField::Body));
1641                            path.push(AstParentKind::BlockStmtOrExpr(
1642                                BlockStmtOrExprField::BlockStmt,
1643                            ));
1644                            Some(path)
1645                        }
1646                        Expr::Arrow(ArrowExpr {
1647                            body: box BlockStmtOrExpr::Expr(_),
1648                            ..
1649                        }) => {
1650                            let mut path = as_parent_path(&ast_path);
1651                            path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1652                            path.push(AstParentKind::Expr(ExprField::Arrow));
1653                            path.push(AstParentKind::ArrowExpr(ArrowExprField::Body));
1654                            path.push(AstParentKind::BlockStmtOrExpr(BlockStmtOrExprField::Expr));
1655                            Some(path)
1656                        }
1657                        _ => None,
1658                    };
1659                    if let Some(path) = block_path {
1660                        let old_effects = take(&mut self.effects);
1661                        arg.visit_with_ast_path(self, &mut ast_path);
1662                        let effects = replace(&mut self.effects, old_effects);
1663                        EffectArg::Closure(
1664                            value,
1665                            Box::new(EffectsBlock {
1666                                effects,
1667                                range: AstPathRange::Exact(path),
1668                            }),
1669                        )
1670                    } else {
1671                        arg.visit_with_ast_path(self, &mut ast_path);
1672                        EffectArg::Value(value)
1673                    }
1674                } else {
1675                    arg.visit_with_ast_path(self, &mut ast_path);
1676                    EffectArg::Spread
1677                }
1678            })
1679            .collect();
1680
1681        match callee {
1682            Callee::Import(_) => {
1683                self.add_effect(Effect::Call {
1684                    func: Box::new(JsValue::FreeVar(atom!("import"))),
1685                    args,
1686                    ast_path: as_parent_path(ast_path),
1687                    span,
1688                    in_try: self.is_in_try(),
1689                    new,
1690                });
1691            }
1692            Callee::Expr(box expr) => {
1693                if let Expr::Member(MemberExpr { obj, prop, .. }) = unparen(expr) {
1694                    let obj_value = Box::new(self.eval_context.eval(obj));
1695                    let prop_value = match prop {
1696                        // TODO avoid clone
1697                        MemberProp::Ident(i) => Box::new(i.sym.clone().into()),
1698                        MemberProp::PrivateName(_) => Box::new(JsValue::unknown_empty(
1699                            false,
1700                            "private names in member expressions are not supported",
1701                        )),
1702                        MemberProp::Computed(ComputedPropName { expr, .. }) => {
1703                            Box::new(self.eval_context.eval(expr))
1704                        }
1705                    };
1706                    self.add_effect(Effect::MemberCall {
1707                        obj: obj_value,
1708                        prop: prop_value,
1709                        args,
1710                        ast_path: as_parent_path(ast_path),
1711                        span,
1712                        in_try: self.is_in_try(),
1713                        new,
1714                    });
1715                } else {
1716                    let fn_value = Box::new(self.eval_context.eval(expr));
1717                    self.add_effect(Effect::Call {
1718                        func: fn_value,
1719                        args,
1720                        ast_path: as_parent_path(ast_path),
1721                        span,
1722                        in_try: self.is_in_try(),
1723                        new,
1724                    });
1725                }
1726            }
1727            Callee::Super(_) => self.add_effect(Effect::Call {
1728                func: Box::new(
1729                    self.eval_context
1730                        // Unwrap because `new super(..)` isn't valid anyway
1731                        .eval(&Expr::Call(n.as_call().unwrap().clone())),
1732                ),
1733                args,
1734                ast_path: as_parent_path(ast_path),
1735                span,
1736                in_try: self.is_in_try(),
1737                new,
1738            }),
1739        }
1740    }
1741
1742    fn check_member_expr_for_effects<'ast: 'r, 'r>(
1743        &mut self,
1744        member_expr: &'ast MemberExpr,
1745        ast_path: &AstNodePath<AstParentNodeRef<'r>>,
1746    ) {
1747        if !self.analyze_mode.is_code_gen() {
1748            return;
1749        }
1750
1751        let obj_value = Box::new(self.eval_context.eval(&member_expr.obj));
1752        let prop_value = match &member_expr.prop {
1753            // TODO avoid clone
1754            MemberProp::Ident(i) => Box::new(i.sym.clone().into()),
1755            MemberProp::PrivateName(_) => {
1756                return;
1757            }
1758            MemberProp::Computed(ComputedPropName { expr, .. }) => {
1759                Box::new(self.eval_context.eval(expr))
1760            }
1761        };
1762        self.add_effect(Effect::Member {
1763            obj: obj_value,
1764            prop: prop_value,
1765            ast_path: as_parent_path(ast_path),
1766            span: member_expr.span(),
1767        });
1768    }
1769}
1770
1771impl VisitAstPath for Analyzer<'_> {
1772    fn visit_import_specifier<'ast: 'r, 'r>(
1773        &mut self,
1774        _import_specifier: &'ast ImportSpecifier,
1775        _ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1776    ) {
1777        // Skip these nodes entirely: We gather imports in a separate pass
1778    }
1779
1780    fn visit_assign_expr<'ast: 'r, 'r>(
1781        &mut self,
1782        n: &'ast AssignExpr,
1783        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1784    ) {
1785        // LHS
1786        {
1787            let mut ast_path =
1788                ast_path.with_guard(AstParentNodeRef::AssignExpr(n, AssignExprField::Left));
1789
1790            let pat_value = match (n.op, n.left.as_ident()) {
1791                (AssignOp::Assign, _) => self.eval_context.eval(&n.right),
1792                (AssignOp::AndAssign | AssignOp::OrAssign | AssignOp::NullishAssign, Some(_)) => {
1793                    // We can handle the right value as alternative to the existing value
1794                    self.eval_context.eval(&n.right)
1795                }
1796                (AssignOp::AddAssign, Some(key)) => {
1797                    let left = self.eval_context.eval(&Expr::Ident(key.clone().into()));
1798                    let right = self.eval_context.eval(&n.right);
1799                    JsValue::add(vec![left, right])
1800                }
1801                _ => JsValue::unknown_empty(true, "unsupported assign operation"),
1802            };
1803            self.with_pat_value(Some(pat_value), |this| {
1804                n.left.visit_children_with_ast_path(this, &mut ast_path)
1805            });
1806        }
1807
1808        // RHS
1809        {
1810            let mut ast_path =
1811                ast_path.with_guard(AstParentNodeRef::AssignExpr(n, AssignExprField::Right));
1812            self.visit_expr(&n.right, &mut ast_path);
1813        }
1814    }
1815
1816    fn visit_update_expr<'ast: 'r, 'r>(
1817        &mut self,
1818        n: &'ast UpdateExpr,
1819        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1820    ) {
1821        if let Some(key) = n.arg.as_ident() {
1822            self.add_value(
1823                key.to_id(),
1824                JsValue::unknown_empty(true, "updated with update expression"),
1825            );
1826        }
1827
1828        let mut ast_path =
1829            ast_path.with_guard(AstParentNodeRef::UpdateExpr(n, UpdateExprField::Arg));
1830        self.visit_expr(&n.arg, &mut ast_path);
1831    }
1832
1833    fn visit_call_expr<'ast: 'r, 'r>(
1834        &mut self,
1835        n: &'ast CallExpr,
1836        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1837    ) {
1838        // We handle `define(function (require) {})` here.
1839        if let Callee::Expr(callee) = &n.callee
1840            && n.args.len() == 1
1841            && let Some(require_var_id) = extract_var_from_umd_factory(callee, &n.args)
1842        {
1843            self.add_value(
1844                require_var_id,
1845                JsValue::unknown_if(
1846                    self.eval_context
1847                        .imports
1848                        .get_attributes(n.callee.span())
1849                        .ignore,
1850                    JsValue::WellKnownFunction(WellKnownFunctionKind::Require),
1851                    true,
1852                    "ignored require",
1853                ),
1854            );
1855        }
1856
1857        if self.check_iife(n, ast_path) {
1858            return;
1859        }
1860
1861        // special behavior of IIFEs
1862        {
1863            let mut ast_path =
1864                ast_path.with_guard(AstParentNodeRef::CallExpr(n, CallExprField::Callee));
1865            n.callee.visit_with_ast_path(self, &mut ast_path);
1866        }
1867
1868        self.check_call_expr_for_effects(
1869            &n.callee,
1870            n.args.iter(),
1871            n.span(),
1872            ast_path,
1873            CallOrNewExpr::Call(n),
1874        );
1875    }
1876
1877    fn visit_new_expr<'ast: 'r, 'r>(
1878        &mut self,
1879        n: &'ast NewExpr,
1880        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1881    ) {
1882        {
1883            let mut ast_path =
1884                ast_path.with_guard(AstParentNodeRef::NewExpr(n, NewExprField::Callee));
1885            n.callee.visit_with_ast_path(self, &mut ast_path);
1886        }
1887
1888        self.check_call_expr_for_effects(
1889            &Callee::Expr(n.callee.clone()),
1890            n.args.iter().flatten(),
1891            n.span(),
1892            ast_path,
1893            CallOrNewExpr::New(n),
1894        );
1895    }
1896
1897    fn visit_member_expr<'ast: 'r, 'r>(
1898        &mut self,
1899        member_expr: &'ast MemberExpr,
1900        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1901    ) {
1902        self.check_member_expr_for_effects(member_expr, ast_path);
1903        member_expr.visit_children_with_ast_path(self, ast_path);
1904    }
1905
1906    fn visit_expr<'ast: 'r, 'r>(
1907        &mut self,
1908        n: &'ast Expr,
1909        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1910    ) {
1911        self.with_decl_kind(None, |this| {
1912            n.visit_children_with_ast_path(this, ast_path);
1913        });
1914    }
1915
1916    fn visit_params<'ast: 'r, 'r>(
1917        &mut self,
1918        n: &'ast [Param],
1919        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1920    ) {
1921        let cur_fn_ident = self.cur_fn_ident();
1922        for (index, p) in n.iter().enumerate() {
1923            self.with_pat_value(Some(JsValue::Argument(cur_fn_ident, index)), |this| {
1924                let mut ast_path = ast_path.with_index_guard(index);
1925                p.visit_with_ast_path(this, &mut ast_path);
1926            });
1927        }
1928    }
1929
1930    fn visit_param<'ast: 'r, 'r>(
1931        &mut self,
1932        n: &'ast Param,
1933        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1934    ) {
1935        let Param {
1936            decorators,
1937            pat,
1938            span: _,
1939        } = n;
1940        self.with_decl_kind(None, |this| {
1941            // Decorators don't have access to the parameter values, so omit them
1942            this.with_pat_value(None, |this| {
1943                let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param(
1944                    n,
1945                    ParamField::Decorators(usize::MAX),
1946                ));
1947                this.visit_decorators(decorators, &mut ast_path);
1948            });
1949            {
1950                let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param(n, ParamField::Pat));
1951                this.visit_pat(pat, &mut ast_path);
1952            }
1953        });
1954    }
1955
1956    fn visit_fn_decl<'ast: 'r, 'r>(
1957        &mut self,
1958        decl: &'ast FnDecl,
1959        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1960    ) {
1961        let fn_value = self.enter_top_level_decl(&decl.ident, |this| {
1962            this.enter_fn(&*decl.function, |this| {
1963                decl.visit_children_with_ast_path(this, ast_path);
1964            })
1965        });
1966
1967        // Take all effects produced by the function and move them to hoisted effects since
1968        // function declarations are hoisted.
1969        // This accounts for the fact that even with `if (true) { return f} function f() {} ` `f` is
1970        // hoisted earlier of the condition. so we still need to process effects for it.
1971        // TODO(lukesandberg): shouldn't this just be the effects associated with the function.
1972        self.hoisted_effects.append(&mut self.effects);
1973
1974        self.add_value(decl.ident.to_id(), fn_value);
1975    }
1976
1977    fn visit_fn_expr<'ast: 'r, 'r>(
1978        &mut self,
1979        expr: &'ast FnExpr,
1980        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1981    ) {
1982        let fn_value = self.enter_fn(&*expr.function, |this| {
1983            expr.visit_children_with_ast_path(this, ast_path);
1984        });
1985        if let Some(ident) = &expr.ident {
1986            self.add_value(ident.to_id(), fn_value);
1987        } else {
1988            self.add_value(
1989                (
1990                    format!("*anonymous function {}*", expr.function.span.lo.0).into(),
1991                    SyntaxContext::empty(),
1992                ),
1993                fn_value,
1994            );
1995        }
1996    }
1997
1998    fn visit_arrow_expr<'ast: 'r, 'r>(
1999        &mut self,
2000        expr: &'ast ArrowExpr,
2001        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2002    ) {
2003        let fn_value = self.enter_fn(expr, |this| {
2004            let fn_id = this.cur_fn_ident();
2005            for (index, p) in expr.params.iter().enumerate() {
2006                this.with_pat_value(Some(JsValue::Argument(fn_id, index)), |this| {
2007                    let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
2008                        expr,
2009                        ArrowExprField::Params(index),
2010                    ));
2011                    p.visit_with_ast_path(this, &mut ast_path);
2012                });
2013            }
2014
2015            {
2016                let mut ast_path =
2017                    ast_path.with_guard(AstParentNodeRef::ArrowExpr(expr, ArrowExprField::Body));
2018                expr.body.visit_with_ast_path(this, &mut ast_path);
2019                // If body is a single expression treat it as a Block with an return statement
2020                if let BlockStmtOrExpr::Expr(inner_expr) = &*expr.body {
2021                    let implicit_return_value = this.eval_context.eval(inner_expr);
2022                    this.add_return_value(implicit_return_value);
2023                }
2024            }
2025        });
2026        self.add_value(
2027            (
2028                format!("*arrow function {}*", expr.span.lo.0).into(),
2029                SyntaxContext::empty(),
2030            ),
2031            fn_value,
2032        );
2033    }
2034
2035    fn visit_class_decl<'ast: 'r, 'r>(
2036        &mut self,
2037        decl: &'ast ClassDecl,
2038        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2039    ) {
2040        self.add_value_from_expr(
2041            decl.ident.to_id(),
2042            &Expr::Class(ClassExpr {
2043                ident: Some(decl.ident.clone()),
2044                class: decl.class.clone(),
2045            }),
2046        );
2047        decl.visit_children_with_ast_path(self, ast_path);
2048    }
2049
2050    fn visit_class<'ast: 'r, 'r>(
2051        &mut self,
2052        node: &'ast Class,
2053        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2054    ) {
2055        self.enter_block(LexicalContext::ClassBody, |this| {
2056            node.visit_children_with_ast_path(this, ast_path);
2057        });
2058    }
2059
2060    fn visit_getter_prop<'ast: 'r, 'r>(
2061        &mut self,
2062        node: &'ast GetterProp,
2063        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2064    ) {
2065        self.enter_fn(node, |this| {
2066            node.visit_children_with_ast_path(this, ast_path);
2067        });
2068    }
2069
2070    fn visit_setter_prop<'ast: 'r, 'r>(
2071        &mut self,
2072        node: &'ast SetterProp,
2073        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2074    ) {
2075        self.enter_fn(node, |this| {
2076            node.visit_children_with_ast_path(this, ast_path);
2077        });
2078    }
2079
2080    fn visit_constructor<'ast: 'r, 'r>(
2081        &mut self,
2082        node: &'ast Constructor,
2083        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2084    ) {
2085        self.enter_fn(node, |this| {
2086            node.visit_children_with_ast_path(this, ast_path);
2087        });
2088    }
2089
2090    fn visit_class_method<'ast: 'r, 'r>(
2091        &mut self,
2092        node: &'ast ClassMethod,
2093        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2094    ) {
2095        self.enter_fn(&*node.function, |this| {
2096            node.visit_children_with_ast_path(this, ast_path);
2097        });
2098    }
2099
2100    fn visit_private_method<'ast: 'r, 'r>(
2101        &mut self,
2102        node: &'ast PrivateMethod,
2103        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2104    ) {
2105        self.enter_fn(&*node.function, |this| {
2106            node.visit_children_with_ast_path(this, ast_path);
2107        });
2108    }
2109
2110    fn visit_method_prop<'ast: 'r, 'r>(
2111        &mut self,
2112        node: &'ast MethodProp,
2113        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2114    ) {
2115        self.enter_fn(&*node.function, |this| {
2116            node.visit_children_with_ast_path(this, ast_path);
2117        });
2118    }
2119
2120    fn visit_var_decl<'ast: 'r, 'r>(
2121        &mut self,
2122        n: &'ast VarDecl,
2123        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2124    ) {
2125        self.with_decl_kind(Some(n.kind), |this| {
2126            n.visit_children_with_ast_path(this, ast_path);
2127        });
2128    }
2129
2130    fn visit_var_declarator<'ast: 'r, 'r>(
2131        &mut self,
2132        n: &'ast VarDeclarator,
2133        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2134    ) {
2135        // LHS
2136        {
2137            let mut ast_path =
2138                ast_path.with_guard(AstParentNodeRef::VarDeclarator(n, VarDeclaratorField::Name));
2139
2140            if let Some(var_decl_kind) = self.var_decl_kind()
2141                && let Some(init) = &n.init
2142            {
2143                // For case like
2144                //
2145                // if (shouldRun()) {
2146                //   var x = true;
2147                // }
2148                // if (x) {
2149                // }
2150                //
2151                // The variable `x` is undefined
2152
2153                let should_include_undefined =
2154                    var_decl_kind == VarDeclKind::Var && self.is_in_nested_block_scope();
2155                let init_value = self.eval_context.eval(init);
2156                let pat_value = Some(if should_include_undefined {
2157                    JsValue::alternatives(vec![
2158                        init_value,
2159                        JsValue::Constant(ConstantValue::Undefined),
2160                    ])
2161                } else {
2162                    init_value
2163                });
2164                self.with_pat_value(pat_value, |this| {
2165                    this.visit_pat(&n.name, &mut ast_path);
2166                });
2167            } else {
2168                // Don't use `with_pat_value(None, ...)` here. A `VarDecl` can occur inside of a
2169                // `ForOfStmt` with no `init` field, but still have a `pat_value` set that we want
2170                // to inherit.
2171                self.visit_pat(&n.name, &mut ast_path);
2172            }
2173        }
2174
2175        // RHS
2176        {
2177            let mut ast_path =
2178                ast_path.with_guard(AstParentNodeRef::VarDeclarator(n, VarDeclaratorField::Init));
2179
2180            self.visit_opt_expr(&n.init, &mut ast_path);
2181        }
2182    }
2183
2184    fn visit_for_in_stmt<'ast: 'r, 'r>(
2185        &mut self,
2186        n: &'ast ForInStmt,
2187        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2188    ) {
2189        {
2190            let mut ast_path =
2191                ast_path.with_guard(AstParentNodeRef::ForInStmt(n, ForInStmtField::Right));
2192            n.right.visit_with_ast_path(self, &mut ast_path);
2193        }
2194
2195        {
2196            let mut ast_path =
2197                ast_path.with_guard(AstParentNodeRef::ForInStmt(n, ForInStmtField::Left));
2198            self.with_pat_value(
2199                // TODO this should really be
2200                // `Some(JsValue::iteratedKeys(Box::new(self.eval_context.eval(&n.right))))`
2201                Some(JsValue::unknown_empty(
2202                    false,
2203                    "for-in variable currently not analyzed",
2204                )),
2205                |this| {
2206                    n.left.visit_with_ast_path(this, &mut ast_path);
2207                },
2208            )
2209        }
2210
2211        let mut ast_path =
2212            ast_path.with_guard(AstParentNodeRef::ForInStmt(n, ForInStmtField::Body));
2213
2214        self.enter_control_flow(|this| {
2215            n.body.visit_with_ast_path(this, &mut ast_path);
2216        });
2217    }
2218
2219    fn visit_for_of_stmt<'ast: 'r, 'r>(
2220        &mut self,
2221        n: &'ast ForOfStmt,
2222        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2223    ) {
2224        {
2225            let mut ast_path =
2226                ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Right));
2227            n.right.visit_with_ast_path(self, &mut ast_path);
2228        }
2229
2230        let iterable = self.eval_context.eval(&n.right);
2231
2232        // TODO n.await is ignored (async interables)
2233        self.with_pat_value(Some(JsValue::iterated(Box::new(iterable))), |this| {
2234            let mut ast_path =
2235                ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Left));
2236            n.left.visit_with_ast_path(this, &mut ast_path);
2237        });
2238
2239        let mut ast_path =
2240            ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Body));
2241
2242        self.enter_control_flow(|this| {
2243            n.body.visit_with_ast_path(this, &mut ast_path);
2244        });
2245    }
2246
2247    fn visit_for_stmt<'ast: 'r, 'r>(
2248        &mut self,
2249        n: &'ast ForStmt,
2250        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2251    ) {
2252        {
2253            let mut ast_path =
2254                ast_path.with_guard(AstParentNodeRef::ForStmt(n, ForStmtField::Init));
2255            n.init.visit_with_ast_path(self, &mut ast_path);
2256        }
2257        self.enter_control_flow(|this| {
2258            {
2259                let mut ast_path =
2260                    ast_path.with_guard(AstParentNodeRef::ForStmt(n, ForStmtField::Test));
2261                n.test.visit_with_ast_path(this, &mut ast_path);
2262            }
2263            {
2264                let mut ast_path =
2265                    ast_path.with_guard(AstParentNodeRef::ForStmt(n, ForStmtField::Body));
2266                n.body.visit_with_ast_path(this, &mut ast_path);
2267            }
2268            {
2269                let mut ast_path =
2270                    ast_path.with_guard(AstParentNodeRef::ForStmt(n, ForStmtField::Update));
2271                n.update.visit_with_ast_path(this, &mut ast_path);
2272            }
2273        });
2274    }
2275
2276    fn visit_while_stmt<'ast: 'r, 'r>(
2277        &mut self,
2278        n: &'ast WhileStmt,
2279        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2280    ) {
2281        // Enter control flow for everything (test and body both repeat in loop iterations)
2282        self.enter_control_flow(|this| {
2283            {
2284                let mut ast_path =
2285                    ast_path.with_guard(AstParentNodeRef::WhileStmt(n, WhileStmtField::Test));
2286                n.test.visit_with_ast_path(this, &mut ast_path);
2287            }
2288            {
2289                let mut ast_path =
2290                    ast_path.with_guard(AstParentNodeRef::WhileStmt(n, WhileStmtField::Body));
2291                n.body.visit_with_ast_path(this, &mut ast_path);
2292            }
2293        });
2294    }
2295
2296    fn visit_do_while_stmt<'ast: 'r, 'r>(
2297        &mut self,
2298        n: &'ast DoWhileStmt,
2299        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2300    ) {
2301        // Enter control flow for everything (body and test both are part of loop iterations)
2302        self.enter_control_flow(|this| {
2303            {
2304                let mut ast_path =
2305                    ast_path.with_guard(AstParentNodeRef::DoWhileStmt(n, DoWhileStmtField::Body));
2306                n.body.visit_with_ast_path(this, &mut ast_path);
2307            }
2308            {
2309                let mut ast_path =
2310                    ast_path.with_guard(AstParentNodeRef::DoWhileStmt(n, DoWhileStmtField::Test));
2311                n.test.visit_with_ast_path(this, &mut ast_path);
2312            }
2313        });
2314    }
2315
2316    fn visit_simple_assign_target<'ast: 'r, 'r>(
2317        &mut self,
2318        n: &'ast SimpleAssignTarget,
2319        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2320    ) {
2321        let value = self.take_pat_value();
2322        if let SimpleAssignTarget::Ident(i) = n {
2323            n.visit_children_with_ast_path(self, ast_path);
2324
2325            self.add_value(
2326                i.to_id(),
2327                value.unwrap_or_else(|| {
2328                    JsValue::unknown(JsValue::Variable(i.to_id()), false, "pattern without value")
2329                }),
2330            );
2331            return;
2332        }
2333
2334        n.visit_children_with_ast_path(self, ast_path);
2335    }
2336
2337    fn visit_assign_target_pat<'ast: 'r, 'r>(
2338        &mut self,
2339        pat: &'ast AssignTargetPat,
2340        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2341    ) {
2342        let value = self
2343            .take_pat_value()
2344            .unwrap_or_else(|| JsValue::unknown_empty(false, "pattern without value"));
2345        match pat {
2346            AssignTargetPat::Array(arr) => {
2347                let mut ast_path = ast_path.with_guard(AstParentNodeRef::AssignTargetPat(
2348                    pat,
2349                    AssignTargetPatField::Array,
2350                ));
2351                self.handle_array_pat_with_value(arr, value, &mut ast_path);
2352            }
2353            AssignTargetPat::Object(obj) => {
2354                let mut ast_path = ast_path.with_guard(AstParentNodeRef::AssignTargetPat(
2355                    pat,
2356                    AssignTargetPatField::Object,
2357                ));
2358                self.handle_object_pat_with_value(obj, value, &mut ast_path);
2359            }
2360            AssignTargetPat::Invalid(_) => {}
2361        }
2362    }
2363
2364    fn visit_pat<'ast: 'r, 'r>(
2365        &mut self,
2366        pat: &'ast Pat,
2367        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2368    ) {
2369        let value = self.take_pat_value();
2370        match pat {
2371            Pat::Ident(i) => {
2372                self.add_value(
2373                    i.to_id(),
2374                    value.unwrap_or_else(|| {
2375                        JsValue::unknown(
2376                            JsValue::Variable(i.to_id()),
2377                            false,
2378                            "pattern without value",
2379                        )
2380                    }),
2381                );
2382            }
2383
2384            Pat::Array(arr) => {
2385                let mut ast_path = ast_path.with_guard(AstParentNodeRef::Pat(pat, PatField::Array));
2386                let value =
2387                    value.unwrap_or_else(|| JsValue::unknown_empty(false, "pattern without value"));
2388                self.handle_array_pat_with_value(arr, value, &mut ast_path);
2389            }
2390
2391            Pat::Object(obj) => {
2392                let mut ast_path =
2393                    ast_path.with_guard(AstParentNodeRef::Pat(pat, PatField::Object));
2394                let value =
2395                    value.unwrap_or_else(|| JsValue::unknown_empty(false, "pattern without value"));
2396                self.handle_object_pat_with_value(obj, value, &mut ast_path);
2397            }
2398
2399            _ => pat.visit_children_with_ast_path(self, ast_path),
2400        }
2401    }
2402
2403    fn visit_return_stmt<'ast: 'r, 'r>(
2404        &mut self,
2405        stmt: &'ast ReturnStmt,
2406        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2407    ) {
2408        stmt.visit_children_with_ast_path(self, ast_path);
2409
2410        // Technically a top level return is illegal, but node supports it due to how module
2411        // wrapping works.
2412        if self.is_in_fn() {
2413            let return_value = stmt
2414                .arg
2415                .as_deref()
2416                .map(|e| self.eval_context.eval(e))
2417                .unwrap_or(JsValue::Constant(ConstantValue::Undefined));
2418
2419            self.add_return_value(return_value);
2420        }
2421
2422        self.add_early_return_always(ast_path);
2423    }
2424
2425    fn visit_ident<'ast: 'r, 'r>(
2426        &mut self,
2427        ident: &'ast Ident,
2428        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2429    ) {
2430        // Note: `Ident` is (generally) only used for nodes referencing a variable, as it has scope
2431        // information. In other cases (e.g. object literals, properties of member expressions),
2432        // `IdentName` is used instead.
2433
2434        // Note: The `Ident` children of `ImportSpecifier` are not visited because
2435        // `visit_import_specifier` bails out.
2436
2437        // Attempt to add import effects.
2438        if let Some((esm_reference_index, export)) =
2439            self.eval_context.imports.get_binding(&ident.to_id())
2440        {
2441            let usage = self
2442                .data
2443                .import_usages
2444                .entry(esm_reference_index)
2445                .or_default();
2446            if let Some(top_level) = self.state.cur_top_level_decl_name() {
2447                usage.add_usage(top_level);
2448            } else {
2449                usage.make_side_effects();
2450            }
2451
2452            // Optimization: Look for a MemberExpr to see if we only access a few members from the
2453            // module, add those specific effects instead of depending on the entire module.
2454            //
2455            // export.is_none() checks for a namespace import (*).
2456            if export.is_none()
2457                && !self
2458                    .eval_context
2459                    .imports
2460                    .should_import_all(esm_reference_index)
2461                && let Some(AstParentNodeRef::MemberExpr(member, MemberExprField::Obj)) =
2462                    ast_path.get(ast_path.len() - 2)
2463                && let Some(prop) = self.eval_context.eval_member_prop(&member.prop)
2464                && let Some(prop_str) = prop.as_str()
2465            {
2466                // a namespace member access like
2467                // `import * as ns from "..."; ns.exportName`
2468                self.add_effect(Effect::ImportedBinding {
2469                    esm_reference_index,
2470                    export: Some(prop_str.into()),
2471                    // point to the MemberExpression instead
2472                    ast_path: as_parent_path_skip(ast_path, 1),
2473                    span: member.span(),
2474                });
2475            } else {
2476                self.add_effect(Effect::ImportedBinding {
2477                    esm_reference_index,
2478                    export,
2479                    ast_path: as_parent_path(ast_path),
2480                    span: ident.span(),
2481                })
2482            }
2483            return;
2484        }
2485
2486        // If this identifier is free, produce an effect so we can potentially replace it later.
2487        if self.analyze_mode.is_code_gen()
2488            && let JsValue::FreeVar(var) = self.eval_context.eval_ident(ident)
2489        {
2490            // TODO(lukesandberg): we should consider filtering effects here, e.g. there is no
2491            // benefit in an Effect for `window` or `Math`
2492            self.add_effect(Effect::FreeVar {
2493                var,
2494                ast_path: as_parent_path(ast_path),
2495                span: ident.span(),
2496            })
2497        }
2498
2499        if !is_unresolved(ident, self.eval_context.unresolved_mark) {
2500            if let Some(top_level) = self.state.cur_top_level_decl_name() {
2501                if !(ident.sym == top_level.0 && ident.ctxt == top_level.1) {
2502                    self.data
2503                        .decl_usages
2504                        .entry(ident.to_id())
2505                        .or_default()
2506                        .add_usage(top_level);
2507                }
2508            } else {
2509                self.data
2510                    .decl_usages
2511                    .entry(ident.to_id())
2512                    .or_default()
2513                    .make_side_effects();
2514            }
2515        }
2516    }
2517
2518    fn visit_this_expr<'ast: 'r, 'r>(
2519        &mut self,
2520        node: &'ast ThisExpr,
2521        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2522    ) {
2523        if self.analyze_mode.is_code_gen() && !self.is_this_bound() {
2524            // Otherwise 'this' is free
2525            self.add_effect(Effect::FreeVar {
2526                var: atom!("this"),
2527                ast_path: as_parent_path(ast_path),
2528                span: node.span(),
2529            })
2530        }
2531    }
2532
2533    fn visit_meta_prop_expr<'ast: 'r, 'r>(
2534        &mut self,
2535        expr: &'ast MetaPropExpr,
2536        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2537    ) {
2538        if self.analyze_mode.is_code_gen() && expr.kind == MetaPropKind::ImportMeta {
2539            // MetaPropExpr also covers `new.target`. Only consider `import.meta`
2540            // an effect.
2541            self.add_effect(Effect::ImportMeta {
2542                span: expr.span,
2543                ast_path: as_parent_path(ast_path),
2544            })
2545        }
2546    }
2547
2548    fn visit_program<'ast: 'r, 'r>(
2549        &mut self,
2550        program: &'ast Program,
2551        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2552    ) {
2553        self.effects = take(&mut self.data.effects);
2554        self.enter_block(LexicalContext::Block, |this| {
2555            program.visit_children_with_ast_path(this, ast_path);
2556        });
2557        self.effects.append(&mut self.hoisted_effects);
2558        self.data.effects = take(&mut self.effects);
2559    }
2560
2561    fn visit_cond_expr<'ast: 'r, 'r>(
2562        &mut self,
2563        expr: &'ast CondExpr,
2564        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2565    ) {
2566        {
2567            let mut ast_path =
2568                ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Test));
2569            expr.test.visit_with_ast_path(self, &mut ast_path);
2570        }
2571
2572        let prev_effects = take(&mut self.effects);
2573        let then = {
2574            let mut ast_path =
2575                ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Cons));
2576            expr.cons.visit_with_ast_path(self, &mut ast_path);
2577            Box::new(EffectsBlock {
2578                effects: take(&mut self.effects),
2579                range: AstPathRange::Exact(as_parent_path(&ast_path)),
2580            })
2581        };
2582        let r#else = {
2583            let mut ast_path =
2584                ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Alt));
2585            expr.alt.visit_with_ast_path(self, &mut ast_path);
2586            Box::new(EffectsBlock {
2587                effects: take(&mut self.effects),
2588                range: AstPathRange::Exact(as_parent_path(&ast_path)),
2589            })
2590        };
2591        self.effects = prev_effects;
2592
2593        self.add_conditional_effect(
2594            &expr.test,
2595            ast_path,
2596            AstParentKind::CondExpr(CondExprField::Test),
2597            expr.span(),
2598            ConditionalKind::Ternary { then, r#else },
2599        );
2600    }
2601
2602    fn visit_if_stmt<'ast: 'r, 'r>(
2603        &mut self,
2604        stmt: &'ast IfStmt,
2605        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2606    ) {
2607        {
2608            let mut ast_path =
2609                ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Test));
2610            stmt.test.visit_with_ast_path(self, &mut ast_path);
2611        }
2612        let prev_effects = take(&mut self.effects);
2613        let then_returning;
2614        let then = {
2615            let mut ast_path =
2616                ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Cons));
2617            then_returning = self
2618                .enter_control_flow(|this| {
2619                    stmt.cons.visit_with_ast_path(this, &mut ast_path);
2620                })
2621                .1;
2622
2623            Box::new(EffectsBlock {
2624                effects: take(&mut self.effects),
2625                range: AstPathRange::Exact(as_parent_path(&ast_path)),
2626            })
2627        };
2628        let mut else_returning = false;
2629        let r#else = stmt.alt.as_ref().map(|alt| {
2630            let mut ast_path =
2631                ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Alt));
2632            else_returning = self
2633                .enter_control_flow(|this| {
2634                    alt.visit_with_ast_path(this, &mut ast_path);
2635                })
2636                .1;
2637
2638            Box::new(EffectsBlock {
2639                effects: take(&mut self.effects),
2640                range: AstPathRange::Exact(as_parent_path(&ast_path)),
2641            })
2642        });
2643        self.effects = prev_effects;
2644        self.add_conditional_if_effect_with_early_return(
2645            &stmt.test,
2646            ast_path,
2647            AstParentKind::IfStmt(IfStmtField::Test),
2648            stmt.span(),
2649            (!then.is_empty()).then_some(then),
2650            r#else.and_then(|block| (!block.is_empty()).then_some(block)),
2651            then_returning,
2652            else_returning,
2653        );
2654    }
2655
2656    fn visit_try_stmt<'ast: 'r, 'r>(
2657        &mut self,
2658        stmt: &'ast TryStmt,
2659        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2660    ) {
2661        // TODO: if both try and catch return unconditionally, then so does the whole try statement
2662        let prev_effects = take(&mut self.effects);
2663
2664        let mut block = {
2665            let mut ast_path =
2666                ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Block));
2667            self.enter_try(|this| {
2668                stmt.block.visit_with_ast_path(this, &mut ast_path);
2669            });
2670
2671            take(&mut self.effects)
2672        };
2673        let mut handler = if let Some(handler) = stmt.handler.as_ref() {
2674            let mut ast_path =
2675                ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Handler));
2676            self.enter_control_flow(|this| {
2677                handler.visit_with_ast_path(this, &mut ast_path);
2678            });
2679            take(&mut self.effects)
2680        } else {
2681            vec![]
2682        };
2683        self.effects = prev_effects;
2684        self.effects.append(&mut block);
2685        self.effects.append(&mut handler);
2686        if let Some(finalizer) = stmt.finalizer.as_ref() {
2687            let finally_returns_unconditionally = {
2688                let mut ast_path =
2689                    ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Finalizer));
2690                self.enter_control_flow(|this| {
2691                    finalizer.visit_with_ast_path(this, &mut ast_path);
2692                })
2693                .1
2694            };
2695            // If a finally block early returns the parent block does too.
2696            if finally_returns_unconditionally {
2697                self.add_early_return_always(ast_path);
2698            }
2699        };
2700    }
2701
2702    fn visit_switch_case<'ast: 'r, 'r>(
2703        &mut self,
2704        case: &'ast SwitchCase,
2705        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2706    ) {
2707        let prev_effects = take(&mut self.effects);
2708        self.enter_control_flow(|this| {
2709            case.visit_children_with_ast_path(this, ast_path);
2710        });
2711        let mut effects = take(&mut self.effects);
2712        self.effects = prev_effects;
2713        self.effects.append(&mut effects);
2714    }
2715
2716    fn visit_block_stmt<'ast: 'r, 'r>(
2717        &mut self,
2718        n: &'ast BlockStmt,
2719        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2720    ) {
2721        match self.cur_lexical_context() {
2722            LexicalContext::Function { .. } => {
2723                let mut effects = take(&mut self.effects);
2724                let hoisted_effects = take(&mut self.hoisted_effects);
2725
2726                let (_, returns_unconditionally) =
2727                    self.enter_block(LexicalContext::Block, |this| {
2728                        n.visit_children_with_ast_path(this, ast_path);
2729                    });
2730                // By handling this logic here instead of in enter_fn, we naturally skip it
2731                // for arrow functions with single expression bodies, since they just don't hit this
2732                // path.
2733                if !returns_unconditionally {
2734                    self.add_return_value(JsValue::Constant(ConstantValue::Undefined));
2735                }
2736                self.effects.append(&mut self.hoisted_effects);
2737                effects.append(&mut self.effects);
2738                self.hoisted_effects = hoisted_effects;
2739                self.effects = effects;
2740            }
2741            LexicalContext::ControlFlow { .. } => {
2742                self.with_block(LexicalContext::Block, |this| {
2743                    n.visit_children_with_ast_path(this, ast_path)
2744                });
2745            }
2746            LexicalContext::Block => {
2747                // Handle anonymous block statement
2748                // e.g., enter a new control flow context and because it is 'unconditiona' we
2749                // need to propagate early returns
2750                let (_, returns_early) = self.enter_control_flow(|this| {
2751                    n.visit_children_with_ast_path(this, ast_path);
2752                });
2753                if returns_early {
2754                    self.add_early_return_always(ast_path);
2755                }
2756            }
2757            LexicalContext::ClassBody => {
2758                // this would be something like a `static` initialization block
2759                // there is no early return logic required here so just visit children
2760                n.visit_children_with_ast_path(self, ast_path);
2761            }
2762        }
2763    }
2764
2765    fn visit_unary_expr<'ast: 'r, 'r>(
2766        &mut self,
2767        n: &'ast UnaryExpr,
2768        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2769    ) {
2770        if n.op == UnaryOp::TypeOf && self.analyze_mode.is_code_gen() {
2771            let arg_value = Box::new(self.eval_context.eval(&n.arg));
2772
2773            self.add_effect(Effect::TypeOf {
2774                arg: arg_value,
2775                ast_path: as_parent_path(ast_path),
2776                span: n.span(),
2777            });
2778        }
2779
2780        n.visit_children_with_ast_path(self, ast_path);
2781    }
2782
2783    fn visit_labeled_stmt<'ast: 'r, 'r>(
2784        &mut self,
2785        stmt: &'ast LabeledStmt,
2786        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2787    ) {
2788        let mut prev_effects = take(&mut self.effects);
2789        self.enter_control_flow(|this| {
2790            stmt.visit_children_with_ast_path(this, ast_path);
2791        });
2792
2793        let effects = take(&mut self.effects);
2794
2795        prev_effects.push(Effect::Conditional {
2796            condition: Box::new(JsValue::unknown_empty(true, "labeled statement")),
2797            kind: Box::new(ConditionalKind::Labeled {
2798                body: Box::new(EffectsBlock {
2799                    effects,
2800                    range: AstPathRange::Exact(as_parent_path_with(
2801                        ast_path,
2802                        AstParentKind::LabeledStmt(LabeledStmtField::Body),
2803                    )),
2804                }),
2805            }),
2806            ast_path: as_parent_path(ast_path),
2807            span: stmt.span,
2808        });
2809
2810        self.effects = prev_effects;
2811    }
2812
2813    fn visit_export_decl<'ast: 'r, 'r>(
2814        &mut self,
2815        node: &'ast ExportDecl,
2816        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2817    ) {
2818        match &node.decl {
2819            Decl::Class(node) => {
2820                self.data
2821                    .exports
2822                    .insert(node.ident.sym.clone(), node.ident.to_id());
2823            }
2824            Decl::Fn(node) => {
2825                self.data
2826                    .exports
2827                    .insert(node.ident.sym.clone(), node.ident.to_id());
2828            }
2829            Decl::Var(node) => {
2830                for VarDeclarator { name, .. } in &node.decls {
2831                    for_each_ident_in_pat(name, &mut |name, ctxt| {
2832                        self.data.exports.insert(name.clone(), (name.clone(), ctxt));
2833                    });
2834                }
2835            }
2836            _ => {}
2837        };
2838        node.visit_children_with_ast_path(self, ast_path);
2839    }
2840
2841    fn visit_export_named_specifier<'ast: 'r, 'r>(
2842        &mut self,
2843        node: &'ast ExportNamedSpecifier,
2844        ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2845    ) {
2846        let export_name = node
2847            .exported
2848            .as_ref()
2849            .unwrap_or(&node.orig)
2850            .atom()
2851            .into_owned();
2852        self.data.exports.insert(
2853            export_name,
2854            match &node.orig {
2855                ModuleExportName::Ident(ident) => ident.to_id(),
2856                ModuleExportName::Str(_) => unreachable!("exporting a string should be impossible"),
2857            },
2858        );
2859        node.visit_children_with_ast_path(self, ast_path);
2860    }
2861}
2862
2863impl Analyzer<'_> {
2864    fn add_conditional_if_effect_with_early_return(
2865        &mut self,
2866        test: &Expr,
2867        ast_path: &AstNodePath<AstParentNodeRef<'_>>,
2868        condition_ast_kind: AstParentKind,
2869        span: Span,
2870        then: Option<Box<EffectsBlock>>,
2871        r#else: Option<Box<EffectsBlock>>,
2872        early_return_when_true: bool,
2873        early_return_when_false: bool,
2874    ) {
2875        if then.is_none() && r#else.is_none() && !early_return_when_false && !early_return_when_true
2876        {
2877            return;
2878        }
2879        let condition = Box::new(self.eval_context.eval(test));
2880        if condition.is_unknown() {
2881            if let Some(mut then) = then {
2882                self.effects.append(&mut then.effects);
2883            }
2884            if let Some(mut r#else) = r#else {
2885                self.effects.append(&mut r#else.effects);
2886            }
2887            return;
2888        }
2889        match (early_return_when_true, early_return_when_false) {
2890            (true, false) => {
2891                let early_return = EarlyReturn::Conditional {
2892                    prev_effects: take(&mut self.effects),
2893                    start_ast_path: as_parent_path(ast_path),
2894                    condition,
2895                    then,
2896                    r#else,
2897                    condition_ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2898                    span,
2899                    early_return_condition_value: true,
2900                };
2901                self.early_return_stack_mut().push(early_return);
2902            }
2903            (false, true) => {
2904                let early_return = EarlyReturn::Conditional {
2905                    prev_effects: take(&mut self.effects),
2906                    start_ast_path: as_parent_path(ast_path),
2907                    condition,
2908                    then,
2909                    r#else,
2910                    condition_ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2911                    span,
2912                    early_return_condition_value: false,
2913                };
2914                self.early_return_stack_mut().push(early_return);
2915            }
2916            (false, false) | (true, true) => {
2917                let kind = match (then, r#else) {
2918                    (Some(then), Some(r#else)) => ConditionalKind::IfElse { then, r#else },
2919                    (Some(then), None) => ConditionalKind::If { then },
2920                    (None, Some(r#else)) => ConditionalKind::Else { r#else },
2921                    (None, None) => {
2922                        // No effects, ignore
2923                        return;
2924                    }
2925                };
2926                self.add_effect(Effect::Conditional {
2927                    condition,
2928                    kind: Box::new(kind),
2929                    ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2930                    span,
2931                });
2932                if early_return_when_false && early_return_when_true {
2933                    let early_return = EarlyReturn::Always {
2934                        prev_effects: take(&mut self.effects),
2935                        start_ast_path: as_parent_path(ast_path),
2936                    };
2937                    self.early_return_stack_mut().push(early_return);
2938                }
2939            }
2940        }
2941    }
2942
2943    fn add_conditional_effect(
2944        &mut self,
2945        test: &Expr,
2946        ast_path: &AstNodePath<AstParentNodeRef<'_>>,
2947        ast_kind: AstParentKind,
2948        span: Span,
2949        mut cond_kind: ConditionalKind,
2950    ) {
2951        let condition = Box::new(self.eval_context.eval(test));
2952        if condition.is_unknown() {
2953            match &mut cond_kind {
2954                ConditionalKind::If { then } => {
2955                    self.effects.append(&mut then.effects);
2956                }
2957                ConditionalKind::Else { r#else } => {
2958                    self.effects.append(&mut r#else.effects);
2959                }
2960                ConditionalKind::IfElse { then, r#else }
2961                | ConditionalKind::Ternary { then, r#else } => {
2962                    self.effects.append(&mut then.effects);
2963                    self.effects.append(&mut r#else.effects);
2964                }
2965                ConditionalKind::IfElseMultiple { then, r#else } => {
2966                    for block in then {
2967                        self.effects.append(&mut block.effects);
2968                    }
2969                    for block in r#else {
2970                        self.effects.append(&mut block.effects);
2971                    }
2972                }
2973                ConditionalKind::And { expr }
2974                | ConditionalKind::Or { expr }
2975                | ConditionalKind::NullishCoalescing { expr } => {
2976                    self.effects.append(&mut expr.effects);
2977                }
2978                ConditionalKind::Labeled { body } => {
2979                    self.effects.append(&mut body.effects);
2980                }
2981            }
2982        } else {
2983            self.add_effect(Effect::Conditional {
2984                condition,
2985                kind: Box::new(cond_kind),
2986                ast_path: as_parent_path_with(ast_path, ast_kind),
2987                span,
2988            });
2989        }
2990    }
2991
2992    fn handle_array_pat_with_value<'ast: 'r, 'r>(
2993        &mut self,
2994        arr: &'ast ArrayPat,
2995        pat_value: JsValue,
2996        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2997    ) {
2998        match pat_value {
2999            JsValue::Array { items, .. } => {
3000                for (idx, (elem_pat, value_item)) in arr
3001                    .elems
3002                    .iter()
3003                    // TODO: This does not handle inline spreads correctly
3004                    // e.g. `let [a,..b,c] = [1,2,3]`
3005                    .zip(items.into_iter().map(Some).chain(iter::repeat(None)))
3006                    .enumerate()
3007                {
3008                    self.with_pat_value(value_item, |this| {
3009                        let mut ast_path = ast_path
3010                            .with_guard(AstParentNodeRef::ArrayPat(arr, ArrayPatField::Elems(idx)));
3011                        elem_pat.visit_with_ast_path(this, &mut ast_path);
3012                    });
3013                }
3014            }
3015            value => {
3016                for (idx, elem) in arr.elems.iter().enumerate() {
3017                    let pat_value = Some(JsValue::member(
3018                        Box::new(value.clone()),
3019                        Box::new(JsValue::Constant(ConstantValue::Num(ConstantNumber(
3020                            idx as f64,
3021                        )))),
3022                    ));
3023                    self.with_pat_value(pat_value, |this| {
3024                        let mut ast_path = ast_path
3025                            .with_guard(AstParentNodeRef::ArrayPat(arr, ArrayPatField::Elems(idx)));
3026                        elem.visit_with_ast_path(this, &mut ast_path);
3027                    });
3028                }
3029            }
3030        }
3031    }
3032
3033    fn handle_object_pat_with_value<'ast: 'r, 'r>(
3034        &mut self,
3035        obj: &'ast ObjectPat,
3036        pat_value: JsValue,
3037        ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
3038    ) {
3039        for (i, prop) in obj.props.iter().enumerate() {
3040            let mut ast_path =
3041                ast_path.with_guard(AstParentNodeRef::ObjectPat(obj, ObjectPatField::Props(i)));
3042            match prop {
3043                ObjectPatProp::KeyValue(kv) => {
3044                    let mut ast_path = ast_path.with_guard(AstParentNodeRef::ObjectPatProp(
3045                        prop,
3046                        ObjectPatPropField::KeyValue,
3047                    ));
3048                    let KeyValuePatProp { key, value } = kv;
3049                    let key_value = self.eval_context.eval_prop_name(key);
3050                    {
3051                        let mut ast_path = ast_path.with_guard(AstParentNodeRef::KeyValuePatProp(
3052                            kv,
3053                            KeyValuePatPropField::Key,
3054                        ));
3055                        key.visit_with_ast_path(self, &mut ast_path);
3056                    }
3057                    let pat_value = Some(JsValue::member(
3058                        Box::new(pat_value.clone()),
3059                        Box::new(key_value),
3060                    ));
3061                    self.with_pat_value(pat_value, |this| {
3062                        let mut ast_path = ast_path.with_guard(AstParentNodeRef::KeyValuePatProp(
3063                            kv,
3064                            KeyValuePatPropField::Value,
3065                        ));
3066                        value.visit_with_ast_path(this, &mut ast_path);
3067                    });
3068                }
3069                ObjectPatProp::Assign(assign) => {
3070                    let mut ast_path = ast_path.with_guard(AstParentNodeRef::ObjectPatProp(
3071                        prop,
3072                        ObjectPatPropField::Assign,
3073                    ));
3074                    let AssignPatProp { key, value, .. } = assign;
3075                    let key_value = key.sym.clone().into();
3076                    {
3077                        let mut ast_path = ast_path.with_guard(AstParentNodeRef::AssignPatProp(
3078                            assign,
3079                            AssignPatPropField::Key,
3080                        ));
3081                        key.visit_with_ast_path(self, &mut ast_path);
3082                    }
3083                    self.add_value(
3084                        key.to_id(),
3085                        if let Some(box value) = value {
3086                            let value = self.eval_context.eval(value);
3087                            JsValue::alternatives(vec![
3088                                JsValue::member(Box::new(pat_value.clone()), Box::new(key_value)),
3089                                value,
3090                            ])
3091                        } else {
3092                            JsValue::member(Box::new(pat_value.clone()), Box::new(key_value))
3093                        },
3094                    );
3095                    {
3096                        let mut ast_path = ast_path.with_guard(AstParentNodeRef::AssignPatProp(
3097                            assign,
3098                            AssignPatPropField::Value,
3099                        ));
3100                        value.visit_with_ast_path(self, &mut ast_path);
3101                    }
3102                }
3103
3104                _ => prop.visit_with_ast_path(self, &mut ast_path),
3105            }
3106        }
3107    }
3108}
3109
3110fn extract_var_from_umd_factory(callee: &Expr, args: &[ExprOrSpread]) -> Option<Id> {
3111    match unparen(callee) {
3112        Expr::Ident(Ident { sym, .. }) => {
3113            if &**sym == "define"
3114                && let Expr::Fn(FnExpr { function, .. }) = &*args[0].expr
3115            {
3116                let params = &*function.params;
3117                if params.len() == 1
3118                    && let Pat::Ident(param) = &params[0].pat
3119                    && &*param.id.sym == "require"
3120                {
3121                    return Some(param.to_id());
3122                }
3123            }
3124        }
3125
3126        // umd may use (function (factory){
3127        //   // Somewhere, define(['require', 'exports'], factory)
3128        // }(function (require, exports){}))
3129        //
3130        // In all module system which has `require`, `require` in the factory function can be
3131        // treated as a well-known require.
3132        Expr::Fn(FnExpr { function, .. }) => {
3133            let params = &*function.params;
3134            if params.len() == 1
3135                && let Some(FnExpr { function, .. }) =
3136                    args.first().and_then(|arg| arg.expr.as_fn_expr())
3137            {
3138                let params = &*function.params;
3139                if !params.is_empty()
3140                    && let Pat::Ident(param) = &params[0].pat
3141                    && &*param.id.sym == "require"
3142                {
3143                    return Some(param.to_id());
3144                }
3145            }
3146        }
3147
3148        _ => {}
3149    }
3150
3151    None
3152}