swc_ecma_transforms_base/rename/
mod.rs

1#![allow(unused_imports)]
2
3use std::{borrow::Cow, collections::hash_map::Entry};
4
5use rustc_hash::{FxHashMap, FxHashSet};
6use swc_atoms::Atom;
7use swc_ecma_ast::*;
8use swc_ecma_utils::stack_size::maybe_grow_default;
9use swc_ecma_visit::{
10    noop_visit_mut_type, visit_mut_pass, Fold, VisitMut, VisitMutWith, VisitWith,
11};
12
13#[cfg(feature = "concurrent-renamer")]
14use self::renamer_concurrent::{Send, Sync};
15#[cfg(not(feature = "concurrent-renamer"))]
16use self::renamer_single::{Send, Sync};
17use self::{
18    analyzer::Analyzer,
19    collector::{collect_decls, CustomBindingCollector, IdCollector},
20    eval::contains_eval,
21    ops::Operator,
22};
23use crate::hygiene::Config;
24
25mod analyzer;
26mod collector;
27mod eval;
28mod ops;
29
30pub trait Renamer: Send + Sync {
31    /// Should reset `n` to 0 for each identifier?
32    const RESET_N: bool;
33
34    /// It should be true if you expect lots of collisions
35    const MANGLE: bool;
36
37    fn preserved_ids_for_module(&mut self, _: &Module) -> FxHashSet<Id> {
38        Default::default()
39    }
40
41    fn preserved_ids_for_script(&mut self, _: &Script) -> FxHashSet<Id> {
42        Default::default()
43    }
44
45    fn get_cached(&self) -> Option<Cow<RenameMap>> {
46        None
47    }
48
49    fn store_cache(&mut self, _update: &RenameMap) {}
50
51    /// Should increment `n`.
52    fn new_name_for(&self, orig: &Id, n: &mut usize) -> Atom;
53}
54
55pub type RenameMap = FxHashMap<Id, Atom>;
56
57pub fn rename(map: &RenameMap) -> impl '_ + Pass + VisitMut {
58    rename_with_config(map, Default::default())
59}
60
61pub fn rename_with_config(map: &RenameMap, config: Config) -> impl '_ + Pass + VisitMut {
62    visit_mut_pass(Operator {
63        rename: map,
64        config,
65        extra: Default::default(),
66    })
67}
68
69pub fn remap(map: &FxHashMap<Id, Id>, config: Config) -> impl '_ + Pass + VisitMut {
70    visit_mut_pass(Operator {
71        rename: map,
72        config,
73        extra: Default::default(),
74    })
75}
76
77pub fn renamer<R>(config: Config, renamer: R) -> impl Pass + VisitMut
78where
79    R: Renamer,
80{
81    visit_mut_pass(RenamePass {
82        config,
83        renamer,
84        preserved: Default::default(),
85        unresolved: Default::default(),
86        previous_cache: Default::default(),
87        total_map: None,
88    })
89}
90
91#[derive(Debug, Default)]
92struct RenamePass<R>
93where
94    R: Renamer,
95{
96    config: Config,
97    renamer: R,
98
99    preserved: FxHashSet<Id>,
100    unresolved: FxHashSet<Atom>,
101
102    previous_cache: RenameMap,
103
104    /// Used to store cache.
105    ///
106    /// [Some] if the [`Renamer::get_cached`] returns [Some].
107    total_map: Option<RenameMap>,
108}
109
110impl<R> RenamePass<R>
111where
112    R: Renamer,
113{
114    fn get_unresolved<N>(&self, n: &N, has_eval: bool) -> FxHashSet<Atom>
115    where
116        N: VisitWith<IdCollector> + VisitWith<CustomBindingCollector<Id>>,
117    {
118        let usages = {
119            let mut v = IdCollector {
120                ids: Default::default(),
121            };
122            n.visit_with(&mut v);
123            v.ids
124        };
125        let (decls, preserved) = collect_decls(
126            n,
127            if has_eval {
128                Some(self.config.top_level_mark)
129            } else {
130                None
131            },
132        );
133        usages
134            .into_iter()
135            .filter(|used_id| !decls.contains(used_id))
136            .map(|v| v.0)
137            .chain(preserved.into_iter().map(|v| v.0))
138            .collect()
139    }
140
141    fn get_map<N>(&mut self, node: &N, skip_one: bool, top_level: bool, has_eval: bool) -> RenameMap
142    where
143        N: VisitWith<IdCollector> + VisitWith<CustomBindingCollector<Id>>,
144        N: VisitWith<Analyzer>,
145    {
146        let mut scope = {
147            let mut v = Analyzer {
148                has_eval,
149                top_level_mark: self.config.top_level_mark,
150
151                ..Default::default()
152            };
153            if skip_one {
154                node.visit_children_with(&mut v);
155            } else {
156                node.visit_with(&mut v);
157            }
158            v.scope
159        };
160        scope.prepare_renaming();
161
162        let mut map = RenameMap::default();
163
164        let mut unresolved = if !top_level {
165            let mut unresolved = self.unresolved.clone();
166            unresolved.extend(self.get_unresolved(node, has_eval));
167            Cow::Owned(unresolved)
168        } else {
169            Cow::Borrowed(&self.unresolved)
170        };
171
172        if !self.preserved.is_empty() {
173            unresolved
174                .to_mut()
175                .extend(self.preserved.iter().map(|v| v.0.clone()));
176        }
177
178        if !self.config.preserved_symbols.is_empty() {
179            unresolved
180                .to_mut()
181                .extend(self.config.preserved_symbols.iter().cloned());
182        }
183
184        if R::MANGLE {
185            let cost = scope.rename_cost();
186            scope.rename_in_mangle_mode(
187                &self.renamer,
188                &mut map,
189                &self.previous_cache,
190                &Default::default(),
191                &self.preserved,
192                &unresolved,
193                cost > 1024,
194            );
195        } else {
196            scope.rename_in_normal_mode(
197                &self.renamer,
198                &mut map,
199                &self.previous_cache,
200                &mut Default::default(),
201                &self.preserved,
202                &unresolved,
203            );
204        }
205
206        if let Some(total_map) = &mut self.total_map {
207            total_map.reserve(map.len());
208
209            for (k, v) in &map {
210                match total_map.entry(k.clone()) {
211                    Entry::Occupied(old) => {
212                        unreachable!(
213                            "{} is already renamed to {}, but it's renamed as {}",
214                            k.0,
215                            old.get(),
216                            v
217                        );
218                    }
219                    Entry::Vacant(e) => {
220                        e.insert(v.clone());
221                    }
222                }
223            }
224        }
225
226        map
227    }
228
229    fn load_cache(&mut self) {
230        if let Some(cache) = self.renamer.get_cached() {
231            self.previous_cache = cache.into_owned();
232            self.total_map = Some(Default::default());
233        }
234    }
235}
236
237/// Mark a node as a unit of minification.
238///
239/// This is
240macro_rules! unit {
241    ($name:ident, $T:ty) => {
242        /// Only called if `eval` exists
243        fn $name(&mut self, n: &mut $T) {
244            if !self.config.ignore_eval && contains_eval(n, true) {
245                n.visit_mut_children_with(self);
246            } else {
247                let map = self.get_map(n, false, false, false);
248
249                if !map.is_empty() {
250                    n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
251                }
252            }
253        }
254    };
255    ($name:ident, $T:ty, true) => {
256        /// Only called if `eval` exists
257        fn $name(&mut self, n: &mut $T) {
258            if !self.config.ignore_eval && contains_eval(n, true) {
259                n.visit_mut_children_with(self);
260            } else {
261                let map = self.get_map(n, true, false, false);
262
263                if !map.is_empty() {
264                    n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
265                }
266            }
267        }
268    };
269}
270
271impl<R> VisitMut for RenamePass<R>
272where
273    R: Renamer,
274{
275    noop_visit_mut_type!();
276
277    unit!(visit_mut_arrow_expr, ArrowExpr);
278
279    unit!(visit_mut_setter_prop, SetterProp);
280
281    unit!(visit_mut_getter_prop, GetterProp);
282
283    unit!(visit_mut_constructor, Constructor);
284
285    unit!(visit_mut_fn_expr, FnExpr);
286
287    unit!(visit_mut_method_prop, MethodProp);
288
289    unit!(visit_mut_class_method, ClassMethod);
290
291    unit!(visit_mut_private_method, PrivateMethod);
292
293    fn visit_mut_fn_decl(&mut self, n: &mut FnDecl) {
294        if !self.config.ignore_eval && contains_eval(n, true) {
295            n.visit_mut_children_with(self);
296        } else {
297            let id = n.ident.to_id();
298            let inserted = self.preserved.insert(id.clone());
299            let map = self.get_map(n, true, false, false);
300
301            if inserted {
302                self.preserved.remove(&id);
303            }
304
305            if !map.is_empty() {
306                n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
307            }
308        }
309    }
310
311    fn visit_mut_class_decl(&mut self, n: &mut ClassDecl) {
312        if !self.config.ignore_eval && contains_eval(n, true) {
313            n.visit_mut_children_with(self);
314        } else {
315            let id = n.ident.to_id();
316            let inserted = self.preserved.insert(id.clone());
317            let map = self.get_map(n, true, false, false);
318
319            if inserted {
320                self.preserved.remove(&id);
321            }
322
323            if !map.is_empty() {
324                n.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
325            }
326        }
327    }
328
329    fn visit_mut_default_decl(&mut self, n: &mut DefaultDecl) {
330        match n {
331            DefaultDecl::Class(n) => {
332                n.visit_mut_children_with(self);
333            }
334            DefaultDecl::Fn(n) => {
335                n.visit_mut_children_with(self);
336            }
337            DefaultDecl::TsInterfaceDecl(n) => {
338                n.visit_mut_children_with(self);
339            }
340        }
341    }
342
343    fn visit_mut_expr(&mut self, n: &mut Expr) {
344        maybe_grow_default(|| n.visit_mut_children_with(self));
345    }
346
347    fn visit_mut_module(&mut self, m: &mut Module) {
348        self.load_cache();
349
350        self.preserved = self.renamer.preserved_ids_for_module(m);
351
352        let has_eval = !self.config.ignore_eval && contains_eval(m, true);
353
354        self.unresolved = self.get_unresolved(m, has_eval);
355
356        let map = self.get_map(m, false, true, has_eval);
357
358        // If we have eval, we cannot rename a whole program at once.
359        //
360        // Still, we can, and should rename some identifiers, if the containing scope
361        // (function-like nodes) does not have eval. This `eval` check includes
362        // `eval` in children.
363        //
364        // We calculate the top level map first, rename children, and then rename the
365        // top level.
366        //
367        //
368        // Order:
369        //
370        // 1. Top level map calculation
371        // 2. Per-unit map calculation
372        // 3. Per-unit renaming
373        // 4. Top level renaming
374        //
375        // This is because the top level map may contain a mapping which conflicts
376        // with a map from one of the children.
377        //
378        // See https://github.com/swc-project/swc/pull/7615
379        if has_eval {
380            m.visit_mut_children_with(self);
381        }
382
383        if !map.is_empty() {
384            m.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
385        }
386
387        if let Some(total_map) = &self.total_map {
388            self.renamer.store_cache(total_map);
389        }
390    }
391
392    fn visit_mut_script(&mut self, m: &mut Script) {
393        self.load_cache();
394
395        self.preserved = self.renamer.preserved_ids_for_script(m);
396
397        let has_eval = !self.config.ignore_eval && contains_eval(m, true);
398
399        self.unresolved = self.get_unresolved(m, has_eval);
400
401        let map = self.get_map(m, false, true, has_eval);
402
403        if has_eval {
404            m.visit_mut_children_with(self);
405        }
406
407        if !map.is_empty() {
408            m.visit_mut_with(&mut rename_with_config(&map, self.config.clone()));
409        }
410
411        if let Some(total_map) = &self.total_map {
412            self.renamer.store_cache(total_map);
413        }
414    }
415}
416
417#[cfg(feature = "concurrent-renamer")]
418mod renamer_concurrent {
419    pub use std::marker::{Send, Sync};
420}
421
422#[cfg(not(feature = "concurrent-renamer"))]
423mod renamer_single {
424    /// Dummy trait because swc_common is in single thread mode.
425    pub trait Send {}
426    /// Dummy trait because swc_common is in single thread mode.
427    pub trait Sync {}
428
429    impl<T> Send for T where T: ?Sized {}
430    impl<T> Sync for T where T: ?Sized {}
431}