1use std::mem;
2
3use indexmap::IndexMap;
4use rustc_hash::FxBuildHasher;
5use swc_atoms::{atom, Atom};
6use swc_common::{util::take::Take, Span, Spanned, SyntaxContext, DUMMY_SP};
7use swc_ecma_ast::*;
8use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
9
10use crate::ExprFactory;
11
12#[derive(Default)]
13struct SuperField {
14 computed: Option<Ident>,
15 ident: IndexMap<Atom, Ident, FxBuildHasher>,
16}
17
18#[derive(Default)]
22pub struct FnEnvHoister {
23 unresolved_ctxt: SyntaxContext,
24 this: Option<Ident>,
25 args: Option<Ident>,
26 new_target: Option<Ident>,
27 super_get: SuperField,
28 super_set: SuperField,
29 super_update: SuperField,
30
31 arguments_disabled: bool,
32 this_disabled: bool,
33 super_disabled: bool,
34
35 in_pat: bool,
36
37 extra_ident: Vec<Ident>,
39}
40
41impl FnEnvHoister {
42 pub fn new(unresolved_ctxt: SyntaxContext) -> Self {
43 Self {
44 unresolved_ctxt,
45 ..Default::default()
46 }
47 }
48
49 pub fn disable_arguments(&mut self) {
51 self.arguments_disabled = true;
52 }
53
54 pub fn disable_this(&mut self) {
56 self.this_disabled = true;
57 }
58
59 pub fn disable_super(&mut self) {
61 self.super_disabled = true;
62 }
63
64 pub fn take(&mut self) -> Self {
65 let mut new = Self {
66 unresolved_ctxt: self.unresolved_ctxt,
67 ..Default::default()
68 };
69
70 mem::swap(self, &mut new);
71
72 new
73 }
74
75 pub fn to_decl(self) -> Vec<VarDeclarator> {
76 let Self {
77 this,
78 args,
79 new_target,
80 super_get,
81 super_set,
82 super_update,
83 ..
84 } = self;
85
86 let mut decls = Vec::with_capacity(3);
87 if let Some(this_id) = this {
88 decls.push(VarDeclarator {
89 span: DUMMY_SP,
90 name: this_id.into(),
91 init: Some(ThisExpr { span: DUMMY_SP }.into()),
92 definite: false,
93 });
94 }
95 if let Some(id) = args {
96 decls.push(VarDeclarator {
97 span: DUMMY_SP,
98 name: id.into(),
99 init: Some(Ident::new_no_ctxt(atom!("arguments"), DUMMY_SP).into()),
100 definite: false,
101 });
102 }
103 if let Some(id) = new_target {
104 decls.push(VarDeclarator {
105 span: DUMMY_SP,
106 name: id.into(),
107 init: Some(
108 MetaPropExpr {
109 span: DUMMY_SP,
110 kind: MetaPropKind::NewTarget,
111 }
112 .into(),
113 ),
114 definite: false,
115 });
116 }
117 extend_super(&mut decls, super_get, super_set, super_update);
118 decls
119 }
120
121 pub fn to_stmt(self) -> Option<Stmt> {
122 let decls = self.to_decl();
123
124 if decls.is_empty() {
125 None
126 } else {
127 Some(
128 VarDecl {
129 kind: VarDeclKind::Var,
130 decls,
131 ..Default::default()
132 }
133 .into(),
134 )
135 }
136 }
137
138 pub fn to_stmt_in_subclass(self) -> (Option<Stmt>, Option<Ident>) {
139 let Self {
140 this,
141 args,
142 new_target,
143 super_get,
144 super_set,
145 super_update,
146 ..
147 } = self;
148
149 let mut decls = Vec::with_capacity(3);
150 if let Some(this_id) = &this {
151 decls.push(VarDeclarator {
152 span: DUMMY_SP,
153 name: this_id.clone().into(),
154 init: None,
155 definite: false,
156 });
157 }
158 if let Some(id) = args {
159 decls.push(VarDeclarator {
160 span: DUMMY_SP,
161 name: id.into(),
162 init: Some(Ident::new_no_ctxt(atom!("arguments"), DUMMY_SP).into()),
163 definite: false,
164 });
165 }
166 if let Some(id) = new_target {
167 decls.push(VarDeclarator {
168 span: DUMMY_SP,
169 name: id.into(),
170 init: Some(
171 MetaPropExpr {
172 span: DUMMY_SP,
173 kind: MetaPropKind::NewTarget,
174 }
175 .into(),
176 ),
177 definite: false,
178 });
179 }
180
181 extend_super(&mut decls, super_get, super_set, super_update);
182
183 if decls.is_empty() {
184 (None, None)
185 } else {
186 (
187 Some(
188 VarDecl {
189 kind: VarDeclKind::Var,
190 decls,
191 ..Default::default()
192 }
193 .into(),
194 ),
195 this,
196 )
197 }
198 }
199
200 fn get_this(&mut self) -> Ident {
201 self.this
202 .get_or_insert_with(|| private_ident!("_this"))
203 .clone()
204 }
205
206 fn super_get(&mut self, prop_name: &Atom, prop_span: Span) -> Ident {
207 if let Some(callee) = self.super_get.ident.get(prop_name) {
208 callee.clone()
209 } else {
210 let ident = private_ident!(prop_span, format!("_superprop_get_{}", prop_name));
211 self.super_get
212 .ident
213 .insert(prop_name.clone(), ident.clone());
214 ident
215 }
216 }
217
218 fn super_get_computed(&mut self, span: Span) -> Ident {
219 self.super_get
220 .computed
221 .get_or_insert_with(|| private_ident!(span, "_superprop_get"))
222 .clone()
223 }
224
225 fn super_set(&mut self, prop_name: &Atom, prop_span: Span) -> Ident {
226 if let Some(callee) = self.super_set.ident.get(prop_name) {
227 callee.clone()
228 } else {
229 let ident = private_ident!(prop_span, format!("_superprop_set_{}", prop_name));
230 self.super_set
231 .ident
232 .insert(prop_name.clone(), ident.clone());
233 ident
234 }
235 }
236
237 fn super_set_computed(&mut self, span: Span) -> Ident {
238 self.super_set
239 .computed
240 .get_or_insert_with(|| private_ident!(span, "_superprop_set"))
241 .clone()
242 }
243
244 fn super_update(&mut self, prop_name: &Atom, prop_span: Span) -> Ident {
245 if let Some(callee) = self.super_update.ident.get(prop_name) {
246 callee.clone()
247 } else {
248 self.super_get
249 .ident
250 .entry(prop_name.clone())
251 .or_insert_with(|| {
252 private_ident!(prop_span, format!("_superprop_get_{}", prop_name))
253 });
254
255 self.super_set
256 .ident
257 .entry(prop_name.clone())
258 .or_insert_with(|| {
259 private_ident!(prop_span, format!("_superprop_set_{}", prop_name))
260 });
261
262 let ident = private_ident!(prop_span, format!("_superprop_update_{}", prop_name));
263 self.super_update
264 .ident
265 .insert(prop_name.clone(), ident.clone());
266 ident
267 }
268 }
269
270 fn super_update_computed(&mut self, span: Span) -> Ident {
271 self.super_get
272 .computed
273 .get_or_insert_with(|| private_ident!(span, "_superprop_get"));
274 self.super_set
275 .computed
276 .get_or_insert_with(|| private_ident!(span, "_superprop_set"));
277 self.super_update
278 .computed
279 .get_or_insert_with(|| private_ident!(span, "_superprop_update"))
280 .clone()
281 }
282}
283
284impl VisitMut for FnEnvHoister {
285 noop_visit_mut_type!(fail);
286
287 fn visit_mut_assign_target_pat(&mut self, n: &mut AssignTargetPat) {
288 let in_pat = self.in_pat;
289 self.in_pat = true;
290 n.visit_mut_children_with(self);
291 self.in_pat = in_pat;
292 }
293
294 fn visit_mut_block_stmt(&mut self, b: &mut BlockStmt) {
295 b.visit_mut_children_with(self);
296
297 if !self.extra_ident.is_empty() {
299 b.stmts.insert(
300 0,
301 VarDecl {
302 kind: VarDeclKind::Var,
303 decls: self
304 .extra_ident
305 .take()
306 .into_iter()
307 .map(|ident| VarDeclarator {
308 span: DUMMY_SP,
309 name: ident.into(),
310 init: None,
311 definite: false,
312 })
313 .collect(),
314 ..Default::default()
315 }
316 .into(),
317 )
318 }
319 }
320
321 fn visit_mut_block_stmt_or_expr(&mut self, b: &mut BlockStmtOrExpr) {
322 b.visit_mut_children_with(self);
323
324 if !self.extra_ident.is_empty() {
326 if let BlockStmtOrExpr::Expr(e) = b {
327 *b = BlockStmtOrExpr::BlockStmt(BlockStmt {
328 stmts: vec![
329 Stmt::Decl(Decl::Var(Box::new(VarDecl {
330 kind: VarDeclKind::Var,
331 decls: self
332 .extra_ident
333 .take()
334 .into_iter()
335 .map(|ident| VarDeclarator {
336 span: DUMMY_SP,
337 name: ident.into(),
338 init: None,
339 definite: false,
340 })
341 .collect(),
342 ..Default::default()
343 }))),
344 Stmt::Return(ReturnStmt {
345 span: e.span(),
346 arg: Some(e.take()),
347 }),
348 ],
349 ..Default::default()
350 })
351 }
352 }
353 }
354
355 fn visit_mut_class(&mut self, _: &mut Class) {}
357
358 fn visit_mut_expr(&mut self, e: &mut Expr) {
359 match e {
360 Expr::Ident(Ident { ctxt, sym, .. })
361 if !self.arguments_disabled
362 && *sym == "arguments"
363 && (*ctxt == self.unresolved_ctxt || *ctxt == SyntaxContext::empty()) =>
364 {
365 let arguments = self
366 .args
367 .get_or_insert_with(|| private_ident!("_arguments"));
368 *e = arguments.clone().into();
369 }
370 Expr::This(..) if !self.this_disabled => {
371 let this = self.get_this();
372 *e = this.into();
373 }
374 Expr::MetaProp(MetaPropExpr {
375 kind: MetaPropKind::NewTarget,
376 ..
377 }) => {
378 let target = self
379 .new_target
380 .get_or_insert_with(|| private_ident!("_newtarget"));
381 *e = target.clone().into();
382 }
383 Expr::Assign(AssignExpr {
385 left,
386 right,
387 span,
388 op,
389 }) => {
390 let expr = match left {
391 AssignTarget::Simple(e) => e,
392 AssignTarget::Pat(..) => {
393 e.visit_mut_children_with(self);
394 return;
395 }
396 #[cfg(swc_ast_unknown)]
397 _ => return,
398 };
399 if !self.super_disabled {
400 if let SimpleAssignTarget::SuperProp(super_prop) = &mut *expr {
401 let left_span = super_prop.span;
402 match &mut super_prop.prop {
403 SuperProp::Computed(c) => {
404 let callee = self.super_set_computed(left_span);
405
406 let op = op.to_update();
407
408 let args = if let Some(op) = op {
409 let tmp = private_ident!("tmp");
410 self.extra_ident.push(tmp.clone());
411 vec![
412 Expr::Assign(AssignExpr {
413 span: DUMMY_SP,
414 left: tmp.clone().into(),
415 op: op!("="),
416 right: c.expr.take(),
417 })
418 .as_arg(),
419 Expr::Bin(BinExpr {
420 span: DUMMY_SP,
421 left: Box::new(Expr::Call(CallExpr {
422 span: DUMMY_SP,
423 callee: self
424 .super_get_computed(DUMMY_SP)
425 .as_callee(),
426 args: vec![tmp.as_arg()],
427 ..Default::default()
428 })),
429 op,
430 right: right.take(),
431 })
432 .as_arg(),
433 ]
434 } else {
435 vec![c.expr.take().as_arg(), right.take().as_arg()]
436 };
437 *e = CallExpr {
438 span: *span,
439 args,
440 callee: callee.as_callee(),
441 ..Default::default()
442 }
443 .into();
444 }
445 SuperProp::Ident(id) => {
446 let callee = self.super_set(&id.sym, left_span);
447 *e = CallExpr {
448 span: *span,
449 args: vec![(if let Some(op) = op.to_update() {
450 Box::new(Expr::Bin(BinExpr {
451 span: DUMMY_SP,
452 left: Box::new(
453 self.super_get(&id.sym, id.span)
454 .as_call(id.span, Vec::new()),
455 ),
456 op,
457 right: right.take(),
458 }))
459 } else {
460 right.take()
461 })
462 .as_arg()],
463 callee: callee.as_callee(),
464 ..Default::default()
465 }
466 .into();
467 }
468 #[cfg(swc_ast_unknown)]
469 _ => (),
470 }
471 }
472 }
473 e.visit_mut_children_with(self)
474 }
475 Expr::Call(CallExpr {
477 span,
478 callee: Callee::Expr(expr),
479 args,
480 ..
481 }) => {
482 if !self.super_disabled {
483 if let Expr::SuperProp(super_prop) = &mut **expr {
484 match &mut super_prop.prop {
485 SuperProp::Computed(c) => {
486 let callee = self.super_get_computed(super_prop.span);
487 let call: Expr = CallExpr {
488 span: *span,
489 args: vec![c.expr.take().as_arg()],
490 callee: callee.as_callee(),
491 ..Default::default()
492 }
493 .into();
494 let mut new_args = args.take();
495
496 new_args.insert(0, self.get_this().as_arg());
497
498 *e = call.call_fn(*span, new_args);
499 }
500 SuperProp::Ident(id) => {
501 let callee = self.super_get(&id.sym, super_prop.span);
502 let call: Expr = CallExpr {
503 span: *span,
504 args: Vec::new(),
505 callee: callee.as_callee(),
506 ..Default::default()
507 }
508 .into();
509 let mut new_args = args.take();
510
511 new_args.insert(0, self.get_this().as_arg());
512
513 *e = call.call_fn(*span, new_args);
514 }
515 #[cfg(swc_ast_unknown)]
516 _ => (),
517 }
518 };
519 }
520 e.visit_mut_children_with(self)
521 }
522 Expr::Update(UpdateExpr { arg, .. }) if arg.is_super_prop() => {
524 let in_pat = self.in_pat;
525 self.in_pat = true;
527 arg.visit_mut_with(self);
528 self.in_pat = in_pat;
529 }
530 Expr::SuperProp(SuperPropExpr { prop, span, .. }) if !self.super_disabled => match prop
531 {
532 SuperProp::Computed(c) => {
533 c.expr.visit_mut_children_with(self);
534 *e = if self.in_pat {
535 Expr::from(CallExpr {
536 span: *span,
537 args: vec![c.expr.take().as_arg()],
538 callee: self.super_update_computed(*span).as_callee(),
539 ..Default::default()
540 })
541 .make_member(atom!("_").into())
542 .into()
543 } else {
544 CallExpr {
545 span: *span,
546 args: vec![c.expr.take().as_arg()],
547 callee: self.super_get_computed(*span).as_callee(),
548 ..Default::default()
549 }
550 .into()
551 };
552 }
553 SuperProp::Ident(id) => {
554 *e = if self.in_pat {
555 self.super_update(&id.sym, *span)
556 .make_member(quote_ident!("_"))
557 .into()
558 } else {
559 CallExpr {
560 span: *span,
561 args: Vec::new(),
562 callee: self.super_get(&id.sym, *span).as_callee(),
563 ..Default::default()
564 }
565 .into()
566 };
567 }
568 #[cfg(swc_ast_unknown)]
569 _ => (),
570 },
571 _ => e.visit_mut_children_with(self),
572 }
573 }
574
575 fn visit_mut_function(&mut self, _: &mut Function) {}
577
578 fn visit_mut_getter_prop(&mut self, p: &mut GetterProp) {
580 if p.key.is_computed() {
581 p.key.visit_mut_with(self);
582 }
583 }
584
585 fn visit_mut_method_prop(&mut self, p: &mut MethodProp) {
586 if p.key.is_computed() {
587 p.key.visit_mut_with(self);
588 }
589 }
590
591 fn visit_mut_pat(&mut self, n: &mut Pat) {
592 let in_pat = self.in_pat;
593 self.in_pat = true;
594 n.visit_mut_children_with(self);
595 self.in_pat = in_pat;
596 }
597
598 fn visit_mut_setter_prop(&mut self, p: &mut SetterProp) {
599 if p.key.is_computed() {
600 p.key.visit_mut_with(self);
601 }
602 }
603}
604
605pub fn init_this(stmts: &mut Vec<Stmt>, this_id: &Ident) {
606 stmts.visit_mut_children_with(&mut InitThis { this_id })
607}
608
609struct InitThis<'a> {
610 this_id: &'a Ident,
611}
612
613impl VisitMut for InitThis<'_> {
615 noop_visit_mut_type!(fail);
616
617 fn visit_mut_class(&mut self, _: &mut Class) {}
618
619 fn visit_mut_expr(&mut self, expr: &mut Expr) {
624 expr.visit_mut_children_with(self);
625
626 if let Expr::Call(
627 call_expr @ CallExpr {
628 callee: Callee::Super(..),
629 ..
630 },
631 ) = expr
632 {
633 let span = call_expr.span;
634 *expr = ParenExpr {
635 span,
636 expr: SeqExpr {
637 span,
638 exprs: vec![
639 Box::new(Expr::Call(call_expr.take())),
640 Box::new(Expr::Assign(AssignExpr {
641 span: DUMMY_SP,
642 left: self.this_id.clone().into(),
643 op: AssignOp::Assign,
644 right: Box::new(Expr::This(ThisExpr { span: DUMMY_SP })),
645 })),
646 ],
647 }
648 .into(),
649 }
650 .into()
651 }
652 }
653}
654
655fn extend_super(
656 decls: &mut Vec<VarDeclarator>,
657 get: SuperField,
658 set: SuperField,
659 update: SuperField,
660) {
661 decls.extend(update.ident.into_iter().map(|(key, ident)| {
662 let value = private_ident!("v");
663 VarDeclarator {
664 span: DUMMY_SP,
665 name: ident.into(),
666 init: Some(
667 ObjectLit {
668 span: DUMMY_SP,
669 props: vec![
670 Prop::Getter(GetterProp {
671 span: DUMMY_SP,
672 key: PropName::Ident(atom!("_").into()),
673 type_ann: None,
674 body: Some(BlockStmt {
675 stmts: vec![Expr::Ident(
676 get.ident
677 .get(&key)
678 .cloned()
679 .expect("getter not found")
680 .without_loc(),
681 )
682 .as_call(DUMMY_SP, Default::default())
683 .into_return_stmt()
684 .into()],
685 ..Default::default()
686 }),
687 }),
688 Prop::Setter(SetterProp {
689 span: DUMMY_SP,
690 key: PropName::Ident(atom!("_").into()),
691 this_param: None,
692 param: value.clone().into(),
693 body: Some(BlockStmt {
694 stmts: vec![Expr::Ident(
695 set.ident
696 .get(&key)
697 .cloned()
698 .expect("setter not found")
699 .without_loc(),
700 )
701 .as_call(DUMMY_SP, vec![value.as_arg()])
702 .into_stmt()],
703 ..Default::default()
704 }),
705 }),
706 ]
707 .into_iter()
708 .map(Box::new)
709 .map(From::from)
710 .collect(),
711 }
712 .into(),
713 ),
714 definite: false,
715 }
716 }));
717 if let Some(id) = update.computed {
718 let prop = private_ident!("_prop");
719 let value = private_ident!("v");
720
721 decls.push(VarDeclarator {
722 span: DUMMY_SP,
723 name: id.into(),
724 init: Some(
725 ArrowExpr {
726 span: DUMMY_SP,
727 params: vec![prop.clone().into()],
728 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
729 ObjectLit {
730 span: DUMMY_SP,
731 props: vec![
732 Prop::Getter(GetterProp {
733 span: DUMMY_SP,
734 key: PropName::Ident(atom!("_").into()),
735 type_ann: None,
736 body: Some(BlockStmt {
737 stmts: vec![Expr::Ident(
738 get.computed
739 .clone()
740 .expect("getter computed not found")
741 .without_loc(),
742 )
743 .as_call(DUMMY_SP, vec![prop.clone().as_arg()])
744 .into_return_stmt()
745 .into()],
746 ..Default::default()
747 }),
748 }),
749 Prop::Setter(SetterProp {
750 span: DUMMY_SP,
751 key: PropName::Ident(atom!("_").into()),
752 this_param: None,
753 param: value.clone().into(),
754 body: Some(BlockStmt {
755 stmts: vec![Expr::Ident(
756 set.computed
757 .clone()
758 .expect("setter computed not found")
759 .without_loc(),
760 )
761 .as_call(DUMMY_SP, vec![prop.as_arg(), value.as_arg()])
762 .into_return_stmt()
763 .into()],
764 ..Default::default()
765 }),
766 }),
767 ]
768 .into_iter()
769 .map(Box::new)
770 .map(From::from)
771 .collect(),
772 }
773 .into(),
774 ))),
775 ..Default::default()
776 }
777 .into(),
778 ),
779 definite: false,
780 });
781 }
782 decls.extend(get.ident.into_iter().map(|(key, ident)| {
783 VarDeclarator {
784 span: DUMMY_SP,
785 name: ident.without_loc().into(),
786 init: Some(
787 ArrowExpr {
788 span: DUMMY_SP,
789 params: Vec::new(),
790 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
791 SuperPropExpr {
792 obj: Super { span: DUMMY_SP },
793 prop: SuperProp::Ident(key.into()),
794 span: DUMMY_SP,
795 }
796 .into(),
797 ))),
798 ..Default::default()
799 }
800 .into(),
801 ),
802 definite: false,
803 }
804 }));
805 if let Some(id) = get.computed {
806 let param = private_ident!("_prop");
807 decls.push(VarDeclarator {
808 span: DUMMY_SP,
809 name: id.without_loc().into(),
810 init: Some(
811 ArrowExpr {
812 span: DUMMY_SP,
813 params: vec![param.clone().into()],
814 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
815 SuperPropExpr {
816 obj: Super { span: DUMMY_SP },
817 prop: SuperProp::Computed(ComputedPropName {
818 span: DUMMY_SP,
819 expr: Box::new(Expr::Ident(param)),
820 }),
821 span: DUMMY_SP,
822 }
823 .into(),
824 ))),
825 ..Default::default()
826 }
827 .into(),
828 ),
829 definite: false,
830 });
831 }
832 decls.extend(set.ident.into_iter().map(|(key, ident)| {
833 let value = private_ident!("_value");
834 VarDeclarator {
835 span: DUMMY_SP,
836 name: ident.without_loc().into(),
837 init: Some(
838 ArrowExpr {
839 span: DUMMY_SP,
840 params: vec![value.clone().into()],
841 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
842 AssignExpr {
843 span: DUMMY_SP,
844 left: SuperPropExpr {
845 obj: Super { span: DUMMY_SP },
846 prop: SuperProp::Ident(key.into()),
847 span: DUMMY_SP,
848 }
849 .into(),
850 op: op!("="),
851 right: Box::new(Expr::Ident(value)),
852 }
853 .into(),
854 ))),
855 ..Default::default()
856 }
857 .into(),
858 ),
859 definite: false,
860 }
861 }));
862 if let Some(id) = set.computed {
863 let prop = private_ident!("_prop");
864 let value = private_ident!("_value");
865 decls.push(VarDeclarator {
866 span: DUMMY_SP,
867 name: id.without_loc().into(),
868 init: Some(
869 ArrowExpr {
870 span: DUMMY_SP,
871 params: vec![prop.clone().into(), value.clone().into()],
872 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
873 AssignExpr {
874 span: DUMMY_SP,
875 left: SuperPropExpr {
876 obj: Super { span: DUMMY_SP },
877 prop: SuperProp::Computed(ComputedPropName {
878 span: DUMMY_SP,
879 expr: Box::new(Expr::Ident(prop)),
880 }),
881 span: DUMMY_SP,
882 }
883 .into(),
884 op: op!("="),
885 right: Box::new(Expr::Ident(value)),
886 }
887 .into(),
888 ))),
889 ..Default::default()
890 }
891 .into(),
892 ),
893 definite: false,
894 });
895 }
896}