swc_ecma_compat_es2016/
exponentiation.rs1use swc_common::{util::take::Take, Span, Spanned, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_transforms_base::perf::{ParExplode, Parallel};
4use swc_ecma_transforms_macros::parallel;
5use swc_ecma_utils::{member_expr, private_ident, ExprFactory};
6use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
7use swc_trace_macro::swc_trace;
8
9pub fn exponentiation() -> impl Pass {
29 visit_mut_pass(Exponentiation::default())
30}
31
32#[derive(Default)]
33struct Exponentiation {
34 vars: Vec<VarDeclarator>,
35}
36
37impl Parallel for Exponentiation {
38 fn create(&self) -> Self {
39 Self::default()
40 }
41
42 fn merge(&mut self, other: Self) {
43 self.vars.extend(other.vars);
44 }
45}
46
47#[swc_trace]
48impl ParExplode for Exponentiation {
49 fn after_one_stmt(&mut self, stmts: &mut Vec<Stmt>) {
50 if !self.vars.is_empty() {
51 stmts.push(
52 VarDecl {
53 span: DUMMY_SP,
54 kind: VarDeclKind::Var,
55 decls: self.vars.take(),
56 declare: false,
57 ..Default::default()
58 }
59 .into(),
60 );
61 }
62 }
63
64 fn after_one_module_item(&mut self, stmts: &mut Vec<ModuleItem>) {
65 if !self.vars.is_empty() {
66 stmts.push(
67 VarDecl {
68 span: DUMMY_SP,
69 kind: VarDeclKind::Var,
70 decls: self.vars.take(),
71 declare: false,
72 ..Default::default()
73 }
74 .into(),
75 );
76 }
77 }
78}
79
80#[swc_trace]
81#[parallel(explode)]
82impl VisitMut for Exponentiation {
83 noop_visit_mut_type!(fail);
84
85 fn visit_mut_expr(&mut self, e: &mut Expr) {
86 e.visit_mut_children_with(self);
87
88 match e {
89 Expr::Assign(AssignExpr {
90 span,
91 left,
92 op: op @ op!("**="),
93 right,
94 }) => {
95 let lhs: Ident = match left {
96 _ if left.as_ident().is_some() => left.as_ident().unwrap().clone().into(),
97
98 AssignTarget::Simple(ref e) => {
100 let ref_ident = private_ident!(e.span(), "ref");
101
102 self.vars.push(VarDeclarator {
103 span: DUMMY_SP,
104 name: ref_ident.clone().into(),
105 init: Some(e.clone().into()),
106 definite: false,
107 });
108 ref_ident
109 }
110
111 left => {
112 *e = AssignExpr {
113 span: *span,
114 left: left.take(),
115 op: op!("="),
116 right: right.take(),
117 }
118 .into();
119 return;
120 }
121 };
122
123 *op = op!("=");
124 *right = Box::new(mk_call(*span, Box::new(lhs.into()), right.take()));
125 }
126 Expr::Bin(BinExpr {
127 span,
128 left,
129 op: op!("**"),
130 right,
131 }) => {
132 *e = mk_call(*span, left.take(), right.take());
133 }
134 _ => {}
135 }
136 }
137}
138
139#[tracing::instrument(level = "debug", skip_all)]
140fn mk_call(span: Span, left: Box<Expr>, right: Box<Expr>) -> Expr {
141 CallExpr {
143 span,
144 callee: member_expr!(Default::default(), span, Math.pow).as_callee(),
145
146 args: vec![left.as_arg(), right.as_arg()],
147 ..Default::default()
148 }
149 .into()
150}
151
152#[cfg(test)]
153mod tests {
154 use swc_ecma_transforms_testing::{test, test_exec};
155
156 use super::*;
157
158 test!(
159 ::swc_ecma_parser::Syntax::default(),
160 |_| exponentiation(),
161 babel_binary,
162 "2 ** 2"
163 );
164
165 test_exec!(
166 ignore,
167 ::swc_ecma_parser::Syntax::default(),
168 |_| exponentiation(),
169 babel_comprehensive,
170 r#"expect(2 ** 3).toBe(8);
171expect(3 * 2 ** 3).toBe(24);
172var x = 2;
173expect(2 ** ++x).toBe(8);
174expect(2 ** -1 * 2).toBe(1);
175
176var calls = 0;
177var q = {q: 3};
178var o = {
179 get p() {
180 calls++;
181 return q;
182 }
183};
184
185o.p.q **= 2;
186expect(calls).toBe(1);
187expect(o.p.q).toBe(9);
188
189expect(2 ** (3 ** 2)).toBe(512);
190expect(2 ** 3 ** 2).toBe(512);"#
191 );
192
193 test_exec!(
194 ignore,
196 ::swc_ecma_parser::Syntax::default(),
197 |_| exponentiation(),
198 babel_memoize_object,
199 r#"var counters = 0;
200Object.defineProperty(global, "reader", {
201 get: function () {
202 counters += 1;
203 return { x: 2 };
204 },
205 configurable: true
206});
207reader.x **= 2;
208expect(counters).toBe(1);"#
209 );
210
211 test!(
212 ::swc_ecma_parser::Syntax::default(),
213 |_| exponentiation(),
214 assign,
215 r#"x **= 3"#,
216 ok_if_code_eq
217 );
218
219 test!(
231 ::swc_ecma_parser::Syntax::default(),
232 |_| exponentiation(),
233 issue_740,
234 "self.a = 10 ** 2",
235 ok_if_code_eq
236 );
237
238 test!(
241 ::swc_ecma_parser::Syntax::default(),
242 |_| exponentiation(),
243 babel_binary_member_assignment_expression,
244 "var x = {}; x.a = 2 ** 2"
245 );
246
247 test!(
248 ::swc_ecma_parser::Syntax::default(),
249 |_| exponentiation(),
250 assign_to_object_property,
251 r#"var self = {}; self.x **= 3"#,
252 ok_if_code_eq
253 );
254}