1use std::iter;
2
3use swc_common::{util::take::Take, Span, DUMMY_SP};
4use swc_ecma_ast::*;
5use swc_ecma_transforms_base::helper;
6use swc_ecma_utils::{
7 alias_ident_for, is_rest_arguments, prepend_stmt, private_ident, quote_ident, ExprFactory,
8};
9use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
10use swc_trace_macro::swc_trace;
11
12struct ObjectSuper {
13 extra_vars: Vec<Ident>,
14}
15
16pub fn object_super() -> impl Pass {
17 visit_mut_pass(ObjectSuper {
18 extra_vars: Vec::new(),
19 })
20}
21
22#[swc_trace]
23impl VisitMut for ObjectSuper {
24 noop_visit_mut_type!(fail);
25
26 fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
27 n.visit_mut_children_with(self);
28 if !self.extra_vars.is_empty() {
29 prepend_stmt(
30 n,
31 VarDecl {
32 span: DUMMY_SP,
33 kind: VarDeclKind::Var,
34 declare: false,
35 decls: self
36 .extra_vars
37 .take()
38 .into_iter()
39 .map(|v| VarDeclarator {
40 span: DUMMY_SP,
41 name: v.into(),
42 init: None,
43 definite: false,
44 })
45 .collect(),
46 ..Default::default()
47 }
48 .into(),
49 );
50 }
51 }
52
53 fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
54 stmts.visit_mut_children_with(self);
55 if !self.extra_vars.is_empty() {
56 prepend_stmt(
57 stmts,
58 VarDecl {
59 span: DUMMY_SP,
60 kind: VarDeclKind::Var,
61 decls: self
62 .extra_vars
63 .drain(..)
64 .map(|v| VarDeclarator {
65 span: DUMMY_SP,
66 name: v.into(),
67 init: None,
68 definite: false,
69 })
70 .collect(),
71 ..Default::default()
72 }
73 .into(),
74 );
75 }
76 }
77
78 fn visit_mut_expr(&mut self, expr: &mut Expr) {
79 expr.visit_mut_children_with(self);
80 if let Expr::Object(ObjectLit { span: _, props }) = expr {
81 let mut replacer = SuperReplacer {
82 obj: None,
83 vars: Vec::new(),
84 };
85 for prop_or_spread in props.iter_mut() {
86 if let PropOrSpread::Prop(ref mut prop) = prop_or_spread {
87 if let Prop::Method(MethodProp { key: _, function }) = &mut **prop {
88 function.visit_mut_with(&mut replacer);
89 if !replacer.vars.is_empty() {
90 if let Some(BlockStmt { span: _, stmts, .. }) = &mut function.body {
91 prepend_stmt(
92 stmts,
93 VarDecl {
94 span: DUMMY_SP,
95 kind: VarDeclKind::Var,
96 declare: false,
97 decls: replacer
98 .vars
99 .drain(..)
100 .map(|v| VarDeclarator {
101 span: DUMMY_SP,
102 name: v.into(),
103 init: None,
104 definite: false,
105 })
106 .collect(),
107 ..Default::default()
108 }
109 .into(),
110 );
111 }
112 }
113 }
114 }
115 }
116 if let Some(obj) = replacer.obj {
117 *expr = AssignExpr {
118 span: DUMMY_SP,
119 op: op!("="),
120 left: obj.clone().into(),
121 right: Box::new(expr.take()),
122 }
123 .into();
124 self.extra_vars.push(obj);
125 }
126 }
127 }
128}
129
130struct SuperReplacer {
131 obj: Option<Ident>,
132 vars: Vec<Ident>,
133}
134
135#[swc_trace]
136impl VisitMut for SuperReplacer {
137 noop_visit_mut_type!(fail);
138
139 fn visit_mut_object_lit(&mut self, obj: &mut ObjectLit) {
140 for prop_or_spread in obj.props.iter_mut() {
141 if let PropOrSpread::Prop(prop) = prop_or_spread {
142 match &mut **prop {
143 Prop::Method(MethodProp { key, .. })
144 | Prop::Getter(GetterProp { key, .. })
145 | Prop::Setter(SetterProp { key, .. }) => key.visit_mut_with(self),
146 Prop::KeyValue(KeyValueProp { key, value }) => {
147 key.visit_mut_with(self);
148 if !(value.is_fn_expr() || value.is_class()) {
149 value.visit_mut_with(self)
150 }
151 }
152 Prop::Shorthand(_) | Prop::Assign(_) => (),
153 #[cfg(swc_ast_unknown)]
154 _ => panic!("unable to access unknown nodes"),
155 }
156 }
157 }
158 }
159
160 fn visit_mut_expr(&mut self, expr: &mut Expr) {
161 self.visit_mut_super_member_call(expr);
162 self.visit_mut_super_member_set(expr);
163 self.visit_mut_super_member_get(expr);
164
165 expr.visit_mut_children_with(self)
166 }
167}
168
169#[swc_trace]
170impl SuperReplacer {
171 fn get_obj_ref(&mut self) -> Ident {
172 if let Some(obj) = &self.obj {
173 obj.clone()
174 } else {
175 let ident = private_ident!("_obj");
176 self.obj = Some(ident.clone());
177 ident
178 }
179 }
180
181 fn get_proto(&mut self) -> ExprOrSpread {
182 CallExpr {
183 span: DUMMY_SP,
184 callee: helper!(get_prototype_of),
185 args: vec![self.get_obj_ref().as_arg()],
186
187 ..Default::default()
188 }
189 .as_arg()
190 }
191
192 fn normalize_computed_expr(&mut self, prop: &mut SuperProp) -> Box<Expr> {
194 match prop.take() {
195 SuperProp::Ident(IdentName {
196 sym: value, span, ..
197 }) => Lit::Str(Str {
198 raw: None,
199 value: value.into(),
200 span,
201 })
202 .into(),
203
204 SuperProp::Computed(ComputedPropName { expr, .. }) => expr,
205 #[cfg(swc_ast_unknown)]
206 _ => panic!("unable to access unknown nodes"),
207 }
208 }
209
210 fn visit_mut_super_member_call(&mut self, n: &mut Expr) {
219 if let Expr::Call(CallExpr {
220 callee: Callee::Expr(callee_expr),
221 args,
222 ..
223 }) = n
224 {
225 if let Expr::SuperProp(SuperPropExpr {
226 obj: Super { span: super_token },
227 prop,
228 ..
229 }) = &mut **callee_expr
230 {
231 let prop = self.normalize_computed_expr(prop);
232 let callee =
233 SuperReplacer::super_to_get_call(self.get_proto(), *super_token, prop.as_arg());
234 let this = ThisExpr { span: DUMMY_SP }.as_arg();
235 if args.len() == 1 && is_rest_arguments(&args[0]) {
236 *n = CallExpr {
237 span: DUMMY_SP,
238 callee: MemberExpr {
239 span: DUMMY_SP,
240 obj: Box::new(callee),
241 prop: quote_ident!("apply").into(),
242 }
243 .as_callee(),
244 args: iter::once(this)
245 .chain(iter::once({
246 let mut arg = args.pop().unwrap();
247 arg.spread = None;
248 arg
249 }))
250 .collect(),
251 ..Default::default()
252 }
253 .into();
254 return;
255 }
256
257 *n = CallExpr {
258 span: DUMMY_SP,
259 callee: MemberExpr {
260 span: DUMMY_SP,
261 obj: Box::new(callee),
262 prop: MemberProp::Ident(quote_ident!("call")),
263 }
264 .as_callee(),
265 args: iter::once(this).chain(args.take()).collect(),
266 ..Default::default()
267 }
268 .into();
269 }
270 }
271 }
272
273 fn visit_mut_super_member_set(&mut self, n: &mut Expr) {
281 match n {
282 Expr::Update(UpdateExpr {
283 arg, op, prefix, ..
284 }) => {
285 if let Expr::SuperProp(SuperPropExpr {
286 obj: Super { span: super_token },
287 prop,
288 ..
289 }) = &mut **arg
290 {
291 let op = match op {
292 op!("++") => op!("+="),
293 op!("--") => op!("-="),
294 #[cfg(swc_ast_unknown)]
295 _ => panic!("unable to access unknown nodes"),
296 };
297 *n = self.super_to_set_call(*super_token, true, prop, op, 1.0.into(), *prefix);
298 }
299 }
300
301 Expr::Assign(AssignExpr {
302 span,
303 left,
304 op,
305 right,
306 }) => {
307 if let AssignTarget::Simple(SimpleAssignTarget::SuperProp(SuperPropExpr {
308 obj: Super { span: super_token },
309 prop,
310 ..
311 })) = left
312 {
313 *n =
314 self.super_to_set_call(*super_token, false, prop, *op, right.take(), false);
315 return;
316 }
317 left.visit_mut_children_with(self);
318 *n = AssignExpr {
319 span: *span,
320 left: left.take(),
321 op: *op,
322 right: right.take(),
323 }
324 .into();
325 }
326 _ => {}
327 }
328 }
329
330 fn visit_mut_super_member_get(&mut self, n: &mut Expr) {
339 if let Expr::SuperProp(SuperPropExpr {
340 obj: Super {
341 span: super_token, ..
342 },
343 prop,
344 ..
345 }) = n
346 {
347 let prop = self.normalize_computed_expr(prop);
348 *n = SuperReplacer::super_to_get_call(self.get_proto(), *super_token, prop.as_arg());
349 }
350 }
351
352 fn super_to_get_call(proto: ExprOrSpread, super_token: Span, prop: ExprOrSpread) -> Expr {
353 CallExpr {
354 span: super_token,
355 callee: helper!(get),
356 args: vec![proto, prop, ThisExpr { span: super_token }.as_arg()],
357 ..Default::default()
358 }
359 .into()
360 }
361
362 fn to_bin_expr(left: Box<Expr>, op: AssignOp, rhs: Box<Expr>) -> BinExpr {
363 BinExpr {
364 span: DUMMY_SP,
365 left,
366 op: op.to_update().unwrap(),
367 right: rhs,
368 }
369 }
370
371 fn call_set_helper(
372 &mut self,
373 super_token: Span,
374 prop: ExprOrSpread,
375 rhs: ExprOrSpread,
376 ) -> Expr {
377 CallExpr {
378 span: super_token,
379 callee: helper!(set),
380 args: vec![
381 self.get_proto(),
382 prop,
383 rhs,
384 ThisExpr { span: super_token }.as_arg(),
385 true.as_arg(),
387 ],
388 ..Default::default()
389 }
390 .into()
391 }
392
393 fn super_to_set_call(
394 &mut self,
395 super_token: Span,
396 is_update: bool,
397 prop: &mut SuperProp,
398 op: AssignOp,
399 rhs: Box<Expr>,
400 prefix: bool,
401 ) -> Expr {
402 let computed = match prop {
403 SuperProp::Ident(_) => false,
404 SuperProp::Computed(_) => true,
405 #[cfg(swc_ast_unknown)]
406 _ => panic!("unable to access unknown nodes"),
407 };
408 let mut prop = self.normalize_computed_expr(prop);
409 match op {
410 op!("=") => self.call_set_helper(super_token, prop.as_arg(), rhs.as_arg()),
411 _ => {
412 let left = Box::new(SuperReplacer::super_to_get_call(
413 self.get_proto(),
414 super_token,
415 if computed {
416 let ref_ident = alias_ident_for(&rhs, "_ref").into_private();
417 self.vars.push(ref_ident.clone());
418 *prop = AssignExpr {
419 span: DUMMY_SP,
420 left: ref_ident.clone().into(),
421 op: op!("="),
422 right: prop.take(),
423 }
424 .into();
425 ref_ident.as_arg()
426 } else {
427 prop.clone().as_arg()
428 },
429 ));
430 if is_update {
431 if prefix {
432 self.call_set_helper(
433 super_token,
434 prop.as_arg(),
435 SuperReplacer::to_bin_expr(
436 UnaryExpr {
437 span: DUMMY_SP,
438 op: op!(unary, "+"),
439 arg: left,
440 }
441 .into(),
442 op,
443 rhs,
444 )
445 .as_arg(),
446 )
447 } else {
448 let update_ident = alias_ident_for(&rhs, "_super").into_private();
449 self.vars.push(update_ident.clone());
450 SeqExpr {
451 span: DUMMY_SP,
452 exprs: vec![
453 Box::new(
454 self.call_set_helper(
455 super_token,
456 prop.as_arg(),
457 SuperReplacer::to_bin_expr(
458 Box::new(
459 AssignExpr {
460 span: DUMMY_SP,
461 left: update_ident.clone().into(),
462 op: op!("="),
463 right: Box::new(Expr::Unary(UnaryExpr {
464 span: DUMMY_SP,
465 op: op!(unary, "+"),
466 arg: left,
467 })),
468 }
469 .into(),
470 ),
471 op,
472 rhs,
473 )
474 .as_arg(),
475 ),
476 ),
477 Box::new(Expr::Ident(update_ident)),
478 ],
479 }
480 .into()
481 }
482 } else {
483 self.call_set_helper(
484 super_token,
485 prop.as_arg(),
486 SuperReplacer::to_bin_expr(left, op, rhs).as_arg(),
487 )
488 }
489 }
490 }
491 }
492}
493#[cfg(test)]
494mod tests {
495 use swc_common::Mark;
496 use swc_ecma_parser::{EsSyntax, Syntax};
497 use swc_ecma_transforms_base::resolver;
498 use swc_ecma_transforms_testing::test;
499
500 use super::*;
501 use crate::{function_name, shorthand};
502 test!(
503 ::swc_ecma_parser::Syntax::default(),
504 |_| {
505 let unresolved_mark = Mark::new();
506 let top_level_mark = Mark::new();
507 (
508 resolver(unresolved_mark, top_level_mark, false),
509 object_super(),
510 shorthand(),
511 function_name(),
512 )
513 },
514 get,
515 "let obj = {
516 a(){
517 let c = super.x;
518 }
519 }"
520 );
521 test!(
522 ::swc_ecma_parser::Syntax::default(),
523 |_| {
524 (
525 resolver(Mark::new(), Mark::new(), false),
526 object_super(),
527 shorthand(),
528 function_name(),
529 )
530 },
531 call,
532 "let obj = {
533 a(){
534 super.y(1,2,3);
535 }
536 }"
537 );
538 test!(
539 ::swc_ecma_parser::Syntax::default(),
540 |_| {
541 (
542 resolver(Mark::new(), Mark::new(), false),
543 object_super(),
544 shorthand(),
545 function_name(),
546 )
547 },
548 set,
549 "let obj = {
550 a(){
551 super.x = 1;
552 }
553 }"
554 );
555 test!(
556 ::swc_ecma_parser::Syntax::default(),
557 |_| {
558 (
559 resolver(Mark::new(), Mark::new(), false),
560 object_super(),
561 shorthand(),
562 function_name(),
563 )
564 },
565 nest,
566 "let obj = {
567 b(){
568 super.bar()
569 let o = {
570 d(){
571 super.d()
572 }
573 }
574 },
575 }"
576 );
577 test!(
578 Syntax::Es(EsSyntax {
579 allow_super_outside_method: true,
580 ..Default::default()
581 }),
582 |_| {
583 (
584 resolver(Mark::new(), Mark::new(), false),
585 object_super(),
586 shorthand(),
587 function_name(),
588 )
589 },
590 do_not_transform,
591 "let outer = {
592 b(){
593 let inner = {
594 d:function d(){
595 super.d() // should not transform
596 }
597 }
598 },
599 }"
600 );
601}