swc_ecma_transforms_base/rename/analyzer/
scope.rs1#![allow(clippy::too_many_arguments)]
2
3use std::{hash::BuildHasherDefault, mem::take};
4
5use indexmap::IndexSet;
6#[cfg(feature = "concurrent-renamer")]
7use par_iter::prelude::*;
8use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
9use swc_atoms::{atom, Atom};
10use swc_common::Mark;
11use swc_ecma_ast::*;
12use tracing::debug;
13
14use super::reverse_map::ReverseMap;
15use crate::rename::{RenamedVariable, Renamer};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub(crate) enum ScopeKind {
19 Fn,
20 Block,
21}
22
23impl Default for ScopeKind {
24 fn default() -> Self {
25 Self::Fn
26 }
27}
28
29#[derive(Debug, Default)]
30pub(crate) struct Scope {
31 pub(super) kind: ScopeKind,
32 pub(super) data: ScopeData,
33
34 pub(super) children: Vec<Scope>,
35}
36
37pub(super) type FxIndexSet<T> = IndexSet<T, BuildHasherDefault<FxHasher>>;
38
39#[derive(Debug, Default)]
40pub(super) struct ScopeData {
41 all: FxHashSet<Id>,
48
49 queue: FxIndexSet<Id>,
50}
51
52impl Scope {
53 pub(super) fn add_decl(&mut self, id: &Id, has_eval: bool, top_level_mark: Mark) {
54 if id.0 == atom!("arguments") {
55 return;
56 }
57
58 self.data.all.insert(id.clone());
59
60 if !self.data.queue.contains(id) {
61 if has_eval && id.1.outer().is_descendant_of(top_level_mark) {
62 return;
63 }
64
65 self.data.queue.insert(id.clone());
66 }
67 }
68
69 pub(crate) fn reserve_decl(&mut self, len: usize) {
70 self.data.all.reserve(len);
71
72 self.data.queue.reserve(len);
73 }
74
75 pub(super) fn add_usage(&mut self, id: Id) {
76 if id.0 == atom!("arguments") {
77 return;
78 }
79
80 self.data.all.insert(id);
81 }
82
83 pub(crate) fn reserve_usage(&mut self, len: usize) {
84 self.data.all.reserve(len);
85 }
86
87 pub(crate) fn prepare_renaming(&mut self) {
89 self.children.iter_mut().for_each(|child| {
90 child.prepare_renaming();
91
92 self.data.all.extend(child.data.all.iter().cloned());
93 });
94 }
95
96 pub(crate) fn rename_in_normal_mode<R, V>(
97 &mut self,
98 renamer: &R,
99 to: &mut FxHashMap<Id, V>,
100 previous: &FxHashMap<Id, V>,
101 reverse: &mut ReverseMap,
102 preserved: &FxHashSet<Id>,
103 preserved_symbols: &FxHashSet<Atom>,
104 ) where
105 R: Renamer,
106 V: RenamedVariable,
107 {
108 let queue = take(&mut self.data.queue);
109
110 self.rename_one_scope_in_normal_mode(
113 renamer,
114 to,
115 previous,
116 reverse,
117 queue,
118 preserved,
119 preserved_symbols,
120 );
121
122 for child in &mut self.children {
123 child.rename_in_normal_mode(
124 renamer,
125 to,
126 &Default::default(),
127 reverse,
128 preserved,
129 preserved_symbols,
130 );
131 }
132 }
133
134 fn rename_one_scope_in_normal_mode<R, V>(
135 &self,
136 renamer: &R,
137 to: &mut FxHashMap<Id, V>,
138 previous: &FxHashMap<Id, V>,
139 reverse: &mut ReverseMap,
140 queue: FxIndexSet<Id>,
141 preserved: &FxHashSet<Id>,
142 preserved_symbols: &FxHashSet<Atom>,
143 ) where
144 R: Renamer,
145 V: RenamedVariable,
146 {
147 let mut latest_n = FxHashMap::default();
148 let mut n = 0;
149
150 for id in queue {
151 if renamer.preserve_name(&id)
152 || preserved.contains(&id)
153 || to.get(&id).is_some()
154 || previous.get(&id).is_some()
155 || id.0 == "eval"
156 {
157 continue;
158 }
159
160 if R::RESET_N {
161 n = latest_n.get(&id.0).copied().unwrap_or(0);
162 }
163
164 loop {
165 let sym = renamer.new_name_for(&id, &mut n);
166
167 if preserved_symbols.contains(&sym) {
168 continue;
169 }
170
171 if self.can_rename(&id, &sym, reverse) {
172 let renamed = V::new_private(sym.clone());
173 if cfg!(debug_assertions) {
174 let renamed = renamed.to_id();
175 debug!(
176 "Renaming `{}{:?}` to `{}{:?}`",
177 id.0, id.1, renamed.0, renamed.1
178 );
179 }
180 latest_n.insert(id.0.clone(), n);
181
182 reverse.push_entry(sym, id.clone());
183 to.insert(id.clone(), renamed);
184 break;
185 }
186 }
187 }
188 }
189
190 fn can_rename(&self, id: &Id, symbol: &Atom, reverse: &ReverseMap) -> bool {
191 for left in reverse.get(symbol) {
195 if left.1 == id.1 && *left.0 == id.0 {
196 continue;
197 }
198
199 if self.data.all.contains(left) {
200 return false;
201 }
202 }
203
204 true
205 }
206
207 #[cfg_attr(
208 not(feature = "concurrent-renamer"),
209 allow(unused, clippy::only_used_in_recursion)
210 )]
211 pub(crate) fn rename_in_mangle_mode<R, V>(
212 &mut self,
213 renamer: &R,
214 to: &mut FxHashMap<Id, V>,
215 previous: &FxHashMap<Id, V>,
216 reverse: &ReverseMap,
217 preserved: &FxHashSet<Id>,
218 preserved_symbols: &FxHashSet<Atom>,
219 parallel: bool,
220 ) where
221 R: Renamer,
222 V: RenamedVariable,
223 {
224 let queue = take(&mut self.data.queue);
225
226 let mut cloned_reverse = reverse.next();
227
228 self.rename_one_scope_in_mangle_mode(
229 renamer,
230 to,
231 previous,
232 &mut cloned_reverse,
233 queue,
234 preserved,
235 preserved_symbols,
236 );
237
238 #[cfg(feature = "concurrent-renamer")]
239 if parallel {
240 #[cfg(not(all(target_arch = "wasm32", not(target_os = "wasi"))))]
241 let iter = self.children.par_iter_mut();
242 #[cfg(target_arch = "wasm32")]
243 let iter = self.children.iter_mut();
244
245 let iter = iter
246 .map(|child| {
247 use std::collections::HashMap;
248
249 let mut new_map = HashMap::default();
250 child.rename_in_mangle_mode(
251 renamer,
252 &mut new_map,
253 to,
254 &cloned_reverse,
255 preserved,
256 preserved_symbols,
257 parallel,
258 );
259 new_map
260 })
261 .collect::<Vec<_>>();
262
263 for (k, v) in iter.into_iter().flatten() {
264 to.entry(k).or_insert(v);
265 }
266 return;
267 }
268
269 for child in &mut self.children {
270 child.rename_in_mangle_mode(
271 renamer,
272 to,
273 &Default::default(),
274 &cloned_reverse,
275 preserved,
276 preserved_symbols,
277 parallel,
278 );
279 }
280 }
281
282 fn rename_one_scope_in_mangle_mode<R, V>(
283 &self,
284 renamer: &R,
285 to: &mut FxHashMap<Id, V>,
286 previous: &FxHashMap<Id, V>,
287 reverse: &mut ReverseMap,
288 queue: FxIndexSet<Id>,
289 preserved: &FxHashSet<Id>,
290 preserved_symbols: &FxHashSet<Atom>,
291 ) where
292 R: Renamer,
293 V: RenamedVariable,
294 {
295 let mut n = 0;
296
297 for id in queue {
298 if renamer.preserve_name(&id)
299 || preserved.contains(&id)
300 || to.get(&id).is_some()
301 || previous.get(&id).is_some()
302 || id.0 == "eval"
303 {
304 continue;
305 }
306
307 loop {
308 let sym = renamer.new_name_for(&id, &mut n);
309
310 if preserved_symbols.contains(&sym) {
312 continue;
313 }
314
315 if self.can_rename(&id, &sym, reverse) {
316 #[cfg(debug_assertions)]
317 {
318 debug!("mangle: `{}{:?}` -> {}", id.0, id.1, sym);
319 }
320
321 reverse.push_entry(sym.clone(), id.clone());
322 to.insert(id.clone(), V::new_private(sym));
323 break;
327 }
328 }
329 }
330 }
331
332 pub fn rename_cost(&self) -> usize {
333 let children = &self.children;
334 self.data.queue.len() + children.iter().map(|v| v.rename_cost()).sum::<usize>()
335 }
336}