swc_ecma_transforms_base/rename/analyzer/
mod.rs

1use swc_common::Mark;
2use swc_ecma_ast::*;
3
4use self::scope::{Scope, ScopeKind};
5
6mod reverse_map;
7pub(super) mod scope;
8
9#[derive(Debug, Default)]
10pub(super) struct Analyzer {
11    /// If `eval` exists for the current scope, we only rename synthesized
12    /// identifiers.
13    pub(super) has_eval: bool,
14    /// The [Mark] which is parent of user-specified identifiers.
15    pub(super) top_level_mark: Mark,
16
17    pub(super) is_pat_decl: bool,
18    pub(super) var_belong_to_fn_scope: bool,
19    pub(super) in_catch_params: bool,
20    pub(super) scope: Scope,
21    /// If we try add variables declared by `var` to the block scope,
22    /// variables will be added to `hoisted_vars` and merged to latest
23    /// function scope in the end.
24    pub(super) hoisted_vars: Vec<Id>,
25
26    pub(super) skip_first_fn_or_class_decl: bool,
27    pub(super) is_first_node: bool,
28}
29
30impl Analyzer {
31    pub(super) fn new(
32        has_eval: bool,
33        top_level_mark: Mark,
34        skip_first_fn_or_class_decl: bool,
35    ) -> Self {
36        Self {
37            has_eval,
38            top_level_mark,
39            skip_first_fn_or_class_decl,
40            is_first_node: true,
41            hoisted_vars: Vec::with_capacity(32),
42            ..Default::default()
43        }
44    }
45
46    pub(super) fn add_decl(&mut self, id: Id, belong_to_fn_scope: bool) {
47        if belong_to_fn_scope {
48            match self.scope.kind {
49                ScopeKind::Fn => {
50                    self.scope.add_decl(&id, self.has_eval, self.top_level_mark);
51                }
52                ScopeKind::Block => self.hoisted_vars.push(id),
53            }
54        } else {
55            self.scope.add_decl(&id, self.has_eval, self.top_level_mark);
56        }
57    }
58
59    fn reserve_decl(&mut self, len: usize, belong_to_fn_scope: bool) {
60        if belong_to_fn_scope {
61            match self.scope.kind {
62                ScopeKind::Fn => {
63                    self.scope.reserve_decl(len);
64                }
65                ScopeKind::Block => {
66                    self.hoisted_vars.reserve(len);
67                }
68            }
69        } else {
70            self.scope.reserve_decl(len);
71        }
72    }
73
74    pub(super) fn add_usage(&mut self, id: Id) {
75        self.scope.add_usage(id);
76    }
77
78    fn reserve_usage(&mut self, len: usize) {
79        self.scope.reserve_usage(len);
80    }
81
82    fn enter_scope(&mut self, kind: ScopeKind) -> Self {
83        let mut analyer = Analyzer {
84            has_eval: self.has_eval,
85            top_level_mark: self.top_level_mark,
86            is_pat_decl: self.is_pat_decl,
87            var_belong_to_fn_scope: false,
88            in_catch_params: false,
89            scope: Scope {
90                kind,
91                ..Default::default()
92            },
93            skip_first_fn_or_class_decl: false,
94            is_first_node: false,
95            hoisted_vars: Default::default(),
96        };
97        std::mem::swap(self, &mut analyer);
98        analyer // old analyzer
99    }
100
101    pub(super) fn enter_fn_scope(&mut self) -> Self {
102        self.enter_scope(ScopeKind::Fn)
103    }
104
105    pub(super) fn enter_block_scope(&mut self) -> Self {
106        self.enter_scope(ScopeKind::Block)
107    }
108
109    pub(super) fn exit_scope(&mut self, mut v: Self) {
110        std::mem::swap(self, &mut v);
111        if !v.hoisted_vars.is_empty() {
112            debug_assert!(matches!(v.scope.kind, ScopeKind::Block));
113            self.reserve_usage(v.hoisted_vars.len());
114            v.hoisted_vars.clone().into_iter().for_each(|id| {
115                // For variables declared in block scope using `var` and `function`,
116                // We should create a fake usage in the block to prevent conflicted
117                // renaming.
118                v.add_usage(id);
119            });
120            match self.scope.kind {
121                ScopeKind::Fn => {
122                    self.reserve_decl(v.hoisted_vars.len(), true);
123                    v.hoisted_vars
124                        .into_iter()
125                        .for_each(|id| self.add_decl(id, true));
126                }
127                ScopeKind::Block => {
128                    self.hoisted_vars.extend(v.hoisted_vars);
129                }
130            }
131        }
132        self.scope.children.push(v.scope);
133    }
134
135    pub(super) fn handle_binding_ident(&mut self, node: &BindingIdent) {
136        if self.is_pat_decl {
137            self.add_decl(node.to_id(), self.var_belong_to_fn_scope)
138        } else {
139            self.add_usage(node.to_id())
140        }
141    }
142
143    pub(super) fn handle_class_decl(&mut self, node: &ClassDecl) {
144        if self.is_first_node && self.skip_first_fn_or_class_decl {
145            self.is_first_node = false;
146            return;
147        }
148        self.is_first_node = false;
149        self.add_decl(node.ident.to_id(), false);
150    }
151
152    pub(super) fn handle_class_expr(&mut self, node: &ClassExpr) {
153        if let Some(id) = &node.ident {
154            self.add_decl(id.to_id(), false);
155        }
156    }
157
158    pub(super) fn handle_fn_expr(&mut self, node: &FnExpr) {
159        if let Some(id) = &node.ident {
160            self.add_decl(id.to_id(), true);
161        }
162    }
163
164    pub(super) fn handle_export_named_specifier(&mut self, n: &ExportNamedSpecifier) {
165        match &n.orig {
166            ModuleExportName::Ident(orig) => {
167                self.add_usage(orig.to_id());
168            }
169            ModuleExportName::Str(..) => {}
170            #[cfg(swc_ast_unknown)]
171            _ => {}
172        };
173    }
174
175    pub(super) fn handle_expr(&mut self, e: &Expr) {
176        if let Expr::Ident(i) = e {
177            self.add_usage(i.to_id());
178        }
179    }
180
181    pub(super) fn handle_prop(&mut self, node: &Prop) {
182        if let Prop::Shorthand(i) = node {
183            self.add_usage(i.to_id());
184        }
185    }
186}