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 fn take_extra_var_decl(&mut self) -> Option<Stmt> {
284 if self.extra_ident.is_empty() {
285 return None;
286 }
287
288 Some(
289 VarDecl {
290 kind: VarDeclKind::Var,
291 decls: self
292 .extra_ident
293 .take()
294 .into_iter()
295 .map(|ident| VarDeclarator {
296 span: DUMMY_SP,
297 name: ident.into(),
298 init: None,
299 definite: false,
300 })
301 .collect(),
302 ..Default::default()
303 }
304 .into(),
305 )
306 }
307
308 fn prepend_extra_var_decl(&mut self, stmts: &mut Vec<Stmt>) {
309 if let Some(decl) = self.take_extra_var_decl() {
310 stmts.insert(0, decl);
311 }
312 }
313}
314
315impl VisitMut for FnEnvHoister {
316 noop_visit_mut_type!(fail);
317
318 fn visit_mut_assign_target_pat(&mut self, n: &mut AssignTargetPat) {
319 let in_pat = self.in_pat;
320 self.in_pat = true;
321 n.visit_mut_children_with(self);
322 self.in_pat = in_pat;
323 }
324
325 fn visit_mut_block_stmt(&mut self, b: &mut BlockStmt) {
326 b.visit_mut_children_with(self);
327
328 self.prepend_extra_var_decl(&mut b.stmts);
331 }
332
333 fn visit_mut_function_body(&mut self, body: &mut FunctionBody) {
334 body.visit_mut_children_with(self);
335
336 self.prepend_extra_var_decl(&mut body.stmts);
337 }
338
339 fn visit_mut_arrow_function_body(&mut self, body: &mut ArrowFunctionBody) {
340 body.visit_mut_children_with(self);
341
342 let Some(decl) = self.take_extra_var_decl() else {
343 return;
344 };
345 if let ArrowFunctionBody::Expr(expr) = body {
346 *body = ArrowFunctionBody::FunctionBody(FunctionBody {
347 span: DUMMY_SP,
348 stmts: vec![
349 decl,
350 Stmt::Return(ReturnStmt {
351 span: expr.span(),
352 arg: Some(expr.take()),
353 }),
354 ],
355 });
356 }
357 }
358
359 fn visit_mut_class(&mut self, _: &mut Class) {}
361
362 fn visit_mut_expr(&mut self, e: &mut Expr) {
363 match e {
364 Expr::Ident(Ident { ctxt, sym, .. })
365 if !self.arguments_disabled
366 && *sym == "arguments"
367 && (*ctxt == self.unresolved_ctxt || *ctxt == SyntaxContext::empty()) =>
368 {
369 let arguments = self
370 .args
371 .get_or_insert_with(|| private_ident!("_arguments"));
372 *e = arguments.clone().into();
373 }
374 Expr::This(..) if !self.this_disabled => {
375 let this = self.get_this();
376 *e = this.into();
377 }
378 Expr::MetaProp(MetaPropExpr {
379 kind: MetaPropKind::NewTarget,
380 ..
381 }) => {
382 let target = self
383 .new_target
384 .get_or_insert_with(|| private_ident!("_newtarget"));
385 *e = target.clone().into();
386 }
387 Expr::Assign(AssignExpr {
389 left,
390 right,
391 span,
392 op,
393 }) => {
394 let expr = match left {
395 AssignTarget::Simple(e) => e,
396 AssignTarget::Pat(..) => {
397 e.visit_mut_children_with(self);
398 return;
399 }
400 #[cfg(swc_ast_unknown)]
401 _ => return,
402 };
403 if !self.super_disabled {
404 if let SimpleAssignTarget::SuperProp(super_prop) = &mut *expr {
405 let left_span = super_prop.span;
406 match &mut super_prop.prop {
407 SuperProp::Computed(c) => {
408 let callee = self.super_set_computed(left_span);
409
410 let op = op.to_update();
411
412 let args = if let Some(op) = op {
413 let tmp = private_ident!("tmp");
414 self.extra_ident.push(tmp.clone());
415 vec![
416 Expr::Assign(AssignExpr {
417 span: DUMMY_SP,
418 left: tmp.clone().into(),
419 op: op!("="),
420 right: c.expr.take(),
421 })
422 .as_arg(),
423 Expr::Bin(BinExpr {
424 span: DUMMY_SP,
425 left: Box::new(Expr::Call(CallExpr {
426 span: DUMMY_SP,
427 callee: self
428 .super_get_computed(DUMMY_SP)
429 .as_callee(),
430 args: vec![tmp.as_arg()],
431 ..Default::default()
432 })),
433 op,
434 right: right.take(),
435 })
436 .as_arg(),
437 ]
438 } else {
439 vec![c.expr.take().as_arg(), right.take().as_arg()]
440 };
441 *e = CallExpr {
442 span: *span,
443 args,
444 callee: callee.as_callee(),
445 ..Default::default()
446 }
447 .into();
448 }
449 SuperProp::Ident(id) => {
450 let callee = self.super_set(&id.sym, left_span);
451 *e = CallExpr {
452 span: *span,
453 args: vec![(if let Some(op) = op.to_update() {
454 Box::new(Expr::Bin(BinExpr {
455 span: DUMMY_SP,
456 left: Box::new(
457 self.super_get(&id.sym, id.span)
458 .as_call(id.span, Vec::new()),
459 ),
460 op,
461 right: right.take(),
462 }))
463 } else {
464 right.take()
465 })
466 .as_arg()],
467 callee: callee.as_callee(),
468 ..Default::default()
469 }
470 .into();
471 }
472 #[cfg(swc_ast_unknown)]
473 _ => (),
474 }
475 }
476 }
477 e.visit_mut_children_with(self)
478 }
479 Expr::Call(CallExpr {
481 span,
482 callee: Callee::Expr(expr),
483 args,
484 ..
485 }) => {
486 if !self.super_disabled {
487 if let Expr::SuperProp(super_prop) = &mut **expr {
488 match &mut super_prop.prop {
489 SuperProp::Computed(c) => {
490 let callee = self.super_get_computed(super_prop.span);
491 let call: Expr = CallExpr {
492 span: *span,
493 args: vec![c.expr.take().as_arg()],
494 callee: callee.as_callee(),
495 ..Default::default()
496 }
497 .into();
498 let mut new_args = args.take();
499
500 new_args.insert(0, self.get_this().as_arg());
501
502 *e = call.call_fn(*span, new_args);
503 }
504 SuperProp::Ident(id) => {
505 let callee = self.super_get(&id.sym, super_prop.span);
506 let call: Expr = CallExpr {
507 span: *span,
508 args: Vec::new(),
509 callee: callee.as_callee(),
510 ..Default::default()
511 }
512 .into();
513 let mut new_args = args.take();
514
515 new_args.insert(0, self.get_this().as_arg());
516
517 *e = call.call_fn(*span, new_args);
518 }
519 #[cfg(swc_ast_unknown)]
520 _ => (),
521 }
522 };
523 }
524 e.visit_mut_children_with(self)
525 }
526 Expr::Update(UpdateExpr { arg, .. }) if arg.is_super_prop() => {
528 let in_pat = self.in_pat;
529 self.in_pat = true;
531 arg.visit_mut_with(self);
532 self.in_pat = in_pat;
533 }
534 Expr::SuperProp(SuperPropExpr { prop, span, .. }) if !self.super_disabled => match prop
535 {
536 SuperProp::Computed(c) => {
537 c.expr.visit_mut_children_with(self);
538 *e = if self.in_pat {
539 Expr::from(CallExpr {
540 span: *span,
541 args: vec![c.expr.take().as_arg()],
542 callee: self.super_update_computed(*span).as_callee(),
543 ..Default::default()
544 })
545 .make_member(atom!("_").into())
546 .into()
547 } else {
548 CallExpr {
549 span: *span,
550 args: vec![c.expr.take().as_arg()],
551 callee: self.super_get_computed(*span).as_callee(),
552 ..Default::default()
553 }
554 .into()
555 };
556 }
557 SuperProp::Ident(id) => {
558 *e = if self.in_pat {
559 self.super_update(&id.sym, *span)
560 .make_member(quote_ident!("_"))
561 .into()
562 } else {
563 CallExpr {
564 span: *span,
565 args: Vec::new(),
566 callee: self.super_get(&id.sym, *span).as_callee(),
567 ..Default::default()
568 }
569 .into()
570 };
571 }
572 #[cfg(swc_ast_unknown)]
573 _ => (),
574 },
575 _ => e.visit_mut_children_with(self),
576 }
577 }
578
579 fn visit_mut_function(&mut self, _: &mut Function) {}
581
582 fn visit_mut_pat(&mut self, n: &mut Pat) {
583 let in_pat = self.in_pat;
584 self.in_pat = true;
585 n.visit_mut_children_with(self);
586 self.in_pat = in_pat;
587 }
588}
589
590pub fn init_this(stmts: &mut Vec<Stmt>, this_id: &Ident) {
591 stmts.visit_mut_children_with(&mut InitThis { this_id })
592}
593
594struct InitThis<'a> {
595 this_id: &'a Ident,
596}
597
598impl VisitMut for InitThis<'_> {
600 noop_visit_mut_type!(fail);
601
602 fn visit_mut_class(&mut self, _: &mut Class) {}
603
604 fn visit_mut_expr(&mut self, expr: &mut Expr) {
609 expr.visit_mut_children_with(self);
610
611 if let Expr::Call(
612 call_expr @ CallExpr {
613 callee: Callee::Super(..),
614 ..
615 },
616 ) = expr
617 {
618 let span = call_expr.span;
619 *expr = ParenExpr {
620 span,
621 expr: SeqExpr {
622 span,
623 exprs: vec![
624 Box::new(Expr::Call(call_expr.take())),
625 Box::new(Expr::Assign(AssignExpr {
626 span: DUMMY_SP,
627 left: self.this_id.clone().into(),
628 op: AssignOp::Assign,
629 right: Box::new(Expr::This(ThisExpr { span: DUMMY_SP })),
630 })),
631 ],
632 }
633 .into(),
634 }
635 .into()
636 }
637 }
638}
639
640fn extend_super(
641 decls: &mut Vec<VarDeclarator>,
642 get: SuperField,
643 set: SuperField,
644 update: SuperField,
645) {
646 decls.extend(update.ident.into_iter().map(|(key, ident)| {
647 let value = private_ident!("v");
648 VarDeclarator {
649 span: DUMMY_SP,
650 name: ident.into(),
651 init: Some(
652 ObjectLit {
653 span: DUMMY_SP,
654 props: vec![
655 Prop::Getter(GetterProp {
656 span: DUMMY_SP,
657 key: PropName::Ident(atom!("_").into()),
658 function: Box::new(Function {
659 body: Some(FunctionBody {
660 span: DUMMY_SP,
661 stmts: vec![Expr::Ident(
662 get.ident
663 .get(&key)
664 .cloned()
665 .expect("getter not found")
666 .without_loc(),
667 )
668 .as_call(DUMMY_SP, Default::default())
669 .into_return_stmt()
670 .into()],
671 }),
672 ..Default::default()
673 }),
674 }),
675 Prop::Setter(SetterProp {
676 span: DUMMY_SP,
677 key: PropName::Ident(atom!("_").into()),
678 function: Box::new(Function {
679 params: vec![Param::from(Pat::from(value.clone()))],
680 body: Some(FunctionBody {
681 span: DUMMY_SP,
682 stmts: vec![Expr::Ident(
683 set.ident
684 .get(&key)
685 .cloned()
686 .expect("setter not found")
687 .without_loc(),
688 )
689 .as_call(DUMMY_SP, vec![value.as_arg()])
690 .into_stmt()],
691 }),
692 ..Default::default()
693 }),
694 }),
695 ]
696 .into_iter()
697 .map(Box::new)
698 .map(From::from)
699 .collect(),
700 }
701 .into(),
702 ),
703 definite: false,
704 }
705 }));
706 if let Some(id) = update.computed {
707 let prop = private_ident!("_prop");
708 let value = private_ident!("v");
709
710 decls.push(VarDeclarator {
711 span: DUMMY_SP,
712 name: id.into(),
713 init: Some(
714 ArrowExpr {
715 span: DUMMY_SP,
716 params: vec![prop.clone().into()],
717 body: Box::new(ArrowFunctionBody::Expr(Box::new(
718 ObjectLit {
719 span: DUMMY_SP,
720 props: vec![
721 Prop::Getter(GetterProp {
722 span: DUMMY_SP,
723 key: PropName::Ident(atom!("_").into()),
724 function: Box::new(Function {
725 body: Some(FunctionBody {
726 span: DUMMY_SP,
727 stmts: vec![Expr::Ident(
728 get.computed
729 .clone()
730 .expect("getter computed not found")
731 .without_loc(),
732 )
733 .as_call(DUMMY_SP, vec![prop.clone().as_arg()])
734 .into_return_stmt()
735 .into()],
736 }),
737 ..Default::default()
738 }),
739 }),
740 Prop::Setter(SetterProp {
741 span: DUMMY_SP,
742 key: PropName::Ident(atom!("_").into()),
743 function: Box::new(Function {
744 params: vec![Param::from(Pat::from(value.clone()))],
745 body: Some(FunctionBody {
746 span: DUMMY_SP,
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 }),
757 ..Default::default()
758 }),
759 }),
760 ]
761 .into_iter()
762 .map(Box::new)
763 .map(From::from)
764 .collect(),
765 }
766 .into(),
767 ))),
768 ..Default::default()
769 }
770 .into(),
771 ),
772 definite: false,
773 });
774 }
775 decls.extend(get.ident.into_iter().map(|(key, ident)| {
776 VarDeclarator {
777 span: DUMMY_SP,
778 name: ident.without_loc().into(),
779 init: Some(
780 ArrowExpr {
781 span: DUMMY_SP,
782 params: Vec::new(),
783 body: Box::new(ArrowFunctionBody::Expr(Box::new(
784 SuperPropExpr {
785 obj: Super { span: DUMMY_SP },
786 prop: SuperProp::Ident(key.into()),
787 span: DUMMY_SP,
788 }
789 .into(),
790 ))),
791 ..Default::default()
792 }
793 .into(),
794 ),
795 definite: false,
796 }
797 }));
798 if let Some(id) = get.computed {
799 let param = private_ident!("_prop");
800 decls.push(VarDeclarator {
801 span: DUMMY_SP,
802 name: id.without_loc().into(),
803 init: Some(
804 ArrowExpr {
805 span: DUMMY_SP,
806 params: vec![param.clone().into()],
807 body: Box::new(ArrowFunctionBody::Expr(Box::new(
808 SuperPropExpr {
809 obj: Super { span: DUMMY_SP },
810 prop: SuperProp::Computed(ComputedPropName {
811 span: DUMMY_SP,
812 expr: Box::new(Expr::Ident(param)),
813 }),
814 span: DUMMY_SP,
815 }
816 .into(),
817 ))),
818 ..Default::default()
819 }
820 .into(),
821 ),
822 definite: false,
823 });
824 }
825 decls.extend(set.ident.into_iter().map(|(key, ident)| {
826 let value = private_ident!("_value");
827 VarDeclarator {
828 span: DUMMY_SP,
829 name: ident.without_loc().into(),
830 init: Some(
831 ArrowExpr {
832 span: DUMMY_SP,
833 params: vec![value.clone().into()],
834 body: Box::new(ArrowFunctionBody::Expr(Box::new(
835 AssignExpr {
836 span: DUMMY_SP,
837 left: SuperPropExpr {
838 obj: Super { span: DUMMY_SP },
839 prop: SuperProp::Ident(key.into()),
840 span: DUMMY_SP,
841 }
842 .into(),
843 op: op!("="),
844 right: Box::new(Expr::Ident(value)),
845 }
846 .into(),
847 ))),
848 ..Default::default()
849 }
850 .into(),
851 ),
852 definite: false,
853 }
854 }));
855 if let Some(id) = set.computed {
856 let prop = private_ident!("_prop");
857 let value = private_ident!("_value");
858 decls.push(VarDeclarator {
859 span: DUMMY_SP,
860 name: id.without_loc().into(),
861 init: Some(
862 ArrowExpr {
863 span: DUMMY_SP,
864 params: vec![prop.clone().into(), value.clone().into()],
865 body: Box::new(ArrowFunctionBody::Expr(Box::new(
866 AssignExpr {
867 span: DUMMY_SP,
868 left: SuperPropExpr {
869 obj: Super { span: DUMMY_SP },
870 prop: SuperProp::Computed(ComputedPropName {
871 span: DUMMY_SP,
872 expr: Box::new(Expr::Ident(prop)),
873 }),
874 span: DUMMY_SP,
875 }
876 .into(),
877 op: op!("="),
878 right: Box::new(Expr::Ident(value)),
879 }
880 .into(),
881 ))),
882 ..Default::default()
883 }
884 .into(),
885 ),
886 definite: false,
887 });
888 }
889}