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