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