swc_ecma_transforms_base/rename/
mod.rs1use std::{borrow::Cow, collections::hash_map::Entry};
2
3use analyer_and_collector::AnalyzerAndCollector;
4use rustc_hash::{FxHashMap, FxHashSet};
5use swc_atoms::Atom;
6use swc_common::{Mark, SyntaxContext};
7use swc_ecma_ast::*;
8use swc_ecma_utils::stack_size::maybe_grow_default;
9use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith, VisitWith};
10
11pub use self::eval::contains_eval;
12#[cfg(feature = "concurrent-renamer")]
13use self::renamer_concurrent::{Send, Sync};
14#[cfg(not(feature = "concurrent-renamer"))]
15use self::renamer_single::{Send, Sync};
16use self::{analyzer::Analyzer, ops::Operator};
17use crate::hygiene::Config;
18
19mod analyer_and_collector;
20mod analyzer;
21mod eval;
22mod ops;
23
24pub trait Renamer: Send + Sync {
25 type Target: RenamedVariable;
28
29 const RESET_N: bool;
31
32 const MANGLE: bool;
34
35 fn get_cached(&self) -> Option<Cow<FxHashMap<Id, Self::Target>>> {
36 None
37 }
38
39 fn store_cache(&mut self, _update: &FxHashMap<Id, Self::Target>) {}
40
41 fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom;
43
44 fn unresolved_symbols(&self) -> Vec<Atom> {
45 Default::default()
46 }
47
48 #[inline]
50 fn preserve_name(&self, _orig: &Id) -> bool {
51 false
52 }
53}
54
55pub type RenameMap = FxHashMap<Id, Atom>;
56
57pub fn rename<V: RenamedVariable>(map: &FxHashMap<Id, V>) -> impl '_ + Pass + VisitMut {
58 rename_with_config(map, Default::default())
59}
60
61pub fn rename_with_config<V: RenamedVariable>(
62 map: &FxHashMap<Id, V>,
63 config: Config,
64) -> impl '_ + Pass + VisitMut {
65 visit_mut_pass(Operator {
66 rename: map,
67 config,
68 extra: Default::default(),
69 })
70}
71
72pub fn renamer<R>(config: Config, renamer: R) -> impl Pass + VisitMut
73where
74 R: Renamer<Target = Atom>,
75{
76 visit_mut_pass(RenamePass {
77 config,
78 renamer,
79 preserved: Default::default(),
80 unresolved: Default::default(),
81 previous_cache: Default::default(),
82 total_map: None,
83 marker: std::marker::PhantomData::<Atom>,
84 })
85}
86
87pub fn renamer_keep_contexts<R>(config: Config, renamer: R) -> impl Pass + VisitMut
91where
92 R: Renamer<Target = Id>,
93{
94 visit_mut_pass(RenamePass {
95 config,
96 renamer,
97 preserved: Default::default(),
98 unresolved: Default::default(),
99 previous_cache: Default::default(),
100 total_map: None,
101 marker: std::marker::PhantomData::<Id>,
102 })
103}
104
105mod private {
106 use swc_atoms::Atom;
107 use swc_ecma_ast::Id;
108
109 pub trait Sealed {}
110
111 impl Sealed for Atom {}
112 impl Sealed for Id {}
113}
114
115pub trait RenamedVariable:
127 private::Sealed + Clone + Sized + std::marker::Send + std::marker::Sync + 'static
128{
129 fn new_private(sym: Atom) -> Self;
132 fn to_id(&self) -> Id;
133}
134impl RenamedVariable for Atom {
135 fn new_private(sym: Atom) -> Self {
136 sym
137 }
138
139 fn to_id(&self) -> Id {
140 (self.clone(), Default::default())
141 }
142}
143impl RenamedVariable for Id {
144 fn new_private(sym: Atom) -> Self {
145 (sym, SyntaxContext::empty().apply_mark(Mark::new()))
146 }
147
148 fn to_id(&self) -> Id {
149 self.clone()
150 }
151}
152
153#[derive(Debug, Default)]
154struct RenamePass<R, V>
155where
156 R: Renamer<Target = V>,
157 V: RenamedVariable,
158{
159 config: Config,
160 renamer: R,
161
162 preserved: FxHashSet<Id>,
163 unresolved: FxHashSet<Atom>,
164
165 previous_cache: FxHashMap<Id, V>,
166
167 total_map: Option<FxHashMap<Id, V>>,
171
172 marker: std::marker::PhantomData<V>,
173}
174
175impl<R, V> RenamePass<R, V>
176where
177 R: Renamer<Target = V>,
178 V: RenamedVariable,
179{
180 fn get_map<N>(
181 &mut self,
182 node: &N,
183 skip_one: bool,
184 top_level: bool,
185 has_eval: bool,
186 ) -> FxHashMap<Id, V>
187 where
188 N: VisitWith<AnalyzerAndCollector>,
189 {
190 let (mut scope, unresolved) = analyer_and_collector::analyzer_and_collect_unresolved(
191 node,
192 has_eval,
193 self.config.top_level_mark,
194 skip_one,
195 );
196
197 scope.prepare_renaming();
198
199 let mut unresolved = if !top_level {
200 let mut set = self.unresolved.clone();
201 set.extend(unresolved);
202 Cow::Owned(set)
203 } else {
204 self.unresolved = unresolved;
205 Cow::Borrowed(&self.unresolved)
206 };
207
208 if !self.preserved.is_empty() {
209 unresolved
210 .to_mut()
211 .extend(self.preserved.iter().map(|v| v.0.clone()));
212 }
213
214 {
215 let extra_unresolved = self.renamer.unresolved_symbols();
216
217 if !extra_unresolved.is_empty() {
218 unresolved.to_mut().extend(extra_unresolved);
219 }
220 }
221
222 let mut map = FxHashMap::<Id, V>::default();
223
224 if R::MANGLE {
225 let cost = scope.rename_cost();
226 scope.rename_in_mangle_mode(
227 &self.renamer,
228 &mut map,
229 &self.previous_cache,
230 &Default::default(),
231 &self.preserved,
232 &unresolved,
233 cost > 1024,
234 );
235 } else {
236 scope.rename_in_normal_mode(
237 &self.renamer,
238 &mut map,
239 &self.previous_cache,
240 &mut Default::default(),
241 &self.preserved,
242 &unresolved,
243 );
244 }
245
246 if let Some(total_map) = &mut self.total_map {
247 total_map.reserve(map.len());
248
249 for (k, v) in &map {
250 match total_map.entry(k.clone()) {
251 Entry::Occupied(old) => {
252 let old = old.get().to_id();
253 let new = v.to_id();
254 unreachable!(
255 "{} is already renamed to {}, but it's renamed as {}",
256 k.0, old.0, new.0
257 );
258 }
259 Entry::Vacant(e) => {
260 e.insert(v.clone());
261 }
262 }
263 }
264 }
265
266 map
267 }
268
269 fn load_cache(&mut self) {
270 if let Some(cache) = self.renamer.get_cached() {
271 self.previous_cache = cache.into_owned();
272 self.total_map = Some(Default::default());
273 }
274 }
275}
276
277macro_rules! unit {
281 ($name:ident, $T:ty) => {
282 fn $name(&mut self, n: &mut $T) {
284 if !self.config.ignore_eval && contains_eval(n, true) {
285 n.visit_mut_children_with(self);
286 } else {
287 let map = self.get_map(n, false, false, false);
288
289 if !map.is_empty() {
290 n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
291 }
292 }
293 }
294 };
295}
296
297impl<R, V> VisitMut for RenamePass<R, V>
298where
299 R: Renamer<Target = V>,
300 V: RenamedVariable,
301{
302 noop_visit_mut_type!();
303
304 unit!(visit_mut_arrow_expr, ArrowExpr);
305
306 unit!(visit_mut_setter_prop, SetterProp);
307
308 unit!(visit_mut_getter_prop, GetterProp);
309
310 unit!(visit_mut_constructor, Constructor);
311
312 unit!(visit_mut_fn_expr, FnExpr);
313
314 unit!(visit_mut_method_prop, MethodProp);
315
316 unit!(visit_mut_class_method, ClassMethod);
317
318 unit!(visit_mut_private_method, PrivateMethod);
319
320 fn visit_mut_fn_decl(&mut self, n: &mut FnDecl) {
321 if !self.config.ignore_eval && contains_eval(n, true) {
322 n.visit_mut_children_with(self);
323 } else {
324 let id = n.ident.to_id();
325 let inserted = self.preserved.insert(id.clone());
326 let map = self.get_map(n, true, false, false);
327
328 if inserted {
329 self.preserved.remove(&id);
330 }
331
332 if !map.is_empty() {
333 n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
334 }
335 }
336 }
337
338 fn visit_mut_class_decl(&mut self, n: &mut ClassDecl) {
339 if !self.config.ignore_eval && contains_eval(n, true) {
340 n.visit_mut_children_with(self);
341 } else {
342 let id = n.ident.to_id();
343 let inserted = self.preserved.insert(id.clone());
344 let map = self.get_map(n, true, false, false);
345
346 if inserted {
347 self.preserved.remove(&id);
348 }
349
350 if !map.is_empty() {
351 n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
352 }
353 }
354 }
355
356 fn visit_mut_default_decl(&mut self, n: &mut DefaultDecl) {
357 match n {
358 DefaultDecl::Class(n) => {
359 n.visit_mut_children_with(self);
360 }
361 DefaultDecl::Fn(n) => {
362 n.visit_mut_children_with(self);
363 }
364 DefaultDecl::TsInterfaceDecl(n) => {
365 n.visit_mut_children_with(self);
366 }
367 }
368 }
369
370 fn visit_mut_expr(&mut self, n: &mut Expr) {
371 maybe_grow_default(|| n.visit_mut_children_with(self));
372 }
373
374 fn visit_mut_module(&mut self, m: &mut Module) {
375 self.load_cache();
376
377 let has_eval = !self.config.ignore_eval && contains_eval(m, true);
378
379 let map = self.get_map(m, false, true, has_eval);
380
381 if has_eval {
403 m.visit_mut_children_with(self);
404 }
405
406 if !map.is_empty() {
407 m.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
408 }
409
410 if let Some(total_map) = &self.total_map {
411 self.renamer.store_cache(total_map);
412 }
413 }
414
415 fn visit_mut_script(&mut self, m: &mut Script) {
416 self.load_cache();
417
418 let has_eval = !self.config.ignore_eval && contains_eval(m, true);
419
420 let map = self.get_map(m, false, true, has_eval);
421
422 if has_eval {
423 m.visit_mut_children_with(self);
424 }
425
426 if !map.is_empty() {
427 m.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
428 }
429
430 if let Some(total_map) = &self.total_map {
431 self.renamer.store_cache(total_map);
432 }
433 }
434}
435
436#[cfg(feature = "concurrent-renamer")]
437mod renamer_concurrent {
438 pub use std::marker::{Send, Sync};
439}
440
441#[cfg(not(feature = "concurrent-renamer"))]
442mod renamer_single {
443 pub trait Send {}
445 pub trait Sync {}
447
448 impl<T> Send for T where T: ?Sized {}
449 impl<T> Sync for T where T: ?Sized {}
450}