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