1use std::mem;
2
3use indexmap::IndexMap;
4use rustc_hash::FxBuildHasher;
5use swc_atoms::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("arguments".into(), 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("arguments".into(), 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 };
397 if !self.super_disabled {
398 if let SimpleAssignTarget::SuperProp(super_prop) = &mut *expr {
399 let left_span = super_prop.span;
400 match &mut super_prop.prop {
401 SuperProp::Computed(c) => {
402 let callee = self.super_set_computed(left_span);
403
404 let op = op.to_update();
405
406 let args = if let Some(op) = op {
407 let tmp = private_ident!("tmp");
408 self.extra_ident.push(tmp.clone());
409 vec![
410 Expr::Assign(AssignExpr {
411 span: DUMMY_SP,
412 left: tmp.clone().into(),
413 op: op!("="),
414 right: c.expr.take(),
415 })
416 .as_arg(),
417 Expr::Bin(BinExpr {
418 span: DUMMY_SP,
419 left: Box::new(Expr::Call(CallExpr {
420 span: DUMMY_SP,
421 callee: self
422 .super_get_computed(DUMMY_SP)
423 .as_callee(),
424 args: vec![tmp.as_arg()],
425 ..Default::default()
426 })),
427 op,
428 right: right.take(),
429 })
430 .as_arg(),
431 ]
432 } else {
433 vec![c.expr.take().as_arg(), right.take().as_arg()]
434 };
435 *e = CallExpr {
436 span: *span,
437 args,
438 callee: callee.as_callee(),
439 ..Default::default()
440 }
441 .into();
442 }
443 SuperProp::Ident(id) => {
444 let callee = self.super_set(&id.sym, left_span);
445 *e = CallExpr {
446 span: *span,
447 args: vec![(if let Some(op) = op.to_update() {
448 Box::new(Expr::Bin(BinExpr {
449 span: DUMMY_SP,
450 left: Box::new(
451 self.super_get(&id.sym, id.span)
452 .as_call(id.span, Vec::new()),
453 ),
454 op,
455 right: right.take(),
456 }))
457 } else {
458 right.take()
459 })
460 .as_arg()],
461 callee: callee.as_callee(),
462 ..Default::default()
463 }
464 .into();
465 }
466 }
467 }
468 }
469 e.visit_mut_children_with(self)
470 }
471 Expr::Call(CallExpr {
473 span,
474 callee: Callee::Expr(expr),
475 args,
476 ..
477 }) => {
478 if !self.super_disabled {
479 if let Expr::SuperProp(super_prop) = &mut **expr {
480 match &mut super_prop.prop {
481 SuperProp::Computed(c) => {
482 let callee = self.super_get_computed(super_prop.span);
483 let call: Expr = CallExpr {
484 span: *span,
485 args: vec![c.expr.take().as_arg()],
486 callee: callee.as_callee(),
487 ..Default::default()
488 }
489 .into();
490 let mut new_args = args.take();
491
492 new_args.insert(0, self.get_this().as_arg());
493
494 *e = call.call_fn(*span, new_args);
495 }
496 SuperProp::Ident(id) => {
497 let callee = self.super_get(&id.sym, super_prop.span);
498 let call: Expr = CallExpr {
499 span: *span,
500 args: Vec::new(),
501 callee: callee.as_callee(),
502 ..Default::default()
503 }
504 .into();
505 let mut new_args = args.take();
506
507 new_args.insert(0, self.get_this().as_arg());
508
509 *e = call.call_fn(*span, new_args);
510 }
511 }
512 };
513 }
514 e.visit_mut_children_with(self)
515 }
516 Expr::Update(UpdateExpr { arg, .. }) if arg.is_super_prop() => {
518 let in_pat = self.in_pat;
519 self.in_pat = true;
521 arg.visit_mut_with(self);
522 self.in_pat = in_pat;
523 }
524 Expr::SuperProp(SuperPropExpr { prop, span, .. }) if !self.super_disabled => match prop
525 {
526 SuperProp::Computed(c) => {
527 c.expr.visit_mut_children_with(self);
528 *e = if self.in_pat {
529 Expr::from(CallExpr {
530 span: *span,
531 args: vec![c.expr.take().as_arg()],
532 callee: self.super_update_computed(*span).as_callee(),
533 ..Default::default()
534 })
535 .make_member("_".into())
536 .into()
537 } else {
538 CallExpr {
539 span: *span,
540 args: vec![c.expr.take().as_arg()],
541 callee: self.super_get_computed(*span).as_callee(),
542 ..Default::default()
543 }
544 .into()
545 };
546 }
547 SuperProp::Ident(id) => {
548 *e = if self.in_pat {
549 self.super_update(&id.sym, *span)
550 .make_member(quote_ident!("_"))
551 .into()
552 } else {
553 CallExpr {
554 span: *span,
555 args: Vec::new(),
556 callee: self.super_get(&id.sym, *span).as_callee(),
557 ..Default::default()
558 }
559 .into()
560 };
561 }
562 },
563 _ => e.visit_mut_children_with(self),
564 }
565 }
566
567 fn visit_mut_function(&mut self, _: &mut Function) {}
569
570 fn visit_mut_getter_prop(&mut self, p: &mut GetterProp) {
572 if p.key.is_computed() {
573 p.key.visit_mut_with(self);
574 }
575 }
576
577 fn visit_mut_method_prop(&mut self, p: &mut MethodProp) {
578 if p.key.is_computed() {
579 p.key.visit_mut_with(self);
580 }
581 }
582
583 fn visit_mut_pat(&mut self, n: &mut Pat) {
584 let in_pat = self.in_pat;
585 self.in_pat = true;
586 n.visit_mut_children_with(self);
587 self.in_pat = in_pat;
588 }
589
590 fn visit_mut_setter_prop(&mut self, p: &mut SetterProp) {
591 if p.key.is_computed() {
592 p.key.visit_mut_with(self);
593 }
594 }
595}
596
597pub fn init_this(stmts: &mut Vec<Stmt>, this_id: &Ident) {
598 stmts.visit_mut_children_with(&mut InitThis { this_id })
599}
600
601struct InitThis<'a> {
602 this_id: &'a Ident,
603}
604
605impl VisitMut for InitThis<'_> {
607 noop_visit_mut_type!(fail);
608
609 fn visit_mut_class(&mut self, _: &mut Class) {}
610
611 fn visit_mut_expr(&mut self, expr: &mut Expr) {
616 expr.visit_mut_children_with(self);
617
618 if let Expr::Call(
619 call_expr @ CallExpr {
620 callee: Callee::Super(..),
621 ..
622 },
623 ) = expr
624 {
625 let span = call_expr.span;
626 *expr = ParenExpr {
627 span,
628 expr: SeqExpr {
629 span,
630 exprs: vec![
631 Box::new(Expr::Call(call_expr.take())),
632 Box::new(Expr::Assign(AssignExpr {
633 span: DUMMY_SP,
634 left: self.this_id.clone().into(),
635 op: AssignOp::Assign,
636 right: Box::new(Expr::This(ThisExpr { span: DUMMY_SP })),
637 })),
638 ],
639 }
640 .into(),
641 }
642 .into()
643 }
644 }
645}
646
647fn extend_super(
648 decls: &mut Vec<VarDeclarator>,
649 get: SuperField,
650 set: SuperField,
651 update: SuperField,
652) {
653 decls.extend(update.ident.into_iter().map(|(key, ident)| {
654 let value = private_ident!("v");
655 VarDeclarator {
656 span: DUMMY_SP,
657 name: ident.into(),
658 init: Some(
659 ObjectLit {
660 span: DUMMY_SP,
661 props: vec![
662 Prop::Getter(GetterProp {
663 span: DUMMY_SP,
664 key: PropName::Ident("_".into()),
665 type_ann: None,
666 body: Some(BlockStmt {
667 stmts: vec![Expr::Ident(
668 get.ident
669 .get(&key)
670 .cloned()
671 .expect("getter not found")
672 .without_loc(),
673 )
674 .as_call(DUMMY_SP, Default::default())
675 .into_return_stmt()
676 .into()],
677 ..Default::default()
678 }),
679 }),
680 Prop::Setter(SetterProp {
681 span: DUMMY_SP,
682 key: PropName::Ident("_".into()),
683 this_param: None,
684 param: value.clone().into(),
685 body: Some(BlockStmt {
686 stmts: vec![Expr::Ident(
687 set.ident
688 .get(&key)
689 .cloned()
690 .expect("setter not found")
691 .without_loc(),
692 )
693 .as_call(DUMMY_SP, vec![value.as_arg()])
694 .into_stmt()],
695 ..Default::default()
696 }),
697 }),
698 ]
699 .into_iter()
700 .map(Box::new)
701 .map(From::from)
702 .collect(),
703 }
704 .into(),
705 ),
706 definite: false,
707 }
708 }));
709 if let Some(id) = update.computed {
710 let prop = private_ident!("_prop");
711 let value = private_ident!("v");
712
713 decls.push(VarDeclarator {
714 span: DUMMY_SP,
715 name: id.into(),
716 init: Some(
717 ArrowExpr {
718 span: DUMMY_SP,
719 params: vec![prop.clone().into()],
720 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
721 ObjectLit {
722 span: DUMMY_SP,
723 props: vec![
724 Prop::Getter(GetterProp {
725 span: DUMMY_SP,
726 key: PropName::Ident("_".into()),
727 type_ann: None,
728 body: Some(BlockStmt {
729 stmts: vec![Expr::Ident(
730 get.computed
731 .clone()
732 .expect("getter computed not found")
733 .without_loc(),
734 )
735 .as_call(DUMMY_SP, vec![prop.clone().as_arg()])
736 .into_return_stmt()
737 .into()],
738 ..Default::default()
739 }),
740 }),
741 Prop::Setter(SetterProp {
742 span: DUMMY_SP,
743 key: PropName::Ident("_".into()),
744 this_param: None,
745 param: value.clone().into(),
746 body: Some(BlockStmt {
747 stmts: vec![Expr::Ident(
748 set.computed
749 .clone()
750 .expect("setter computed not found")
751 .without_loc(),
752 )
753 .as_call(DUMMY_SP, vec![prop.as_arg(), value.as_arg()])
754 .into_return_stmt()
755 .into()],
756 ..Default::default()
757 }),
758 }),
759 ]
760 .into_iter()
761 .map(Box::new)
762 .map(From::from)
763 .collect(),
764 }
765 .into(),
766 ))),
767 ..Default::default()
768 }
769 .into(),
770 ),
771 definite: false,
772 });
773 }
774 decls.extend(get.ident.into_iter().map(|(key, ident)| {
775 VarDeclarator {
776 span: DUMMY_SP,
777 name: ident.without_loc().into(),
778 init: Some(
779 ArrowExpr {
780 span: DUMMY_SP,
781 params: Vec::new(),
782 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
783 SuperPropExpr {
784 obj: Super { span: DUMMY_SP },
785 prop: SuperProp::Ident(key.into()),
786 span: DUMMY_SP,
787 }
788 .into(),
789 ))),
790 ..Default::default()
791 }
792 .into(),
793 ),
794 definite: false,
795 }
796 }));
797 if let Some(id) = get.computed {
798 let param = private_ident!("_prop");
799 decls.push(VarDeclarator {
800 span: DUMMY_SP,
801 name: id.without_loc().into(),
802 init: Some(
803 ArrowExpr {
804 span: DUMMY_SP,
805 params: vec![param.clone().into()],
806 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
807 SuperPropExpr {
808 obj: Super { span: DUMMY_SP },
809 prop: SuperProp::Computed(ComputedPropName {
810 span: DUMMY_SP,
811 expr: Box::new(Expr::Ident(param)),
812 }),
813 span: DUMMY_SP,
814 }
815 .into(),
816 ))),
817 ..Default::default()
818 }
819 .into(),
820 ),
821 definite: false,
822 });
823 }
824 decls.extend(set.ident.into_iter().map(|(key, ident)| {
825 let value = private_ident!("_value");
826 VarDeclarator {
827 span: DUMMY_SP,
828 name: ident.without_loc().into(),
829 init: Some(
830 ArrowExpr {
831 span: DUMMY_SP,
832 params: vec![value.clone().into()],
833 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
834 AssignExpr {
835 span: DUMMY_SP,
836 left: SuperPropExpr {
837 obj: Super { span: DUMMY_SP },
838 prop: SuperProp::Ident(key.into()),
839 span: DUMMY_SP,
840 }
841 .into(),
842 op: op!("="),
843 right: Box::new(Expr::Ident(value)),
844 }
845 .into(),
846 ))),
847 ..Default::default()
848 }
849 .into(),
850 ),
851 definite: false,
852 }
853 }));
854 if let Some(id) = set.computed {
855 let prop = private_ident!("_prop");
856 let value = private_ident!("_value");
857 decls.push(VarDeclarator {
858 span: DUMMY_SP,
859 name: id.without_loc().into(),
860 init: Some(
861 ArrowExpr {
862 span: DUMMY_SP,
863 params: vec![prop.clone().into(), value.clone().into()],
864 body: Box::new(BlockStmtOrExpr::Expr(Box::new(
865 AssignExpr {
866 span: DUMMY_SP,
867 left: SuperPropExpr {
868 obj: Super { span: DUMMY_SP },
869 prop: SuperProp::Computed(ComputedPropName {
870 span: DUMMY_SP,
871 expr: Box::new(Expr::Ident(prop)),
872 }),
873 span: DUMMY_SP,
874 }
875 .into(),
876 op: op!("="),
877 right: Box::new(Expr::Ident(value)),
878 }
879 .into(),
880 ))),
881 ..Default::default()
882 }
883 .into(),
884 ),
885 definite: false,
886 });
887 }
888}