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