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 }
154 }
155 }
156 }
157
158 fn visit_mut_expr(&mut self, expr: &mut Expr) {
159 self.visit_mut_super_member_call(expr);
160 self.visit_mut_super_member_set(expr);
161 self.visit_mut_super_member_get(expr);
162
163 expr.visit_mut_children_with(self)
164 }
165}
166
167#[swc_trace]
168impl SuperReplacer {
169 fn get_obj_ref(&mut self) -> Ident {
170 if let Some(obj) = &self.obj {
171 obj.clone()
172 } else {
173 let ident = private_ident!("_obj");
174 self.obj = Some(ident.clone());
175 ident
176 }
177 }
178
179 fn get_proto(&mut self) -> ExprOrSpread {
180 CallExpr {
181 span: DUMMY_SP,
182 callee: helper!(get_prototype_of),
183 args: vec![self.get_obj_ref().as_arg()],
184
185 ..Default::default()
186 }
187 .as_arg()
188 }
189
190 fn normalize_computed_expr(&mut self, prop: &mut SuperProp) -> Box<Expr> {
192 match prop.take() {
193 SuperProp::Ident(IdentName {
194 sym: value, span, ..
195 }) => Lit::Str(Str {
196 raw: None,
197 value,
198 span,
199 })
200 .into(),
201
202 SuperProp::Computed(ComputedPropName { expr, .. }) => expr,
203 }
204 }
205
206 fn visit_mut_super_member_call(&mut self, n: &mut Expr) {
215 if let Expr::Call(CallExpr {
216 callee: Callee::Expr(callee_expr),
217 args,
218 ..
219 }) = n
220 {
221 if let Expr::SuperProp(SuperPropExpr {
222 obj: Super { span: super_token },
223 prop,
224 ..
225 }) = &mut **callee_expr
226 {
227 let prop = self.normalize_computed_expr(prop);
228 let callee =
229 SuperReplacer::super_to_get_call(self.get_proto(), *super_token, prop.as_arg());
230 let this = ThisExpr { span: DUMMY_SP }.as_arg();
231 if args.len() == 1 && is_rest_arguments(&args[0]) {
232 *n = CallExpr {
233 span: DUMMY_SP,
234 callee: MemberExpr {
235 span: DUMMY_SP,
236 obj: Box::new(callee),
237 prop: quote_ident!("apply").into(),
238 }
239 .as_callee(),
240 args: iter::once(this)
241 .chain(iter::once({
242 let mut arg = args.pop().unwrap();
243 arg.spread = None;
244 arg
245 }))
246 .collect(),
247 ..Default::default()
248 }
249 .into();
250 return;
251 }
252
253 *n = CallExpr {
254 span: DUMMY_SP,
255 callee: MemberExpr {
256 span: DUMMY_SP,
257 obj: Box::new(callee),
258 prop: MemberProp::Ident(quote_ident!("call")),
259 }
260 .as_callee(),
261 args: iter::once(this).chain(args.take()).collect(),
262 ..Default::default()
263 }
264 .into();
265 }
266 }
267 }
268
269 fn visit_mut_super_member_set(&mut self, n: &mut Expr) {
277 match n {
278 Expr::Update(UpdateExpr {
279 arg, op, prefix, ..
280 }) => {
281 if let Expr::SuperProp(SuperPropExpr {
282 obj: Super { span: super_token },
283 prop,
284 ..
285 }) = &mut **arg
286 {
287 let op = match op {
288 op!("++") => op!("+="),
289 op!("--") => op!("-="),
290 };
291 *n = self.super_to_set_call(*super_token, true, prop, op, 1.0.into(), *prefix);
292 }
293 }
294
295 Expr::Assign(AssignExpr {
296 span,
297 left,
298 op,
299 right,
300 }) => {
301 if let AssignTarget::Simple(SimpleAssignTarget::SuperProp(SuperPropExpr {
302 obj: Super { span: super_token },
303 prop,
304 ..
305 })) = left
306 {
307 *n =
308 self.super_to_set_call(*super_token, false, prop, *op, right.take(), false);
309 return;
310 }
311 left.visit_mut_children_with(self);
312 *n = AssignExpr {
313 span: *span,
314 left: left.take(),
315 op: *op,
316 right: right.take(),
317 }
318 .into();
319 }
320 _ => {}
321 }
322 }
323
324 fn visit_mut_super_member_get(&mut self, n: &mut Expr) {
333 if let Expr::SuperProp(SuperPropExpr {
334 obj: Super {
335 span: super_token, ..
336 },
337 prop,
338 ..
339 }) = n
340 {
341 let prop = self.normalize_computed_expr(prop);
342 *n = SuperReplacer::super_to_get_call(self.get_proto(), *super_token, prop.as_arg());
343 }
344 }
345
346 fn super_to_get_call(proto: ExprOrSpread, super_token: Span, prop: ExprOrSpread) -> Expr {
347 CallExpr {
348 span: super_token,
349 callee: helper!(get),
350 args: vec![proto, prop, ThisExpr { span: super_token }.as_arg()],
351 ..Default::default()
352 }
353 .into()
354 }
355
356 fn to_bin_expr(left: Box<Expr>, op: AssignOp, rhs: Box<Expr>) -> BinExpr {
357 BinExpr {
358 span: DUMMY_SP,
359 left,
360 op: op.to_update().unwrap(),
361 right: rhs,
362 }
363 }
364
365 fn call_set_helper(
366 &mut self,
367 super_token: Span,
368 prop: ExprOrSpread,
369 rhs: ExprOrSpread,
370 ) -> Expr {
371 CallExpr {
372 span: super_token,
373 callee: helper!(set),
374 args: vec![
375 self.get_proto(),
376 prop,
377 rhs,
378 ThisExpr { span: super_token }.as_arg(),
379 true.as_arg(),
381 ],
382 ..Default::default()
383 }
384 .into()
385 }
386
387 fn super_to_set_call(
388 &mut self,
389 super_token: Span,
390 is_update: bool,
391 prop: &mut SuperProp,
392 op: AssignOp,
393 rhs: Box<Expr>,
394 prefix: bool,
395 ) -> Expr {
396 let computed = match prop {
397 SuperProp::Ident(_) => false,
398 SuperProp::Computed(_) => true,
399 };
400 let mut prop = self.normalize_computed_expr(prop);
401 match op {
402 op!("=") => self.call_set_helper(super_token, prop.as_arg(), rhs.as_arg()),
403 _ => {
404 let left = Box::new(SuperReplacer::super_to_get_call(
405 self.get_proto(),
406 super_token,
407 if computed {
408 let ref_ident = alias_ident_for(&rhs, "_ref").into_private();
409 self.vars.push(ref_ident.clone());
410 *prop = AssignExpr {
411 span: DUMMY_SP,
412 left: ref_ident.clone().into(),
413 op: op!("="),
414 right: prop.take(),
415 }
416 .into();
417 ref_ident.as_arg()
418 } else {
419 prop.clone().as_arg()
420 },
421 ));
422 if is_update {
423 if prefix {
424 self.call_set_helper(
425 super_token,
426 prop.as_arg(),
427 SuperReplacer::to_bin_expr(
428 UnaryExpr {
429 span: DUMMY_SP,
430 op: op!(unary, "+"),
431 arg: left,
432 }
433 .into(),
434 op,
435 rhs,
436 )
437 .as_arg(),
438 )
439 } else {
440 let update_ident = alias_ident_for(&rhs, "_super").into_private();
441 self.vars.push(update_ident.clone());
442 SeqExpr {
443 span: DUMMY_SP,
444 exprs: vec![
445 Box::new(
446 self.call_set_helper(
447 super_token,
448 prop.as_arg(),
449 SuperReplacer::to_bin_expr(
450 Box::new(
451 AssignExpr {
452 span: DUMMY_SP,
453 left: update_ident.clone().into(),
454 op: op!("="),
455 right: Box::new(Expr::Unary(UnaryExpr {
456 span: DUMMY_SP,
457 op: op!(unary, "+"),
458 arg: left,
459 })),
460 }
461 .into(),
462 ),
463 op,
464 rhs,
465 )
466 .as_arg(),
467 ),
468 ),
469 Box::new(Expr::Ident(update_ident)),
470 ],
471 }
472 .into()
473 }
474 } else {
475 self.call_set_helper(
476 super_token,
477 prop.as_arg(),
478 SuperReplacer::to_bin_expr(left, op, rhs).as_arg(),
479 )
480 }
481 }
482 }
483 }
484}
485#[cfg(test)]
486mod tests {
487 use swc_common::Mark;
488 use swc_ecma_parser::{EsSyntax, Syntax};
489 use swc_ecma_transforms_base::resolver;
490 use swc_ecma_transforms_testing::test;
491
492 use super::*;
493 use crate::{function_name, shorthand};
494 test!(
495 ::swc_ecma_parser::Syntax::default(),
496 |_| {
497 let unresolved_mark = Mark::new();
498 let top_level_mark = Mark::new();
499 (
500 resolver(unresolved_mark, top_level_mark, false),
501 object_super(),
502 shorthand(),
503 function_name(),
504 )
505 },
506 get,
507 "let obj = {
508 a(){
509 let c = super.x;
510 }
511 }"
512 );
513 test!(
514 ::swc_ecma_parser::Syntax::default(),
515 |_| {
516 (
517 resolver(Mark::new(), Mark::new(), false),
518 object_super(),
519 shorthand(),
520 function_name(),
521 )
522 },
523 call,
524 "let obj = {
525 a(){
526 super.y(1,2,3);
527 }
528 }"
529 );
530 test!(
531 ::swc_ecma_parser::Syntax::default(),
532 |_| {
533 (
534 resolver(Mark::new(), Mark::new(), false),
535 object_super(),
536 shorthand(),
537 function_name(),
538 )
539 },
540 set,
541 "let obj = {
542 a(){
543 super.x = 1;
544 }
545 }"
546 );
547 test!(
548 ::swc_ecma_parser::Syntax::default(),
549 |_| {
550 (
551 resolver(Mark::new(), Mark::new(), false),
552 object_super(),
553 shorthand(),
554 function_name(),
555 )
556 },
557 nest,
558 "let obj = {
559 b(){
560 super.bar()
561 let o = {
562 d(){
563 super.d()
564 }
565 }
566 },
567 }"
568 );
569 test!(
570 Syntax::Es(EsSyntax {
571 allow_super_outside_method: true,
572 ..Default::default()
573 }),
574 |_| {
575 (
576 resolver(Mark::new(), Mark::new(), false),
577 object_super(),
578 shorthand(),
579 function_name(),
580 )
581 },
582 do_not_transform,
583 "let outer = {
584 b(){
585 let inner = {
586 d:function d(){
587 super.d() // should not transform
588 }
589 }
590 },
591 }"
592 );
593}