swc_ecma_transforms_classes/
super_field.rs1use std::iter;
2
3use swc_common::{util::take::Take, Mark, Span, SyntaxContext, DUMMY_SP};
4use swc_ecma_ast::*;
5use swc_ecma_transforms_base::helper;
6use swc_ecma_utils::{is_rest_arguments, quote_ident, ExprFactory};
7use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
8
9use super::get_prototype_of;
10
11pub struct SuperFieldAccessFolder<'a> {
25 pub class_name: &'a Ident,
26
27 pub constructor_this_mark: Option<Mark>,
29 pub is_static: bool,
30
31 pub folding_constructor: bool,
32
33 pub in_injected_define_property_call: bool,
35
36 pub in_nested_scope: bool,
38
39 pub this_alias_mark: Option<Mark>,
41
42 pub constant_super: bool,
44
45 pub super_class: &'a Option<Ident>,
46
47 pub in_pat: bool,
48}
49
50macro_rules! mark_nested {
51 ($name:ident, $T:tt) => {
52 fn $name(&mut self, n: &mut $T) {
53 if self.folding_constructor && !self.in_injected_define_property_call {
55 let old = self.in_nested_scope;
56 self.in_nested_scope = true;
57 n.visit_mut_children_with(self);
58 self.in_nested_scope = old;
59 } else {
60 n.visit_mut_children_with(self)
61 }
62 }
63 };
64}
65
66impl VisitMut for SuperFieldAccessFolder<'_> {
67 noop_visit_mut_type!();
68
69 mark_nested!(visit_mut_class, Class);
71
72 visit_mut_only_key!();
73
74 fn visit_mut_expr(&mut self, n: &mut Expr) {
75 match n {
76 Expr::This(ThisExpr { span }) if self.in_nested_scope => {
77 *n = quote_ident!(
78 SyntaxContext::empty().apply_mark(
79 *self
80 .this_alias_mark
81 .get_or_insert_with(|| Mark::fresh(Mark::root()))
82 ),
83 *span,
84 "_this"
85 )
86 .into();
87 }
88 Expr::Call(CallExpr {
91 callee: Callee::Expr(expr),
92 ..
93 }) if expr.is_ident_ref_to("_define_property") => {
94 let old = self.in_injected_define_property_call;
95 self.in_injected_define_property_call = true;
96 n.visit_mut_children_with(self);
97 self.in_injected_define_property_call = old;
98 }
99 Expr::SuperProp(..) => {
100 self.visit_mut_super_member_get(n);
101 }
102 Expr::Update(UpdateExpr { arg, .. }) if arg.is_super_prop() => {
103 if let Expr::SuperProp(SuperPropExpr {
104 obj: Super {
105 span: super_token, ..
106 },
107 prop,
108 ..
109 }) = &**arg
110 {
111 *arg = self.super_to_update_call(*super_token, prop.clone()).into();
112 }
113 }
114 Expr::Assign(AssignExpr {
115 ref left,
116 op: op!("="),
117 right,
118 ..
119 }) if is_assign_to_super_prop(left) => {
120 right.visit_mut_with(self);
121 self.visit_mut_super_member_set(n)
122 }
123 Expr::Assign(AssignExpr { left, right, .. }) if is_assign_to_super_prop(left) => {
124 right.visit_mut_with(self);
125 self.visit_mut_super_member_update(n);
126 }
127 Expr::Call(CallExpr {
128 callee: Callee::Expr(callee_expr),
129 args,
130 ..
131 }) if callee_expr.is_super_prop() => {
132 args.visit_mut_children_with(self);
133
134 self.visit_mut_super_member_call(n);
135 }
136 _ => {
137 n.visit_mut_children_with(self);
138 }
139 }
140 }
141
142 fn visit_mut_pat(&mut self, n: &mut Pat) {
143 let in_pat = self.in_pat;
144 self.in_pat = true;
145 n.visit_mut_children_with(self);
146 self.in_pat = in_pat;
147 }
148
149 fn visit_mut_function(&mut self, n: &mut Function) {
150 if self.folding_constructor {
151 return;
152 }
153
154 if self.folding_constructor && !self.in_injected_define_property_call {
155 let old = self.in_nested_scope;
156 self.in_nested_scope = true;
157 n.visit_mut_children_with(self);
158 self.in_nested_scope = old;
159 } else {
160 n.visit_mut_children_with(self);
161 }
162 }
163}
164
165impl SuperFieldAccessFolder<'_> {
166 fn visit_mut_super_member_call(&mut self, n: &mut Expr) {
175 if let Expr::Call(CallExpr {
176 callee: Callee::Expr(callee_expr),
177 args,
178 ..
179 }) = n
180 {
181 if let Expr::SuperProp(SuperPropExpr {
182 obj: Super {
183 span: super_token, ..
184 },
185 prop,
186 ..
187 }) = &**callee_expr
188 {
189 let this = match self.this_alias_mark.or(self.constructor_this_mark) {
190 Some(mark) => {
191 let ident =
192 quote_ident!(SyntaxContext::empty().apply_mark(mark), "_this").as_arg();
193 if self.constant_super {
195 CallExpr {
196 span: DUMMY_SP,
197 callee: helper!(assert_this_initialized),
198 args: vec![ident],
199 ..Default::default()
200 }
201 .as_arg()
202 } else {
203 ident
204 }
205 }
206 _ => ThisExpr { span: DUMMY_SP }.as_arg(),
207 };
208
209 let callee = self.super_to_get_call(*super_token, prop.clone());
210 let mut args = args.clone();
211
212 if args.len() == 1 && is_rest_arguments(&args[0]) {
213 *n = CallExpr {
214 span: DUMMY_SP,
215 callee: callee.make_member(quote_ident!("apply")).as_callee(),
216 args: iter::once(this)
217 .chain(iter::once({
218 let mut arg = args.pop().unwrap();
219 arg.spread = None;
220 arg
221 }))
222 .collect(),
223 ..Default::default()
224 }
225 .into();
226 return;
227 }
228
229 *n = CallExpr {
230 span: DUMMY_SP,
231 callee: callee.make_member(quote_ident!("call")).as_callee(),
232 args: iter::once(this).chain(args).collect(),
233 ..Default::default()
234 }
235 .into();
236 }
237 }
238 }
239
240 fn visit_mut_super_member_set(&mut self, n: &mut Expr) {
248 if let Expr::Assign(AssignExpr {
249 left:
250 AssignTarget::Simple(SimpleAssignTarget::SuperProp(SuperPropExpr {
251 obj: Super { span: super_token },
252 prop,
253 ..
254 })),
255 op: op @ op!("="),
256 right,
257 ..
258 }) = n
259 {
260 *n = self.super_to_set_call(*super_token, prop.take(), *op, right.take());
261 }
262 }
263
264 fn visit_mut_super_member_get(&mut self, n: &mut Expr) {
273 if let Expr::SuperProp(SuperPropExpr {
274 obj: Super { span: super_token },
275 prop,
276 ..
277 }) = n
278 {
279 let super_token = *super_token;
280 prop.visit_mut_children_with(self);
281
282 let prop = prop.take();
283 *n = if self.in_pat {
284 self.super_to_update_call(super_token, prop).into()
285 } else {
286 *self.super_to_get_call(super_token, prop)
287 };
288 }
289 }
290
291 fn visit_mut_super_member_update(&mut self, n: &mut Expr) {
292 if let Expr::Assign(AssignExpr { left, op, .. }) = n {
293 debug_assert_ne!(*op, op!("="));
294
295 if let AssignTarget::Simple(expr) = left {
296 if let SimpleAssignTarget::SuperProp(SuperPropExpr {
297 obj: Super { span: super_token },
298 prop,
299 ..
300 }) = expr.take()
301 {
302 *expr = self.super_to_update_call(super_token, prop).into();
303 }
304 }
305 }
306 }
307
308 fn super_to_get_call(&mut self, super_token: Span, prop: SuperProp) -> Box<Expr> {
309 if self.constant_super {
310 MemberExpr {
311 span: super_token,
312 obj: Box::new({
313 let name = self.super_class.clone().unwrap_or_else(|| {
314 quote_ident!(if self.is_static { "Function" } else { "Object" }).into()
315 });
316 if self.is_static && self.super_class.is_some() {
318 name.into()
319 } else {
320 name.make_member(quote_ident!("prototype")).into()
321 }
322 }),
323 prop: match prop {
324 SuperProp::Ident(i) => MemberProp::Ident(i),
325 SuperProp::Computed(c) => MemberProp::Computed(c),
326 },
327 }
328 .into()
329 } else {
330 let proto_arg = self.proto_arg();
331
332 let prop_arg = prop_arg(prop).as_arg();
333
334 let this_arg = self.this_arg(super_token).as_arg();
335
336 CallExpr {
337 span: super_token,
338 callee: helper!(get),
339 args: vec![proto_arg.as_arg(), prop_arg, this_arg],
340 ..Default::default()
341 }
342 .into()
343 }
344 }
345
346 fn super_to_set_call(
347 &mut self,
348 super_token: Span,
349 prop: SuperProp,
350 op: AssignOp,
351 rhs: Box<Expr>,
352 ) -> Expr {
353 debug_assert_eq!(op, op!("="));
354
355 let this_expr = Box::new(match self.constructor_this_mark {
356 Some(mark) => quote_ident!(
357 SyntaxContext::empty().apply_mark(mark),
358 super_token,
359 "_this"
360 )
361 .into(),
362 None => ThisExpr { span: super_token }.into(),
363 });
364
365 if self.constant_super {
366 let left = MemberExpr {
367 span: super_token,
368 obj: this_expr,
369 prop: match prop {
370 SuperProp::Ident(i) => MemberProp::Ident(i),
371 SuperProp::Computed(c) => MemberProp::Computed(c),
372 },
373 };
374
375 AssignExpr {
376 span: super_token,
377 left: left.into(),
378 op,
379 right: rhs,
380 }
381 .into()
382 } else {
383 let proto_arg = self.proto_arg();
384
385 let prop_arg = prop_arg(prop).as_arg();
386
387 CallExpr {
388 span: super_token,
389 callee: helper!(set),
390 args: vec![
391 proto_arg.as_arg(),
392 prop_arg,
393 rhs.as_arg(),
394 this_expr.as_arg(),
395 true.as_arg(),
397 ],
398 ..Default::default()
399 }
400 .into()
401 }
402 }
403
404 fn super_to_update_call(&mut self, super_token: Span, prop: SuperProp) -> MemberExpr {
405 let proto_arg = self.proto_arg();
406
407 let prop_arg = prop_arg(prop).as_arg();
408
409 let this_arg = self.this_arg(super_token).as_arg();
410
411 let expr: Expr = CallExpr {
412 span: super_token,
413 callee: helper!(update),
414 args: vec![
415 proto_arg.as_arg(),
416 prop_arg,
417 this_arg,
418 true.as_arg(),
420 ],
421 ..Default::default()
422 }
423 .into();
424
425 expr.make_member(quote_ident!("_"))
426 }
427
428 fn proto_arg(&mut self) -> Box<Expr> {
429 let expr = if self.is_static {
430 self.class_name.clone().into()
432 } else {
433 self.class_name
435 .clone()
436 .make_member(quote_ident!("prototype"))
437 .into()
438 };
439
440 if self.constant_super {
441 return expr;
442 }
443
444 let mut proto_arg = get_prototype_of(expr);
445
446 if let Some(mark) = self.constructor_this_mark {
447 let this = quote_ident!(SyntaxContext::empty().apply_mark(mark), "_this");
448
449 proto_arg = SeqExpr {
450 span: DUMMY_SP,
451 exprs: vec![
452 Expr::Call(CallExpr {
453 span: DUMMY_SP,
454 callee: helper!(assert_this_initialized),
455 args: vec![this.as_arg()],
456 ..Default::default()
457 })
458 .into(),
459 proto_arg,
460 ],
461 }
462 .into()
463 }
464
465 proto_arg
466 }
467
468 fn this_arg(&self, super_token: Span) -> Expr {
469 match self.constructor_this_mark {
470 Some(mark) => quote_ident!(
471 SyntaxContext::empty().apply_mark(mark),
472 super_token,
473 "_this"
474 )
475 .into(),
476 None => ThisExpr { span: super_token }.into(),
477 }
478 }
479}
480
481fn is_assign_to_super_prop(left: &AssignTarget) -> bool {
482 match left {
483 AssignTarget::Simple(expr) => expr.is_super_prop(),
484 _ => false,
485 }
486}
487
488fn prop_arg(prop: SuperProp) -> Expr {
489 match prop {
490 SuperProp::Ident(IdentName {
491 sym: value, span, ..
492 }) => Lit::Str(Str {
493 span,
494 raw: None,
495 value,
496 })
497 .into(),
498 SuperProp::Computed(c) => *c.expr,
499 }
500}