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 fn atom(&self) -> &Atom;
134 fn ctxt(&self) -> SyntaxContext;
135}
136impl RenamedVariable for Atom {
137 fn new_private(sym: Atom) -> Self {
138 sym
139 }
140
141 fn to_id(&self) -> Id {
142 (self.clone(), Default::default())
143 }
144
145 fn atom(&self) -> &Atom {
146 self
147 }
148
149 fn ctxt(&self) -> SyntaxContext {
150 Default::default()
151 }
152}
153impl RenamedVariable for Id {
154 fn new_private(sym: Atom) -> Self {
155 (sym, SyntaxContext::empty().apply_mark(Mark::new()))
156 }
157
158 fn to_id(&self) -> Id {
159 self.clone()
160 }
161
162 fn atom(&self) -> &Atom {
163 &self.0
164 }
165
166 fn ctxt(&self) -> SyntaxContext {
167 self.1
168 }
169}
170
171#[derive(Debug, Default)]
172struct RenamePass<R, V>
173where
174 R: Renamer<Target = V>,
175 V: RenamedVariable,
176{
177 config: Config,
178 renamer: R,
179
180 preserved: FxHashSet<Id>,
181 unresolved: FxHashSet<Atom>,
182
183 previous_cache: FxHashMap<Id, V>,
184
185 total_map: Option<FxHashMap<Id, V>>,
189
190 marker: std::marker::PhantomData<V>,
191}
192
193impl<R, V> RenamePass<R, V>
194where
195 R: Renamer<Target = V>,
196 V: RenamedVariable,
197{
198 fn get_map<N>(
199 &mut self,
200 node: &N,
201 skip_one: bool,
202 top_level: bool,
203 has_eval: bool,
204 ) -> FxHashMap<Id, V>
205 where
206 N: VisitWith<AnalyzerAndCollector>,
207 {
208 let (mut scope, unresolved) = analyer_and_collector::analyzer_and_collect_unresolved(
209 node,
210 has_eval,
211 self.config.top_level_mark,
212 skip_one,
213 );
214
215 scope.prepare_renaming();
216
217 let mut unresolved = if !top_level {
218 let mut set = self.unresolved.clone();
219 set.extend(unresolved);
220 Cow::Owned(set)
221 } else {
222 self.unresolved = unresolved;
223 Cow::Borrowed(&self.unresolved)
224 };
225
226 if !self.preserved.is_empty() {
227 unresolved
228 .to_mut()
229 .extend(self.preserved.iter().map(|v| v.0.clone()));
230 }
231
232 {
233 let extra_unresolved = self.renamer.unresolved_symbols();
234
235 if !extra_unresolved.is_empty() {
236 unresolved.to_mut().extend(extra_unresolved);
237 }
238 }
239
240 let mut map = FxHashMap::<Id, V>::default();
241
242 if R::MANGLE {
243 let cost = scope.rename_cost();
244 scope.rename_in_mangle_mode(
245 &self.renamer,
246 &mut map,
247 &self.previous_cache,
248 &Default::default(),
249 &self.preserved,
250 &unresolved,
251 cost > 1024,
252 );
253 } else {
254 scope.rename_in_normal_mode(
255 &self.renamer,
256 &mut map,
257 &self.previous_cache,
258 &mut Default::default(),
259 &self.preserved,
260 &unresolved,
261 );
262 }
263
264 if let Some(total_map) = &mut self.total_map {
265 total_map.reserve(map.len());
266
267 for (k, v) in &map {
268 match total_map.entry(k.clone()) {
269 Entry::Occupied(old) => {
270 let old = old.get().to_id();
271 let new = v.to_id();
272 unreachable!(
273 "{} is already renamed to {}, but it's renamed as {}",
274 k.0, old.0, new.0
275 );
276 }
277 Entry::Vacant(e) => {
278 e.insert(v.clone());
279 }
280 }
281 }
282 }
283
284 map
285 }
286
287 fn load_cache(&mut self) {
288 if let Some(cache) = self.renamer.get_cached() {
289 self.previous_cache = cache.into_owned();
290 self.total_map = Some(Default::default());
291 }
292 }
293}
294
295macro_rules! unit {
299 ($name:ident, $T:ty) => {
300 fn $name(&mut self, n: &mut $T) {
302 if !self.config.ignore_eval && contains_eval(n, true) {
303 n.visit_mut_children_with(self);
304 } else {
305 let map = self.get_map(n, false, false, false);
306
307 if !map.is_empty() {
308 n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
309 }
310 }
311 }
312 };
313}
314
315impl<R, V> VisitMut for RenamePass<R, V>
316where
317 R: Renamer<Target = V>,
318 V: RenamedVariable,
319{
320 noop_visit_mut_type!();
321
322 unit!(visit_mut_arrow_expr, ArrowExpr);
323
324 unit!(visit_mut_setter_prop, SetterProp);
325
326 unit!(visit_mut_getter_prop, GetterProp);
327
328 unit!(visit_mut_constructor, Constructor);
329
330 unit!(visit_mut_fn_expr, FnExpr);
331
332 unit!(visit_mut_method_prop, MethodProp);
333
334 unit!(visit_mut_class_method, ClassMethod);
335
336 unit!(visit_mut_private_method, PrivateMethod);
337
338 fn visit_mut_fn_decl(&mut self, n: &mut FnDecl) {
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_class_decl(&mut self, n: &mut ClassDecl) {
357 if !self.config.ignore_eval && contains_eval(n, true) {
358 n.visit_mut_children_with(self);
359 } else {
360 let id = n.ident.to_id();
361 let inserted = self.preserved.insert(id.clone());
362 let map = self.get_map(n, true, false, false);
363
364 if inserted {
365 self.preserved.remove(&id);
366 }
367
368 if !map.is_empty() {
369 n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
370 }
371 }
372 }
373
374 fn visit_mut_default_decl(&mut self, n: &mut DefaultDecl) {
375 match n {
376 DefaultDecl::Class(n) => {
377 n.visit_mut_children_with(self);
378 }
379 DefaultDecl::Fn(n) => {
380 n.visit_mut_children_with(self);
381 }
382 DefaultDecl::TsInterfaceDecl(n) => {
383 n.visit_mut_children_with(self);
384 }
385 #[cfg(swc_ast_unknown)]
386 _ => (),
387 }
388 }
389
390 fn visit_mut_expr(&mut self, n: &mut Expr) {
391 maybe_grow_default(|| n.visit_mut_children_with(self));
392 }
393
394 fn visit_mut_module(&mut self, m: &mut Module) {
395 self.load_cache();
396
397 let has_eval = !self.config.ignore_eval && contains_eval(m, true);
398
399 let map = self.get_map(m, false, true, has_eval);
400
401 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 fn visit_mut_script(&mut self, m: &mut Script) {
436 self.load_cache();
437
438 let has_eval = !self.config.ignore_eval && contains_eval(m, true);
439
440 let map = self.get_map(m, false, true, has_eval);
441
442 if has_eval {
443 m.visit_mut_children_with(self);
444 }
445
446 if !map.is_empty() {
447 m.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
448 }
449
450 if let Some(total_map) = &self.total_map {
451 self.renamer.store_cache(total_map);
452 }
453 }
454}
455
456#[cfg(feature = "concurrent-renamer")]
457mod renamer_concurrent {
458 pub use std::marker::{Send, Sync};
459}
460
461#[cfg(not(feature = "concurrent-renamer"))]
462mod renamer_single {
463 pub trait Send {}
465 pub trait Sync {}
467
468 impl<T> Send for T where T: ?Sized {}
469 impl<T> Sync for T where T: ?Sized {}
470}