turbopack_ecmascript/analyzer/
graph.rs

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