swc_ecma_transforms_optimization/simplify/
const_propagation.rs1#![allow(clippy::borrowed_box)]
2
3use rustc_hash::FxHashMap;
4use swc_common::util::take::Take;
5use swc_ecma_ast::*;
6use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
7
8pub fn constant_propagation() -> impl 'static + Pass + VisitMut {
10 visit_mut_pass(ConstPropagation::default())
11}
12
13#[derive(Default)]
14struct ConstPropagation<'a> {
15 scope: Scope<'a>,
16}
17#[derive(Default)]
18struct Scope<'a> {
19 parent: Option<&'a Scope<'a>>,
20 vars: FxHashMap<Id, Box<Expr>>,
22}
23
24impl<'a> Scope<'a> {
25 fn new(parent: &'a Scope<'a>) -> Self {
26 Self {
27 parent: Some(parent),
28 vars: Default::default(),
29 }
30 }
31
32 fn find_var(&self, id: &Id) -> Option<&Box<Expr>> {
33 if let Some(v) = self.vars.get(id) {
34 return Some(v);
35 }
36
37 self.parent.and_then(|parent| parent.find_var(id))
38 }
39}
40
41impl VisitMut for ConstPropagation<'_> {
42 noop_visit_mut_type!(fail);
43
44 fn visit_mut_assign_expr(&mut self, _: &mut AssignExpr) {}
46
47 fn visit_mut_export_named_specifier(&mut self, n: &mut ExportNamedSpecifier) {
48 let id = match &n.orig {
49 ModuleExportName::Ident(ident) => ident.to_id(),
50 ModuleExportName::Str(..) => return,
51 #[cfg(swc_ast_unknown)]
52 _ => panic!("unable to access unknown nodes"),
53 };
54 if let Some(expr) = self.scope.find_var(&id) {
55 if let Expr::Ident(v) = &**expr {
56 let orig = n.orig.clone();
57 n.orig = ModuleExportName::Ident(v.clone());
58
59 if n.exported.is_none() {
60 n.exported = Some(orig);
61 }
62 }
63 }
64
65 match &n.exported {
66 Some(ModuleExportName::Ident(exported)) => match &n.orig {
67 ModuleExportName::Ident(orig) => {
68 if exported.sym == orig.sym && exported.ctxt == orig.ctxt {
69 n.exported = None;
70 }
71 }
72 ModuleExportName::Str(..) => {}
73 #[cfg(swc_ast_unknown)]
74 _ => panic!("unable to access unknown nodes"),
75 },
76 Some(ModuleExportName::Str(..)) => {}
77 #[cfg(swc_ast_unknown)]
78 Some(_) => panic!("unable to access unknown nodes"),
79 None => {}
80 }
81 }
82
83 fn visit_mut_expr(&mut self, e: &mut Expr) {
84 if let Expr::Ident(i) = e {
85 if let Some(expr) = self.scope.find_var(&i.to_id()) {
86 *e = *expr.clone();
87 return;
88 }
89 }
90
91 e.visit_mut_children_with(self);
92 }
93
94 fn visit_mut_function(&mut self, n: &mut Function) {
101 let scope = Scope::new(&self.scope);
102 let mut v = ConstPropagation { scope };
103 n.visit_mut_children_with(&mut v);
104 }
105
106 fn visit_mut_prop(&mut self, p: &mut Prop) {
107 p.visit_mut_children_with(self);
108
109 if let Prop::Shorthand(i) = p {
110 if let Some(expr) = self.scope.find_var(&i.to_id()) {
111 *p = Prop::KeyValue(KeyValueProp {
112 key: PropName::Ident(i.take().into()),
113 value: expr.clone(),
114 });
115 }
116 }
117 }
118
119 fn visit_mut_var_decl(&mut self, var: &mut VarDecl) {
120 var.decls.visit_mut_with(self);
121
122 if let VarDeclKind::Const = var.kind {
123 for decl in &var.decls {
124 if let Pat::Ident(name) = &decl.name {
125 if let Some(init) = &decl.init {
126 match &**init {
127 Expr::Lit(Lit::Bool(..))
128 | Expr::Lit(Lit::Num(..))
129 | Expr::Lit(Lit::Null(..)) => {
130 self.scope.vars.insert(name.to_id(), init.clone());
131 }
132
133 Expr::Ident(init)
134 if name.span.is_dummy()
135 || var.span.is_dummy()
136 || init.span.is_dummy() =>
137 {
138 if let Some(value) = self.scope.vars.get(&init.to_id()).cloned() {
140 self.scope.vars.insert(name.to_id(), value);
141 } else {
142 self.scope.vars.insert(name.to_id(), init.clone().into());
143 }
144 }
145 _ => {}
146 }
147 }
148 }
149 }
150 }
151 }
152}