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.is_multiple_of(2) {
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)
801 | AstParentKind::Function(FunctionField::Body)
802 | AstParentKind::Constructor(ConstructorField::Body)
803 | AstParentKind::ClassMethod(ClassMethodField::Function)
804 | AstParentKind::GetterProp(GetterPropField::Body)
805 | AstParentKind::SetterProp(SetterPropField::Body)
806 | AstParentKind::MethodProp(MethodPropField::Function) => Some(false),
807 AstParentKind::TryStmt(TryStmtField::Block) => Some(true),
808 _ => None,
809 })
810 .unwrap_or(false)
811}
812
813enum CallOrNewExpr<'ast> {
814 Call(&'ast CallExpr),
815 New(&'ast NewExpr),
816}
817impl CallOrNewExpr<'_> {
818 fn as_call(&self) -> Option<&CallExpr> {
819 match *self {
820 CallOrNewExpr::Call(n) => Some(n),
821 CallOrNewExpr::New(_) => None,
822 }
823 }
824 fn as_new(&self) -> Option<&NewExpr> {
825 match *self {
826 CallOrNewExpr::Call(_) => None,
827 CallOrNewExpr::New(n) => Some(n),
828 }
829 }
830}
831
832impl Analyzer<'_> {
833 fn add_value(&mut self, id: Id, value: JsValue) {
834 if is_unresolved_id(&id, self.eval_context.unresolved_mark) {
835 self.data.free_var_ids.insert(id.0.clone(), id.clone());
836 }
837
838 if let Some(prev) = self.data.values.get_mut(&id) {
839 prev.add_alt(value);
840 } else {
841 self.data.values.insert(id, value);
842 }
843 }
847
848 fn add_value_from_expr(&mut self, id: Id, value: &Expr) {
849 let value = self.eval_context.eval(value);
850
851 self.add_value(id, value);
852 }
853
854 fn add_effect(&mut self, effect: Effect) {
855 self.effects.push(effect);
856 }
857
858 fn check_iife<'ast: 'r, 'r>(
859 &mut self,
860 n: &'ast CallExpr,
861 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
862 ) -> bool {
863 fn unparen<'ast: 'r, 'r, T>(
864 expr: &'ast Expr,
865 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
866 f: impl FnOnce(&'ast Expr, &mut AstNodePath<AstParentNodeRef<'r>>) -> T,
867 ) -> T {
868 if let Some(inner_expr) = expr.as_paren() {
869 let mut ast_path =
870 ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Paren));
871 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ParenExpr(
872 inner_expr,
873 ParenExprField::Expr,
874 ));
875 unparen(&inner_expr.expr, &mut ast_path, f)
876 } else {
877 f(expr, ast_path)
878 }
879 }
880
881 if n.args.iter().any(|arg| arg.spread.is_some()) {
882 return false;
883 }
884
885 let Some(expr) = n.callee.as_expr() else {
886 return false;
887 };
888
889 let fn_expr = {
890 let mut ast_path =
891 ast_path.with_guard(AstParentNodeRef::CallExpr(n, CallExprField::Callee));
892 let mut ast_path =
893 ast_path.with_guard(AstParentNodeRef::Callee(&n.callee, CalleeField::Expr));
894 unparen(expr, &mut ast_path, |expr, ast_path| match expr {
895 Expr::Fn(fn_expr @ FnExpr { function, ident }) => {
896 let mut ast_path =
897 ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Fn));
898 {
899 let mut ast_path = ast_path
900 .with_guard(AstParentNodeRef::FnExpr(fn_expr, FnExprField::Ident));
901 self.visit_opt_ident(ident, &mut ast_path);
902
903 if let Some(ident) = ident
905 && contains_ident_ref(&function.body, &ident.to_id())
906 {
907 return false;
908 }
909 }
910
911 {
912 let mut ast_path = ast_path
913 .with_guard(AstParentNodeRef::FnExpr(fn_expr, FnExprField::Function));
914 self.handle_iife_function(function, &mut ast_path, &n.args);
915 }
916
917 true
918 }
919
920 Expr::Arrow(arrow_expr) => {
921 let mut ast_path =
922 ast_path.with_guard(AstParentNodeRef::Expr(expr, ExprField::Arrow));
923 let args = &n.args;
924 self.handle_iife_arrow(arrow_expr, args, &mut ast_path);
925 true
926 }
927 _ => false,
928 })
929 };
930
931 if !fn_expr {
932 return false;
933 }
934
935 let mut ast_path = ast_path.with_guard(AstParentNodeRef::CallExpr(
936 n,
937 CallExprField::Args(usize::MAX),
938 ));
939
940 self.visit_expr_or_spreads(&n.args, &mut ast_path);
941
942 true
943 }
944
945 fn handle_iife_arrow<'ast: 'r, 'r>(
946 &mut self,
947 arrow_expr: &'ast ArrowExpr,
948 args: &[ExprOrSpread],
949 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
950 ) {
951 let ArrowExpr {
952 params,
953 body,
954 is_async: _,
955 is_generator: _,
956 return_type,
957 span: _,
958 type_params,
959 ctxt: _,
960 } = arrow_expr;
961 let mut iter = args.iter();
962 for (i, param) in params.iter().enumerate() {
963 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
964 arrow_expr,
965 ArrowExprField::Params(i),
966 ));
967 if let Some(arg) = iter.next() {
968 self.current_value = Some(self.eval_context.eval(&arg.expr));
969 self.visit_pat(param, &mut ast_path);
970 self.current_value = None;
971 } else {
972 self.visit_pat(param, &mut ast_path);
973 }
974 }
975 {
976 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
977 arrow_expr,
978 ArrowExprField::Body,
979 ));
980 self.visit_block_stmt_or_expr(body, &mut ast_path);
981 }
982
983 {
984 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
985 arrow_expr,
986 ArrowExprField::ReturnType,
987 ));
988 self.visit_opt_ts_type_ann(return_type, &mut ast_path);
989 }
990
991 {
992 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
993 arrow_expr,
994 ArrowExprField::TypeParams,
995 ));
996 self.visit_opt_ts_type_param_decl(type_params, &mut ast_path);
997 }
998 }
999
1000 fn handle_iife_function<'ast: 'r, 'r>(
1001 &mut self,
1002 function: &'ast Function,
1003 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1004 args: &[ExprOrSpread],
1005 ) {
1006 let mut iter = args.iter();
1007 let Function {
1008 body,
1009 decorators,
1010 is_async: _,
1011 is_generator: _,
1012 params,
1013 return_type,
1014 span: _,
1015 type_params,
1016 ctxt: _,
1017 } = function;
1018 for (i, param) in params.iter().enumerate() {
1019 let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1020 function,
1021 FunctionField::Params(i),
1022 ));
1023 if let Some(arg) = iter.next() {
1024 self.current_value = Some(self.eval_context.eval(&arg.expr));
1025 self.visit_param(param, &mut ast_path);
1026 self.current_value = None;
1027 } else {
1028 self.visit_param(param, &mut ast_path);
1029 }
1030 }
1031
1032 {
1033 let mut ast_path =
1034 ast_path.with_guard(AstParentNodeRef::Function(function, FunctionField::Body));
1035
1036 self.visit_opt_block_stmt(body, &mut ast_path);
1037 }
1038
1039 {
1040 let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1041 function,
1042 FunctionField::Decorators(usize::MAX),
1043 ));
1044
1045 self.visit_decorators(decorators, &mut ast_path);
1046 }
1047
1048 {
1049 let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1050 function,
1051 FunctionField::ReturnType,
1052 ));
1053
1054 self.visit_opt_ts_type_ann(return_type, &mut ast_path);
1055 }
1056
1057 {
1058 let mut ast_path = ast_path.with_guard(AstParentNodeRef::Function(
1059 function,
1060 FunctionField::TypeParams,
1061 ));
1062
1063 self.visit_opt_ts_type_param_decl(type_params, &mut ast_path);
1064 }
1065 }
1066
1067 fn check_call_expr_for_effects<'ast: 'r, 'n, 'r>(
1068 &mut self,
1069 callee: &'n Callee,
1070 args: impl Iterator<Item = &'ast ExprOrSpread>,
1071 span: Span,
1072 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1073 n: CallOrNewExpr<'ast>,
1074 ) {
1075 let new = n.as_new().is_some();
1076 let args = args
1077 .enumerate()
1078 .map(|(i, arg)| {
1079 let mut ast_path = ast_path.with_guard(match n {
1080 CallOrNewExpr::Call(n) => AstParentNodeRef::CallExpr(n, CallExprField::Args(i)),
1081 CallOrNewExpr::New(n) => AstParentNodeRef::NewExpr(n, NewExprField::Args(i)),
1082 });
1083 if arg.spread.is_none() {
1084 let value = self.eval_context.eval(&arg.expr);
1085
1086 let block_path = match &*arg.expr {
1087 Expr::Fn(FnExpr { .. }) => {
1088 let mut path = as_parent_path(&ast_path);
1089 path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1090 path.push(AstParentKind::Expr(ExprField::Fn));
1091 path.push(AstParentKind::FnExpr(FnExprField::Function));
1092 path.push(AstParentKind::Function(FunctionField::Body));
1093 Some(path)
1094 }
1095 Expr::Arrow(ArrowExpr {
1096 body: box BlockStmtOrExpr::BlockStmt(_),
1097 ..
1098 }) => {
1099 let mut path = as_parent_path(&ast_path);
1100 path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1101 path.push(AstParentKind::Expr(ExprField::Arrow));
1102 path.push(AstParentKind::ArrowExpr(ArrowExprField::Body));
1103 path.push(AstParentKind::BlockStmtOrExpr(
1104 BlockStmtOrExprField::BlockStmt,
1105 ));
1106 Some(path)
1107 }
1108 Expr::Arrow(ArrowExpr {
1109 body: box BlockStmtOrExpr::Expr(_),
1110 ..
1111 }) => {
1112 let mut path = as_parent_path(&ast_path);
1113 path.push(AstParentKind::ExprOrSpread(ExprOrSpreadField::Expr));
1114 path.push(AstParentKind::Expr(ExprField::Arrow));
1115 path.push(AstParentKind::ArrowExpr(ArrowExprField::Body));
1116 path.push(AstParentKind::BlockStmtOrExpr(BlockStmtOrExprField::Expr));
1117 Some(path)
1118 }
1119 _ => None,
1120 };
1121 if let Some(path) = block_path {
1122 let old_effects = take(&mut self.effects);
1123 arg.visit_with_ast_path(self, &mut ast_path);
1124 let effects = replace(&mut self.effects, old_effects);
1125 EffectArg::Closure(
1126 value,
1127 Box::new(EffectsBlock {
1128 effects,
1129 range: AstPathRange::Exact(path),
1130 }),
1131 )
1132 } else {
1133 arg.visit_with_ast_path(self, &mut ast_path);
1134 EffectArg::Value(value)
1135 }
1136 } else {
1137 arg.visit_with_ast_path(self, &mut ast_path);
1138 EffectArg::Spread
1139 }
1140 })
1141 .collect();
1142
1143 match callee {
1144 Callee::Import(_) => {
1145 self.add_effect(Effect::Call {
1146 func: Box::new(JsValue::FreeVar(atom!("import"))),
1147 args,
1148 ast_path: as_parent_path(ast_path),
1149 span,
1150 in_try: is_in_try(ast_path),
1151 new,
1152 });
1153 }
1154 Callee::Expr(box expr) => {
1155 if let Expr::Member(MemberExpr { obj, prop, .. }) = unparen(expr) {
1156 let obj_value = Box::new(self.eval_context.eval(obj));
1157 let prop_value = match prop {
1158 MemberProp::Ident(i) => Box::new(i.sym.clone().into()),
1160 MemberProp::PrivateName(_) => Box::new(JsValue::unknown_empty(
1161 false,
1162 "private names in member expressions are not supported",
1163 )),
1164 MemberProp::Computed(ComputedPropName { expr, .. }) => {
1165 Box::new(self.eval_context.eval(expr))
1166 }
1167 };
1168 self.add_effect(Effect::MemberCall {
1169 obj: obj_value,
1170 prop: prop_value,
1171 args,
1172 ast_path: as_parent_path(ast_path),
1173 span,
1174 in_try: is_in_try(ast_path),
1175 new,
1176 });
1177 } else {
1178 let fn_value = Box::new(self.eval_context.eval(expr));
1179 self.add_effect(Effect::Call {
1180 func: fn_value,
1181 args,
1182 ast_path: as_parent_path(ast_path),
1183 span,
1184 in_try: is_in_try(ast_path),
1185 new,
1186 });
1187 }
1188 }
1189 Callee::Super(_) => self.add_effect(Effect::Call {
1190 func: Box::new(
1191 self.eval_context
1192 .eval(&Expr::Call(n.as_call().unwrap().clone())),
1194 ),
1195 args,
1196 ast_path: as_parent_path(ast_path),
1197 span,
1198 in_try: is_in_try(ast_path),
1199 new,
1200 }),
1201 }
1202 }
1203
1204 fn check_member_expr_for_effects<'ast: 'r, 'r>(
1205 &mut self,
1206 member_expr: &'ast MemberExpr,
1207 ast_path: &AstNodePath<AstParentNodeRef<'r>>,
1208 ) {
1209 let obj_value = Box::new(self.eval_context.eval(&member_expr.obj));
1210 let prop_value = match &member_expr.prop {
1211 MemberProp::Ident(i) => Box::new(i.sym.clone().into()),
1213 MemberProp::PrivateName(_) => {
1214 return;
1215 }
1216 MemberProp::Computed(ComputedPropName { expr, .. }) => {
1217 Box::new(self.eval_context.eval(expr))
1218 }
1219 };
1220 self.add_effect(Effect::Member {
1221 obj: obj_value,
1222 prop: prop_value,
1223 ast_path: as_parent_path(ast_path),
1224 span: member_expr.span(),
1225 in_try: is_in_try(ast_path),
1226 });
1227 }
1228
1229 fn take_return_values(&mut self) -> Box<JsValue> {
1230 let values = self.cur_fn_return_values.take().unwrap();
1231
1232 Box::new(match values.len() {
1233 0 => JsValue::FreeVar(atom!("undefined")),
1234 1 => values.into_iter().next().unwrap(),
1235 _ => JsValue::alternatives(values),
1236 })
1237 }
1238
1239 fn end_early_return_block(&mut self) -> bool {
1242 let mut always_returns = false;
1243 while let Some(early_return) = self.early_return_stack.pop() {
1244 match early_return {
1245 EarlyReturn::Always {
1246 prev_effects,
1247 start_ast_path,
1248 } => {
1249 self.effects = prev_effects;
1250 self.effects.push(Effect::Unreachable { start_ast_path });
1251 always_returns = true;
1252 }
1253 EarlyReturn::Conditional {
1254 prev_effects,
1255 start_ast_path,
1256 condition,
1257 then,
1258 r#else,
1259 condition_ast_path,
1260 span,
1261 in_try,
1262 early_return_condition_value,
1263 } => {
1264 let block = Box::new(EffectsBlock {
1265 effects: take(&mut self.effects),
1266 range: AstPathRange::StartAfter(start_ast_path),
1267 });
1268 self.effects = prev_effects;
1269 let kind = match (then, r#else, early_return_condition_value) {
1270 (None, None, false) => ConditionalKind::If { then: block },
1271 (None, None, true) => ConditionalKind::IfElseMultiple {
1272 then: vec![block],
1273 r#else: vec![],
1274 },
1275 (Some(then), None, false) => ConditionalKind::IfElseMultiple {
1276 then: vec![then, block],
1277 r#else: vec![],
1278 },
1279 (Some(then), None, true) => ConditionalKind::IfElse {
1280 then,
1281 r#else: block,
1282 },
1283 (Some(then), Some(r#else), false) => ConditionalKind::IfElseMultiple {
1284 then: vec![then, block],
1285 r#else: vec![r#else],
1286 },
1287 (Some(then), Some(r#else), true) => ConditionalKind::IfElseMultiple {
1288 then: vec![then],
1289 r#else: vec![r#else, block],
1290 },
1291 (None, Some(r#else), false) => ConditionalKind::IfElse {
1292 then: block,
1293 r#else,
1294 },
1295 (None, Some(r#else), true) => ConditionalKind::IfElseMultiple {
1296 then: vec![],
1297 r#else: vec![r#else, block],
1298 },
1299 };
1300 self.effects.push(Effect::Conditional {
1301 condition,
1302 kind: Box::new(kind),
1303 ast_path: condition_ast_path,
1304 span,
1305 in_try,
1306 })
1307 }
1308 }
1309 }
1310 always_returns
1311 }
1312}
1313
1314impl VisitAstPath for Analyzer<'_> {
1315 fn visit_assign_expr<'ast: 'r, 'r>(
1316 &mut self,
1317 n: &'ast AssignExpr,
1318 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1319 ) {
1320 {
1321 let mut ast_path =
1322 ast_path.with_guard(AstParentNodeRef::AssignExpr(n, AssignExprField::Left));
1323
1324 match n.op {
1325 AssignOp::Assign => {
1326 self.current_value = Some(self.eval_context.eval(&n.right));
1327 n.left.visit_children_with_ast_path(self, &mut ast_path);
1328 self.current_value = None;
1329 }
1330
1331 _ => {
1332 if let Some(key) = n.left.as_ident() {
1333 let value = match n.op {
1334 AssignOp::AndAssign | AssignOp::OrAssign | AssignOp::NullishAssign => {
1335 let right = self.eval_context.eval(&n.right);
1336 Some(right)
1339 }
1340 AssignOp::AddAssign => {
1341 let left = self.eval_context.eval(&Expr::Ident(key.clone().into()));
1342
1343 let right = self.eval_context.eval(&n.right);
1344
1345 Some(JsValue::add(vec![left, right]))
1346 }
1347 _ => Some(JsValue::unknown_empty(true, "unsupported assign operation")),
1348 };
1349 if let Some(value) = value {
1350 self.current_value = Some(value);
1355 n.left.visit_children_with_ast_path(self, &mut ast_path);
1356 self.current_value = None;
1357 }
1358 }
1359
1360 if n.left.as_ident().is_none() {
1361 n.left.visit_children_with_ast_path(self, &mut ast_path);
1362 }
1363 }
1364 }
1365 }
1366
1367 {
1368 let mut ast_path =
1369 ast_path.with_guard(AstParentNodeRef::AssignExpr(n, AssignExprField::Right));
1370 self.visit_expr(&n.right, &mut ast_path);
1371 }
1372 }
1373
1374 fn visit_update_expr<'ast: 'r, 'r>(
1375 &mut self,
1376 n: &'ast UpdateExpr,
1377 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1378 ) {
1379 if let Some(key) = n.arg.as_ident() {
1380 self.add_value(
1381 key.to_id(),
1382 JsValue::unknown_empty(true, "updated with update expression"),
1383 );
1384 }
1385
1386 let mut ast_path =
1387 ast_path.with_guard(AstParentNodeRef::UpdateExpr(n, UpdateExprField::Arg));
1388 self.visit_expr(&n.arg, &mut ast_path);
1389 }
1390
1391 fn visit_call_expr<'ast: 'r, 'r>(
1392 &mut self,
1393 n: &'ast CallExpr,
1394 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1395 ) {
1396 if let Callee::Expr(callee) = &n.callee
1398 && n.args.len() == 1
1399 && let Some(require_var_id) = extract_var_from_umd_factory(callee, &n.args)
1400 {
1401 self.add_value(
1402 require_var_id,
1403 JsValue::unknown_if(
1404 self.eval_context
1405 .imports
1406 .get_attributes(n.callee.span())
1407 .ignore,
1408 JsValue::WellKnownFunction(WellKnownFunctionKind::Require),
1409 true,
1410 "ignored require",
1411 ),
1412 );
1413 }
1414
1415 if self.check_iife(n, ast_path) {
1416 return;
1417 }
1418
1419 {
1421 let mut ast_path =
1422 ast_path.with_guard(AstParentNodeRef::CallExpr(n, CallExprField::Callee));
1423 n.callee.visit_with_ast_path(self, &mut ast_path);
1424 }
1425
1426 self.check_call_expr_for_effects(
1427 &n.callee,
1428 n.args.iter(),
1429 n.span(),
1430 ast_path,
1431 CallOrNewExpr::Call(n),
1432 );
1433 }
1434
1435 fn visit_new_expr<'ast: 'r, 'r>(
1436 &mut self,
1437 n: &'ast NewExpr,
1438 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1439 ) {
1440 {
1441 let mut ast_path =
1442 ast_path.with_guard(AstParentNodeRef::NewExpr(n, NewExprField::Callee));
1443 n.callee.visit_with_ast_path(self, &mut ast_path);
1444 }
1445
1446 self.check_call_expr_for_effects(
1447 &Callee::Expr(n.callee.clone()),
1448 n.args.iter().flatten(),
1449 n.span(),
1450 ast_path,
1451 CallOrNewExpr::New(n),
1452 );
1453 }
1454
1455 fn visit_member_expr<'ast: 'r, 'r>(
1456 &mut self,
1457 member_expr: &'ast MemberExpr,
1458 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1459 ) {
1460 self.check_member_expr_for_effects(member_expr, ast_path);
1461 member_expr.visit_children_with_ast_path(self, ast_path);
1462 }
1463
1464 fn visit_expr<'ast: 'r, 'r>(
1465 &mut self,
1466 n: &'ast Expr,
1467 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1468 ) {
1469 let old = self.var_decl_kind;
1470 self.var_decl_kind = None;
1471 n.visit_children_with_ast_path(self, ast_path);
1472 self.var_decl_kind = old;
1473 }
1474
1475 fn visit_params<'ast: 'r, 'r>(
1476 &mut self,
1477 n: &'ast [Param],
1478 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1479 ) {
1480 let value = self.current_value.take();
1481 for (index, p) in n.iter().enumerate() {
1482 self.current_value = Some(JsValue::Argument(self.cur_fn_ident, index));
1483 let mut ast_path = ast_path.with_index_guard(index);
1484 p.visit_with_ast_path(self, &mut ast_path);
1485 }
1486 self.current_value = value;
1487 }
1488
1489 fn visit_param<'ast: 'r, 'r>(
1490 &mut self,
1491 n: &'ast Param,
1492 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1493 ) {
1494 let old = self.var_decl_kind;
1495 let Param {
1496 decorators,
1497 pat,
1498 span: _,
1499 } = n;
1500 self.var_decl_kind = None;
1501 let value = self.current_value.take();
1502 {
1503 let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param(
1504 n,
1505 ParamField::Decorators(usize::MAX),
1506 ));
1507 self.visit_decorators(decorators, &mut ast_path);
1508 }
1509 self.current_value = value;
1510 {
1511 let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param(n, ParamField::Pat));
1512 self.visit_pat(pat, &mut ast_path);
1513 }
1514 self.current_value = None;
1515 self.var_decl_kind = old;
1516 }
1517
1518 fn visit_fn_decl<'ast: 'r, 'r>(
1519 &mut self,
1520 decl: &'ast FnDecl,
1521 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1522 ) {
1523 let old = self
1524 .cur_fn_return_values
1525 .replace(get_fn_init_return_vals(decl.function.body.as_ref()));
1526 let old_ident = self.cur_fn_ident;
1527 self.cur_fn_ident = decl.function.span.lo.0;
1528 decl.visit_children_with_ast_path(self, ast_path);
1529 let return_value = self.take_return_values();
1530 self.hoisted_effects.append(&mut self.effects);
1531
1532 self.add_value(
1533 decl.ident.to_id(),
1534 JsValue::function(self.cur_fn_ident, return_value),
1535 );
1536
1537 self.cur_fn_ident = old_ident;
1538 self.cur_fn_return_values = old;
1539 }
1540
1541 fn visit_fn_expr<'ast: 'r, 'r>(
1542 &mut self,
1543 expr: &'ast FnExpr,
1544 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1545 ) {
1546 let old = self
1547 .cur_fn_return_values
1548 .replace(get_fn_init_return_vals(expr.function.body.as_ref()));
1549 let old_ident = self.cur_fn_ident;
1550 self.cur_fn_ident = expr.function.span.lo.0;
1551 expr.visit_children_with_ast_path(self, ast_path);
1552 let return_value = self.take_return_values();
1553
1554 if let Some(ident) = &expr.ident {
1555 self.add_value(
1556 ident.to_id(),
1557 JsValue::function(self.cur_fn_ident, return_value),
1558 );
1559 } else {
1560 self.add_value(
1561 (
1562 format!("*anonymous function {}*", expr.function.span.lo.0).into(),
1563 SyntaxContext::empty(),
1564 ),
1565 JsValue::function(self.cur_fn_ident, return_value),
1566 );
1567 }
1568
1569 self.cur_fn_ident = old_ident;
1570 self.cur_fn_return_values = old;
1571 }
1572
1573 fn visit_arrow_expr<'ast: 'r, 'r>(
1574 &mut self,
1575 expr: &'ast ArrowExpr,
1576 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1577 ) {
1578 let old_return_values = replace(
1579 &mut self.cur_fn_return_values,
1580 expr.body
1581 .as_block_stmt()
1582 .map(|block| get_fn_init_return_vals(Some(block))),
1583 );
1584 let old_ident = self.cur_fn_ident;
1585 self.cur_fn_ident = expr.span.lo.0;
1586
1587 let value = self.current_value.take();
1588 for (index, p) in expr.params.iter().enumerate() {
1589 self.current_value = Some(JsValue::Argument(self.cur_fn_ident, index));
1590 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrowExpr(
1591 expr,
1592 ArrowExprField::Params(index),
1593 ));
1594 p.visit_with_ast_path(self, &mut ast_path);
1595 }
1596 self.current_value = value;
1597
1598 {
1599 let mut ast_path =
1600 ast_path.with_guard(AstParentNodeRef::ArrowExpr(expr, ArrowExprField::Body));
1601 expr.body.visit_with_ast_path(self, &mut ast_path);
1602 }
1603
1604 let return_value = match &*expr.body {
1605 BlockStmtOrExpr::BlockStmt(_) => self.take_return_values(),
1606 BlockStmtOrExpr::Expr(inner_expr) => Box::new(self.eval_context.eval(inner_expr)),
1607 };
1608
1609 self.add_value(
1610 (
1611 format!("*arrow function {}*", expr.span.lo.0).into(),
1612 SyntaxContext::empty(),
1613 ),
1614 JsValue::function(self.cur_fn_ident, return_value),
1615 );
1616
1617 self.cur_fn_ident = old_ident;
1618 self.cur_fn_return_values = old_return_values;
1619 }
1620
1621 fn visit_class_decl<'ast: 'r, 'r>(
1622 &mut self,
1623 decl: &'ast ClassDecl,
1624 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1625 ) {
1626 self.add_value_from_expr(
1627 decl.ident.to_id(),
1628 &Expr::Class(ClassExpr {
1630 ident: Some(decl.ident.clone()),
1631 class: decl.class.clone(),
1632 }),
1633 );
1634 decl.visit_children_with_ast_path(self, ast_path);
1635 }
1636
1637 fn visit_var_decl<'ast: 'r, 'r>(
1638 &mut self,
1639 n: &'ast VarDecl,
1640 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1641 ) {
1642 let old = self.var_decl_kind;
1643 self.var_decl_kind = Some(n.kind);
1644 n.visit_children_with_ast_path(self, ast_path);
1645 self.var_decl_kind = old;
1646 }
1647
1648 fn visit_var_declarator<'ast: 'r, 'r>(
1649 &mut self,
1650 n: &'ast VarDeclarator,
1651 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1652 ) {
1653 if self.var_decl_kind.is_some()
1654 && let Some(init) = &n.init
1655 {
1656 let should_include_undefined = matches!(self.var_decl_kind, Some(VarDeclKind::Var))
1667 && is_lexically_block_scope(ast_path);
1668 let init_value = self.eval_context.eval(init);
1669 self.current_value = Some(if should_include_undefined {
1670 JsValue::alternatives(vec![
1671 init_value,
1672 JsValue::Constant(ConstantValue::Undefined),
1673 ])
1674 } else {
1675 init_value
1676 });
1677 }
1678 {
1679 let mut ast_path =
1680 ast_path.with_guard(AstParentNodeRef::VarDeclarator(n, VarDeclaratorField::Name));
1681
1682 self.visit_pat(&n.name, &mut ast_path);
1683 }
1684 self.current_value = None;
1685 {
1686 let mut ast_path =
1687 ast_path.with_guard(AstParentNodeRef::VarDeclarator(n, VarDeclaratorField::Init));
1688
1689 self.visit_opt_expr(&n.init, &mut ast_path);
1690 }
1691 }
1692
1693 fn visit_for_of_stmt<'ast: 'r, 'r>(
1694 &mut self,
1695 n: &'ast ForOfStmt,
1696 ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
1697 ) {
1698 {
1699 let mut ast_path =
1700 ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Right));
1701 self.current_value = None;
1702 self.visit_expr(&n.right, &mut ast_path);
1703 }
1704
1705 let array = self.eval_context.eval(&n.right);
1706
1707 {
1708 let mut ast_path =
1709 ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Left));
1710 self.current_value = Some(JsValue::iterated(Box::new(array)));
1711 self.visit_for_head(&n.left, &mut ast_path);
1712 }
1713
1714 let mut ast_path =
1715 ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Body));
1716
1717 self.visit_stmt(&n.body, &mut ast_path);
1718 }
1719
1720 fn visit_simple_assign_target<'ast: 'r, 'r>(
1721 &mut self,
1722 n: &'ast SimpleAssignTarget,
1723 ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
1724 ) {
1725 let value = self.current_value.take();
1726 if let SimpleAssignTarget::Ident(i) = n {
1727 n.visit_children_with_ast_path(self, ast_path);
1728
1729 self.add_value(
1730 i.to_id(),
1731 value.unwrap_or_else(|| {
1732 JsValue::unknown(JsValue::Variable(i.to_id()), false, "pattern without value")
1733 }),
1734 );
1735 return;
1736 }
1737
1738 n.visit_children_with_ast_path(self, ast_path);
1739 }
1740
1741 fn visit_pat<'ast: 'r, 'r>(
1742 &mut self,
1743 pat: &'ast Pat,
1744 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1745 ) {
1746 let value = self.current_value.take();
1747 match pat {
1748 Pat::Ident(i) => {
1749 self.add_value(
1750 i.to_id(),
1751 value.unwrap_or_else(|| {
1752 JsValue::unknown(
1753 JsValue::Variable(i.to_id()),
1754 false,
1755 "pattern without value",
1756 )
1757 }),
1758 );
1759 }
1760
1761 Pat::Array(arr) => {
1762 match &value {
1763 Some(JsValue::Array { items, .. }) => {
1764 let mut ast_path =
1765 ast_path.with_guard(AstParentNodeRef::Pat(pat, PatField::Array));
1766 for (idx, elem) in arr.elems.iter().enumerate() {
1767 self.current_value = items.get(idx).cloned();
1768 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrayPat(
1769 arr,
1770 ArrayPatField::Elems(idx),
1771 ));
1772 elem.visit_with_ast_path(self, &mut ast_path);
1773 }
1774
1775 return;
1777 }
1778
1779 Some(value) => {
1780 let mut ast_path =
1781 ast_path.with_guard(AstParentNodeRef::Pat(pat, PatField::Array));
1782 for (idx, elem) in arr.elems.iter().enumerate() {
1783 self.current_value = Some(JsValue::member(
1784 Box::new(value.clone()),
1785 Box::new(JsValue::Constant(ConstantValue::Num(ConstantNumber(
1786 idx as f64,
1787 )))),
1788 ));
1789 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ArrayPat(
1790 arr,
1791 ArrayPatField::Elems(idx),
1792 ));
1793 elem.visit_with_ast_path(self, &mut ast_path);
1794 }
1795 return;
1797 }
1798
1799 None => {}
1800 }
1801 }
1802
1803 Pat::Object(obj) => {
1804 let value =
1805 value.unwrap_or_else(|| JsValue::unknown_empty(false, "pattern without value"));
1806
1807 self.visit_pat_with_value(pat, obj, value, ast_path);
1808
1809 return;
1811 }
1812
1813 _ => {}
1814 }
1815 pat.visit_children_with_ast_path(self, ast_path);
1816 }
1817
1818 fn visit_return_stmt<'ast: 'r, 'r>(
1819 &mut self,
1820 stmt: &'ast ReturnStmt,
1821 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1822 ) {
1823 stmt.visit_children_with_ast_path(self, ast_path);
1824
1825 if let Some(values) = &mut self.cur_fn_return_values {
1826 let return_value = stmt
1827 .arg
1828 .as_deref()
1829 .map(|e| self.eval_context.eval(e))
1830 .unwrap_or(JsValue::FreeVar(atom!("undefined")));
1831
1832 values.push(return_value);
1833 }
1834
1835 self.early_return_stack.push(EarlyReturn::Always {
1836 prev_effects: take(&mut self.effects),
1837 start_ast_path: as_parent_path(ast_path),
1838 });
1839 }
1840
1841 fn visit_ident<'ast: 'r, 'r>(
1842 &mut self,
1843 ident: &'ast Ident,
1844 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1845 ) {
1846 if !(matches!(
1847 ast_path.last(),
1848 Some(AstParentNodeRef::Expr(_, ExprField::Ident))
1849 | Some(AstParentNodeRef::Prop(_, PropField::Shorthand))
1850 ) || matches!(
1851 ast_path.get(ast_path.len() - 2),
1852 Some(AstParentNodeRef::SimpleAssignTarget(
1853 _,
1854 SimpleAssignTargetField::Ident,
1855 ))
1856 )) {
1857 return;
1858 }
1859
1860 if let Some((esm_reference_index, export)) =
1861 self.eval_context.imports.get_binding(&ident.to_id())
1862 {
1863 if export.is_none()
1864 && !self
1865 .eval_context
1866 .imports
1867 .should_import_all(esm_reference_index)
1868 {
1869 if let Some(AstParentNodeRef::MemberExpr(member, MemberExprField::Obj)) =
1874 ast_path.get(ast_path.len() - 2)
1875 {
1876 let is_lhs = matches!(
1878 ast_path.get(ast_path.len() - 3),
1879 Some(AstParentNodeRef::SimpleAssignTarget(
1880 _,
1881 SimpleAssignTargetField::Member
1882 ))
1883 );
1884
1885 if !is_lhs
1886 && let Some(prop) = self.eval_context.eval_member_prop(&member.prop)
1887 && let Some(prop_str) = prop.as_str()
1888 {
1889 self.add_effect(Effect::ImportedBinding {
1892 esm_reference_index,
1893 export: Some(prop_str.into()),
1894 ast_path: as_parent_path_skip(ast_path, 1),
1895 span: member.span(),
1896 in_try: is_in_try(ast_path),
1897 });
1898 return;
1899 }
1900 }
1901 }
1902
1903 self.add_effect(Effect::ImportedBinding {
1904 esm_reference_index,
1905 export,
1906 ast_path: as_parent_path(ast_path),
1907 span: ident.span(),
1908 in_try: is_in_try(ast_path),
1909 })
1910 } else if is_unresolved(ident, self.eval_context.unresolved_mark)
1911 || self.eval_context.force_free_values.contains(&ident.to_id())
1912 {
1913 self.add_effect(Effect::FreeVar {
1914 var: Box::new(JsValue::FreeVar(ident.sym.clone())),
1915 ast_path: as_parent_path(ast_path),
1916 span: ident.span(),
1917 in_try: is_in_try(ast_path),
1918 })
1919 }
1920 }
1921
1922 fn visit_this_expr<'ast: 'r, 'r>(
1923 &mut self,
1924 node: &'ast ThisExpr,
1925 ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
1926 ) {
1927 if ast_path.iter().rev().any(|node| {
1930 matches!(
1931 node.kind(),
1932 AstParentKind::MethodProp(MethodPropField::Function)
1933 | AstParentKind::GetterProp(GetterPropField::Body)
1934 | AstParentKind::SetterProp(SetterPropField::Body)
1935 | AstParentKind::Constructor(ConstructorField::Body)
1936 | AstParentKind::ClassMethod(ClassMethodField::Function)
1937 | AstParentKind::ClassDecl(ClassDeclField::Class)
1938 | AstParentKind::ClassExpr(ClassExprField::Class)
1939 | AstParentKind::Function(FunctionField::Body)
1940 )
1941 }) {
1942 return;
1944 }
1945 self.add_effect(Effect::FreeVar {
1947 var: Box::new(JsValue::FreeVar(atom!("this"))),
1948 ast_path: as_parent_path(ast_path),
1949 span: node.span(),
1950 in_try: is_in_try(ast_path),
1951 })
1952 }
1953
1954 fn visit_meta_prop_expr<'ast: 'r, 'r>(
1955 &mut self,
1956 expr: &'ast MetaPropExpr,
1957 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1958 ) {
1959 if expr.kind == MetaPropKind::ImportMeta {
1960 self.add_effect(Effect::ImportMeta {
1963 span: expr.span,
1964 ast_path: as_parent_path(ast_path),
1965 in_try: is_in_try(ast_path),
1966 })
1967 }
1968 }
1969
1970 fn visit_program<'ast: 'r, 'r>(
1971 &mut self,
1972 program: &'ast Program,
1973 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1974 ) {
1975 self.effects = take(&mut self.data.effects);
1976 program.visit_children_with_ast_path(self, ast_path);
1977 self.end_early_return_block();
1978 self.effects.append(&mut self.hoisted_effects);
1979 self.data.effects = take(&mut self.effects);
1980 }
1981
1982 fn visit_cond_expr<'ast: 'r, 'r>(
1983 &mut self,
1984 expr: &'ast CondExpr,
1985 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
1986 ) {
1987 {
1988 let mut ast_path =
1989 ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Test));
1990 expr.test.visit_with_ast_path(self, &mut ast_path);
1991 }
1992
1993 let prev_effects = take(&mut self.effects);
1994 let then = {
1995 let mut ast_path =
1996 ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Cons));
1997 expr.cons.visit_with_ast_path(self, &mut ast_path);
1998 Box::new(EffectsBlock {
1999 effects: take(&mut self.effects),
2000 range: AstPathRange::Exact(as_parent_path(&ast_path)),
2001 })
2002 };
2003 let r#else = {
2004 let mut ast_path =
2005 ast_path.with_guard(AstParentNodeRef::CondExpr(expr, CondExprField::Alt));
2006 expr.alt.visit_with_ast_path(self, &mut ast_path);
2007 Box::new(EffectsBlock {
2008 effects: take(&mut self.effects),
2009 range: AstPathRange::Exact(as_parent_path(&ast_path)),
2010 })
2011 };
2012 self.effects = prev_effects;
2013
2014 self.add_conditional_effect(
2015 &expr.test,
2016 ast_path,
2017 AstParentKind::CondExpr(CondExprField::Test),
2018 expr.span(),
2019 ConditionalKind::Ternary { then, r#else },
2020 );
2021 }
2022
2023 fn visit_if_stmt<'ast: 'r, 'r>(
2024 &mut self,
2025 stmt: &'ast IfStmt,
2026 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2027 ) {
2028 {
2029 let mut ast_path =
2030 ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Test));
2031 stmt.test.visit_with_ast_path(self, &mut ast_path);
2032 }
2033 let prev_effects = take(&mut self.effects);
2034 let prev_early_return_stack = take(&mut self.early_return_stack);
2035 let then_returning;
2036 let then = {
2037 let mut ast_path =
2038 ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Cons));
2039 stmt.cons.visit_with_ast_path(self, &mut ast_path);
2040 then_returning = self.end_early_return_block();
2041 Box::new(EffectsBlock {
2042 effects: take(&mut self.effects),
2043 range: AstPathRange::Exact(as_parent_path(&ast_path)),
2044 })
2045 };
2046 let mut else_returning = false;
2047 let r#else = stmt.alt.as_ref().map(|alt| {
2048 let mut ast_path =
2049 ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Alt));
2050 alt.visit_with_ast_path(self, &mut ast_path);
2051 else_returning = self.end_early_return_block();
2052 Box::new(EffectsBlock {
2053 effects: take(&mut self.effects),
2054 range: AstPathRange::Exact(as_parent_path(&ast_path)),
2055 })
2056 });
2057 self.early_return_stack = prev_early_return_stack;
2058 self.effects = prev_effects;
2059 self.add_conditional_if_effect_with_early_return(
2060 &stmt.test,
2061 ast_path,
2062 AstParentKind::IfStmt(IfStmtField::Test),
2063 stmt.span(),
2064 (!then.is_empty()).then_some(then),
2065 r#else.and_then(|block| (!block.is_empty()).then_some(block)),
2066 then_returning,
2067 else_returning,
2068 );
2069 }
2070
2071 fn visit_try_stmt<'ast: 'r, 'r>(
2072 &mut self,
2073 stmt: &'ast TryStmt,
2074 ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2075 ) {
2076 let prev_effects = take(&mut self.effects);
2077 let prev_early_return_stack = take(&mut self.early_return_stack);
2078 let mut block = {
2079 let mut ast_path =
2080 ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Block));
2081 stmt.block.visit_with_ast_path(self, &mut ast_path);
2082 self.end_early_return_block();
2083 take(&mut self.effects)
2084 };
2085 let mut handler = if let Some(handler) = stmt.handler.as_ref() {
2086 let mut ast_path =
2087 ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Handler));
2088 handler.visit_with_ast_path(self, &mut ast_path);
2089 self.end_early_return_block();
2090 take(&mut self.effects)
2091 } else {
2092 vec![]
2093 };
2094 self.early_return_stack = prev_early_return_stack;
2095 self.effects = prev_effects;
2096 self.effects.append(&mut block);
2097 self.effects.append(&mut handler);
2098 if let Some(finalizer) = stmt.finalizer.as_ref() {
2099 let mut ast_path =
2100 ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Finalizer));
2101 finalizer.visit_with_ast_path(self, &mut ast_path);
2102 };
2103 }
2104
2105 fn visit_switch_case<'ast: 'r, 'r>(
2106 &mut self,
2107 case: &'ast SwitchCase,
2108 ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2109 ) {
2110 let prev_effects = take(&mut self.effects);
2111 let prev_early_return_stack = take(&mut self.early_return_stack);
2112 case.visit_children_with_ast_path(self, ast_path);
2113 self.end_early_return_block();
2114 let mut effects = take(&mut self.effects);
2115 self.early_return_stack = prev_early_return_stack;
2116 self.effects = prev_effects;
2117 self.effects.append(&mut effects);
2118 }
2119
2120 fn visit_block_stmt<'ast: 'r, 'r>(
2121 &mut self,
2122 n: &'ast BlockStmt,
2123 ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2124 ) {
2125 let block_type = if ast_path.len() < 2 {
2126 Some(false)
2127 } else if matches!(
2128 &ast_path[ast_path.len() - 2..],
2129 [
2130 AstParentNodeRef::IfStmt(_, IfStmtField::Cons),
2131 AstParentNodeRef::Stmt(_, StmtField::Block)
2132 ] | [
2133 AstParentNodeRef::IfStmt(_, IfStmtField::Alt),
2134 AstParentNodeRef::Stmt(_, StmtField::Block)
2135 ] | [_, AstParentNodeRef::TryStmt(_, TryStmtField::Block,)]
2136 | [
2137 AstParentNodeRef::TryStmt(_, TryStmtField::Handler),
2138 AstParentNodeRef::CatchClause(_, CatchClauseField::Body)
2139 ]
2140 | [
2141 AstParentNodeRef::LabeledStmt(_, LabeledStmtField::Body),
2142 AstParentNodeRef::Stmt(_, StmtField::Block)
2143 ]
2144 ) {
2145 None
2146 } else if matches!(
2147 &ast_path[ast_path.len() - 2..],
2148 [_, AstParentNodeRef::Function(_, FunctionField::Body)]
2149 | [
2150 AstParentNodeRef::ArrowExpr(_, ArrowExprField::Body),
2151 AstParentNodeRef::BlockStmtOrExpr(_, BlockStmtOrExprField::BlockStmt)
2152 ]
2153 | [_, AstParentNodeRef::GetterProp(_, GetterPropField::Body)]
2154 | [_, AstParentNodeRef::SetterProp(_, SetterPropField::Body)]
2155 | [_, AstParentNodeRef::Constructor(_, ConstructorField::Body)]
2156 ) {
2157 Some(true)
2158 } else {
2159 Some(false)
2160 };
2161 match block_type {
2162 Some(true) => {
2163 let early_return_stack = take(&mut self.early_return_stack);
2164 let mut effects = take(&mut self.effects);
2165 let hoisted_effects = take(&mut self.hoisted_effects);
2166 n.visit_children_with_ast_path(self, ast_path);
2167 self.end_early_return_block();
2168 self.effects.append(&mut self.hoisted_effects);
2169 effects.append(&mut self.effects);
2170 self.hoisted_effects = hoisted_effects;
2171 self.effects = effects;
2172 self.early_return_stack = early_return_stack;
2173 }
2174 Some(false) => {
2175 n.visit_children_with_ast_path(self, ast_path);
2176 if self.end_early_return_block() {
2177 self.early_return_stack.push(EarlyReturn::Always {
2178 prev_effects: take(&mut self.effects),
2179 start_ast_path: as_parent_path(ast_path),
2180 });
2181 }
2182 }
2183 None => {
2184 n.visit_children_with_ast_path(self, ast_path);
2185 }
2186 }
2187 }
2188
2189 fn visit_unary_expr<'ast: 'r, 'r>(
2190 &mut self,
2191 n: &'ast UnaryExpr,
2192 ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
2193 ) {
2194 if n.op == UnaryOp::TypeOf {
2195 let arg_value = Box::new(self.eval_context.eval(&n.arg));
2196 self.add_effect(Effect::TypeOf {
2197 arg: arg_value,
2198 ast_path: as_parent_path(ast_path),
2199 span: n.span(),
2200 });
2201 }
2202
2203 n.visit_children_with_ast_path(self, ast_path);
2204 }
2205
2206 fn visit_labeled_stmt<'ast: 'r, 'r>(
2207 &mut self,
2208 stmt: &'ast LabeledStmt,
2209 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2210 ) {
2211 let mut prev_effects = take(&mut self.effects);
2212 let prev_early_return_stack = take(&mut self.early_return_stack);
2213
2214 stmt.visit_children_with_ast_path(self, ast_path);
2215
2216 self.end_early_return_block();
2217
2218 let effects = take(&mut self.effects);
2219
2220 prev_effects.push(Effect::Conditional {
2221 condition: Box::new(JsValue::unknown_empty(true, "labeled statement")),
2222 kind: Box::new(ConditionalKind::Labeled {
2223 body: Box::new(EffectsBlock {
2224 effects,
2225 range: AstPathRange::Exact(as_parent_path_with(
2226 ast_path,
2227 AstParentKind::LabeledStmt(LabeledStmtField::Body),
2228 )),
2229 }),
2230 }),
2231 ast_path: as_parent_path(ast_path),
2232 span: stmt.span,
2233 in_try: is_in_try(ast_path),
2234 });
2235
2236 self.effects = prev_effects;
2237 self.early_return_stack = prev_early_return_stack;
2238 }
2239}
2240
2241fn is_lexically_block_scope(ast_path: &mut AstNodePath<AstParentNodeRef>) -> bool {
2242 let mut iter = ast_path.iter().rev().peekable();
2243
2244 while let Some(cur) = iter.next() {
2245 if matches!(cur.kind(), AstParentKind::BlockStmt(..)) {
2247 if let Some(next) = iter.peek() {
2248 return !matches!(next.kind(), AstParentKind::Function(FunctionField::Body));
2249 }
2250 return false;
2251 }
2252 }
2253
2254 false
2256}
2257
2258impl Analyzer<'_> {
2259 fn add_conditional_if_effect_with_early_return(
2260 &mut self,
2261 test: &Expr,
2262 ast_path: &AstNodePath<AstParentNodeRef<'_>>,
2263 condition_ast_kind: AstParentKind,
2264 span: Span,
2265 then: Option<Box<EffectsBlock>>,
2266 r#else: Option<Box<EffectsBlock>>,
2267 early_return_when_true: bool,
2268 early_return_when_false: bool,
2269 ) {
2270 if then.is_none() && r#else.is_none() && !early_return_when_false && !early_return_when_true
2271 {
2272 return;
2273 }
2274 let condition = Box::new(self.eval_context.eval(test));
2275 if condition.is_unknown() {
2276 if let Some(mut then) = then {
2277 self.effects.append(&mut then.effects);
2278 }
2279 if let Some(mut r#else) = r#else {
2280 self.effects.append(&mut r#else.effects);
2281 }
2282 return;
2283 }
2284 match (early_return_when_true, early_return_when_false) {
2285 (true, false) => {
2286 self.early_return_stack.push(EarlyReturn::Conditional {
2287 prev_effects: take(&mut self.effects),
2288 start_ast_path: as_parent_path(ast_path),
2289 condition,
2290 then,
2291 r#else,
2292 condition_ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2293 span,
2294 in_try: is_in_try(ast_path),
2295 early_return_condition_value: true,
2296 });
2297 }
2298 (false, true) => {
2299 self.early_return_stack.push(EarlyReturn::Conditional {
2300 prev_effects: take(&mut self.effects),
2301 start_ast_path: as_parent_path(ast_path),
2302 condition,
2303 then,
2304 r#else,
2305 condition_ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2306 span,
2307 in_try: is_in_try(ast_path),
2308 early_return_condition_value: false,
2309 });
2310 }
2311 (false, false) | (true, true) => {
2312 let kind = match (then, r#else) {
2313 (Some(then), Some(r#else)) => ConditionalKind::IfElse { then, r#else },
2314 (Some(then), None) => ConditionalKind::If { then },
2315 (None, Some(r#else)) => ConditionalKind::Else { r#else },
2316 (None, None) => unreachable!(),
2317 };
2318 self.add_effect(Effect::Conditional {
2319 condition,
2320 kind: Box::new(kind),
2321 ast_path: as_parent_path_with(ast_path, condition_ast_kind),
2322 span,
2323 in_try: is_in_try(ast_path),
2324 });
2325 if early_return_when_false && early_return_when_true {
2326 self.early_return_stack.push(EarlyReturn::Always {
2327 prev_effects: take(&mut self.effects),
2328 start_ast_path: as_parent_path(ast_path),
2329 });
2330 }
2331 }
2332 }
2333 }
2334
2335 fn add_conditional_effect(
2336 &mut self,
2337 test: &Expr,
2338 ast_path: &AstNodePath<AstParentNodeRef<'_>>,
2339 ast_kind: AstParentKind,
2340 span: Span,
2341 mut cond_kind: ConditionalKind,
2342 ) {
2343 let condition = Box::new(self.eval_context.eval(test));
2344 if condition.is_unknown() {
2345 match &mut cond_kind {
2346 ConditionalKind::If { then } => {
2347 self.effects.append(&mut then.effects);
2348 }
2349 ConditionalKind::Else { r#else } => {
2350 self.effects.append(&mut r#else.effects);
2351 }
2352 ConditionalKind::IfElse { then, r#else }
2353 | ConditionalKind::Ternary { then, r#else } => {
2354 self.effects.append(&mut then.effects);
2355 self.effects.append(&mut r#else.effects);
2356 }
2357 ConditionalKind::IfElseMultiple { then, r#else } => {
2358 for block in then {
2359 self.effects.append(&mut block.effects);
2360 }
2361 for block in r#else {
2362 self.effects.append(&mut block.effects);
2363 }
2364 }
2365 ConditionalKind::And { expr }
2366 | ConditionalKind::Or { expr }
2367 | ConditionalKind::NullishCoalescing { expr } => {
2368 self.effects.append(&mut expr.effects);
2369 }
2370 ConditionalKind::Labeled { body } => {
2371 self.effects.append(&mut body.effects);
2372 }
2373 }
2374 } else {
2375 self.add_effect(Effect::Conditional {
2376 condition,
2377 kind: Box::new(cond_kind),
2378 ast_path: as_parent_path_with(ast_path, ast_kind),
2379 span,
2380 in_try: is_in_try(ast_path),
2381 });
2382 }
2383 }
2384
2385 fn visit_pat_with_value<'ast: 'r, 'r>(
2386 &mut self,
2387 pat: &'ast Pat,
2388 obj: &'ast ObjectPat,
2389 current_value: JsValue,
2390 ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
2391 ) {
2392 let mut ast_path = ast_path.with_guard(AstParentNodeRef::Pat(pat, PatField::Object));
2393 for (i, prop) in obj.props.iter().enumerate() {
2394 let mut ast_path =
2395 ast_path.with_guard(AstParentNodeRef::ObjectPat(obj, ObjectPatField::Props(i)));
2396 match prop {
2397 ObjectPatProp::KeyValue(kv) => {
2398 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ObjectPatProp(
2399 prop,
2400 ObjectPatPropField::KeyValue,
2401 ));
2402 let KeyValuePatProp { key, value } = kv;
2403 let key_value = self.eval_context.eval_prop_name(key);
2404 {
2405 let mut ast_path = ast_path.with_guard(AstParentNodeRef::KeyValuePatProp(
2406 kv,
2407 KeyValuePatPropField::Key,
2408 ));
2409 key.visit_with_ast_path(self, &mut ast_path);
2410 }
2411 self.current_value = Some(JsValue::member(
2412 Box::new(current_value.clone()),
2413 Box::new(key_value),
2414 ));
2415 {
2416 let mut ast_path = ast_path.with_guard(AstParentNodeRef::KeyValuePatProp(
2417 kv,
2418 KeyValuePatPropField::Value,
2419 ));
2420 value.visit_with_ast_path(self, &mut ast_path);
2421 }
2422 }
2423 ObjectPatProp::Assign(assign) => {
2424 let mut ast_path = ast_path.with_guard(AstParentNodeRef::ObjectPatProp(
2425 prop,
2426 ObjectPatPropField::Assign,
2427 ));
2428 let AssignPatProp { key, value, .. } = assign;
2429 let key_value = key.sym.clone().into();
2430 {
2431 let mut ast_path = ast_path.with_guard(AstParentNodeRef::AssignPatProp(
2432 assign,
2433 AssignPatPropField::Key,
2434 ));
2435 key.visit_with_ast_path(self, &mut ast_path);
2436 }
2437 self.add_value(
2438 key.to_id(),
2439 if let Some(box value) = value {
2440 let value = self.eval_context.eval(value);
2441 JsValue::alternatives(vec![
2442 JsValue::member(
2443 Box::new(current_value.clone()),
2444 Box::new(key_value),
2445 ),
2446 value,
2447 ])
2448 } else {
2449 JsValue::member(Box::new(current_value.clone()), Box::new(key_value))
2450 },
2451 );
2452 {
2453 let mut ast_path = ast_path.with_guard(AstParentNodeRef::AssignPatProp(
2454 assign,
2455 AssignPatPropField::Value,
2456 ));
2457 value.visit_with_ast_path(self, &mut ast_path);
2458 }
2459 }
2460
2461 _ => prop.visit_with_ast_path(self, &mut ast_path),
2462 }
2463 }
2464 }
2465}
2466
2467fn extract_var_from_umd_factory(callee: &Expr, args: &[ExprOrSpread]) -> Option<Id> {
2468 match unparen(callee) {
2469 Expr::Ident(Ident { sym, .. }) => {
2470 if &**sym == "define"
2471 && let Expr::Fn(FnExpr { function, .. }) = &*args[0].expr
2472 {
2473 let params = &*function.params;
2474 if params.len() == 1
2475 && let Pat::Ident(param) = ¶ms[0].pat
2476 && &*param.id.sym == "require"
2477 {
2478 return Some(param.to_id());
2479 }
2480 }
2481 }
2482
2483 Expr::Fn(FnExpr { function, .. }) => {
2490 let params = &*function.params;
2491 if params.len() == 1
2492 && let Some(FnExpr { function, .. }) =
2493 args.first().and_then(|arg| arg.expr.as_fn_expr())
2494 {
2495 let params = &*function.params;
2496 if !params.is_empty()
2497 && let Pat::Ident(param) = ¶ms[0].pat
2498 && &*param.id.sym == "require"
2499 {
2500 return Some(param.to_id());
2501 }
2502 }
2503 }
2504
2505 _ => {}
2506 }
2507
2508 None
2509}
2510
2511fn get_fn_init_return_vals(fn_body_stmts: Option<&BlockStmt>) -> Vec<JsValue> {
2512 let has_final_return_val = match fn_body_stmts {
2513 Some(fn_body_stmts) => {
2514 matches!(
2515 fn_body_stmts.stmts.last(),
2516 Some(Stmt::Return(ReturnStmt { arg: Some(_), .. }))
2517 )
2518 }
2519 None => false,
2520 };
2521
2522 if has_final_return_val {
2523 vec![]
2524 } else {
2525 vec![JsValue::Constant(ConstantValue::Undefined)]
2526 }
2527}