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