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