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