swc_ecma_transforms_base/hygiene/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
use rustc_hash::FxHashSet;
use swc_atoms::Atom;
use swc_common::Mark;
use swc_ecma_ast::*;
use swc_ecma_utils::stack_size::maybe_grow_default;
use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
pub use crate::rename::rename;
use crate::rename::{renamer, Renamer};
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Default)]
pub struct Config {
/// If true, the `hygiene` pass will preserve class names.
pub keep_class_names: bool,
/// If true, the bug of safari 10 is avoided.
#[deprecated = "This field is no longer required to work around bugs in Safari 10."]
pub safari_10: bool,
/// The marks derived from this marks will treated as `specified by user`
/// and other marks will be treated as `generated by swc`.
pub top_level_mark: Mark,
/// Mangle even if vars are visible to `eval` or `with`.
pub ignore_eval: bool,
/// Used for preventing mangler from renaming variables to reserved names.
pub preserved_symbols: FxHashSet<Atom>,
}
/// See [hygiene_with_config] for doc. Creates a `hygiene` pass with default
/// value of [Config].
pub fn hygiene() -> impl Pass + VisitMut {
hygiene_with_config(Default::default())
}
/// The pass actually modifies the identifiers in the way that different
/// identifier (with respect to span hygiene) becomes different identifier.
/// (e.g. `a1` for `a#6`, `a2` for `a#23`)
///
/// # Implementation details
///
/// This document exists For curious people and potential contributors.
///
/// `hygiene` consists of three phases.
///
/// ## First phase
///
/// At first phase, we mark (using [swc_common::Mark]) nodes which can be
/// considered as a `scope`. e.g. [Function], [BlockStmt], [ArrowExpr]
///
/// ## Second phase
///
/// At second phase, we analyzes the file and determine identifiers to rename.
///
/// Note that we store scoping information for each node, using the fact that
/// [SyntaxContext] of all `scope` nodes are unique, thanks to the first phase.
///
///
/// ## Third phase
///
/// At third phase, we rename all identifiers in the queue.
pub fn hygiene_with_config(config: Config) -> impl 'static + Pass + VisitMut {
(
renamer(config, HygieneRenamer),
visit_mut_pass(HygieneRemover),
)
}
struct HygieneRenamer;
impl Renamer for HygieneRenamer {
const MANGLE: bool = false;
const RESET_N: bool = true;
fn new_name_for(&self, orig: &Id, n: &mut usize) -> swc_atoms::JsWord {
let res = if *n == 0 {
orig.0.clone()
} else {
format!("{}{}", orig.0, n).into()
};
*n += 1;
res
}
}
struct HygieneRemover;
impl VisitMut for HygieneRemover {
noop_visit_mut_type!();
fn visit_mut_expr(&mut self, n: &mut Expr) {
maybe_grow_default(|| n.visit_mut_children_with(self));
}
fn visit_mut_ident(&mut self, i: &mut Ident) {
i.ctxt = Default::default();
}
}