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