1use std::mem;
2
3use arrayvec::ArrayVec;
4use serde::Deserialize;
5use swc_common::{util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
6use swc_ecma_ast::*;
7use swc_ecma_utils::{
10 function::{init_this, FnEnvHoister},
11 member_expr, prepend_stmt, prepend_stmts, private_ident, quote_ident, ExprFactory,
12};
13use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
14use swc_trace_macro::swc_trace;
15use tracing::trace;
16
17pub fn parameters(c: Config, unresolved_mark: Mark) -> impl 'static + Pass {
18 let unresolved_ctxt = SyntaxContext::empty().apply_mark(unresolved_mark);
19 visit_mut_pass(Params {
20 c,
21 unresolved_ctxt,
22 hoister: FnEnvHoister::new(unresolved_ctxt),
23 ..Default::default()
24 })
25}
26
27#[derive(Default)]
28struct Params {
29 hoister: FnEnvHoister,
32 unresolved_ctxt: SyntaxContext,
33 in_subclass: bool,
34 in_prop: bool,
35 c: Config,
36}
37
38#[derive(Debug, Clone, Copy, Default, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct Config {
41 #[serde(default)]
42 pub ignore_function_length: bool,
43}
44
45#[swc_trace]
73impl Params {
74 fn visit_mut_fn_like(&mut self, ps: &mut Vec<Param>, body: &mut BlockStmt, is_setter: bool) {
75 let mut params = Vec::new();
76 let mut decls = Vec::new();
77 let mut loose_stmt = Vec::new();
78 let mut unpack_rest = None;
79 let mut decls_after_unpack = Vec::new();
80
81 let mut after_default = false;
82
83 for (i, param) in ps.drain(..).enumerate() {
84 let span = param.span();
85
86 match param.pat {
87 Pat::Ident(..) => {
88 if after_default && !self.c.ignore_function_length {
89 decls.push(VarDeclarator {
90 span,
91 name: param.pat,
92 init: Some(Box::new(check_arg_len_or_undef(i))),
93 definite: false,
94 })
95 } else {
96 params.push(param)
97 }
98 }
99 Pat::Array(..) | Pat::Object(..) => {
100 if after_default && !self.c.ignore_function_length {
101 decls.push(VarDeclarator {
102 span,
103 name: param.pat,
104 init: Some(Box::new(check_arg_len_or_undef(i))),
105 definite: false,
106 })
107 } else {
108 let binding = private_ident!(span, "param");
109
110 params.push(Param {
111 pat: binding.clone().into(),
112 ..param
113 });
114 let decl = VarDeclarator {
115 span,
116 name: param.pat,
117 init: Some(binding.into()),
118 definite: false,
119 };
120 if self.c.ignore_function_length {
121 loose_stmt.push(
122 VarDecl {
123 span,
124 kind: VarDeclKind::Let,
125 decls: vec![decl],
126 declare: false,
127 ..Default::default()
128 }
129 .into(),
130 )
131 } else {
132 decls.push(decl)
133 }
134 }
135 }
136 Pat::Assign(AssignPat { left, right, .. }) => {
137 if !(self.c.ignore_function_length || is_setter) {
139 after_default = true;
140 decls.push(VarDeclarator {
142 span,
143 name: *left,
144 init: Some(
145 CondExpr {
146 span,
147 test: Box::new(
148 BinExpr {
149 left: Box::new(check_arg_len(i)),
150 op: op!("&&"),
151 right: Box::new(Expr::Bin(BinExpr {
152 left: make_arg_nth(i).into(),
153 op: op!("!=="),
154 right: Expr::undefined(DUMMY_SP),
155 span: DUMMY_SP,
156 })),
157 span,
158 }
159 .into(),
160 ),
161 cons: make_arg_nth(i).into(),
162 alt: right,
163 }
164 .into(),
165 ),
166 definite: false,
167 })
168 } else if let Pat::Ident(ident) = left.as_ref() {
169 params.push(Param {
170 span,
171 pat: ident.clone().into(),
172 decorators: Vec::new(),
173 });
174 loose_stmt.push(
175 IfStmt {
176 span,
177 test: BinExpr {
178 span: DUMMY_SP,
179 left: Box::new(Ident::from(ident).into()),
180 op: op!("==="),
181 right: Expr::undefined(DUMMY_SP),
182 }
183 .into(),
184 cons: Box::new(Stmt::Expr(ExprStmt {
185 span,
186 expr: AssignExpr {
187 span,
188 left: left.try_into().unwrap(),
189 op: op!("="),
190 right,
191 }
192 .into(),
193 })),
194 alt: None,
195 }
196 .into(),
197 )
198 } else {
199 let binding = private_ident!(span, "param");
200 params.push(Param {
201 span: DUMMY_SP,
202 decorators: Default::default(),
203 pat: binding.clone().into(),
204 });
205 loose_stmt.push(
206 VarDecl {
207 span,
208 kind: VarDeclKind::Let,
209 decls: vec![VarDeclarator {
210 span,
211 name: *left,
212 init: Some(Box::new(Expr::Cond(CondExpr {
213 span,
214 test: Box::new(Expr::Bin(BinExpr {
215 span: DUMMY_SP,
216 left: Box::new(Expr::Ident(binding.clone())),
217 op: op!("==="),
218 right: Expr::undefined(DUMMY_SP),
219 })),
220 cons: right,
221 alt: Box::new(Expr::Ident(binding)),
222 }))),
223 definite: false,
224 }],
225 declare: false,
226 ..Default::default()
227 }
228 .into(),
229 )
230 }
231 }
232 Pat::Rest(RestPat { arg, .. }) => {
233 assert!(unpack_rest.is_none());
240
241 let mark = Mark::fresh(Mark::root());
244 let idx_ident =
245 quote_ident!(SyntaxContext::empty().apply_mark(mark), span, "_key");
246 let len_ident =
247 quote_ident!(SyntaxContext::empty().apply_mark(mark), span, "_len");
248
249 let arg = match *arg {
250 Pat::Ident(ident) => ident.into(),
251 arg => {
252 let tmp_ident =
253 quote_ident!(SyntaxContext::empty().apply_mark(mark), span, "_tmp");
254 decls_after_unpack.push(VarDeclarator {
255 span: DUMMY_SP,
256 name: arg,
257 init: Some(Box::new(tmp_ident.clone().into())),
258 definite: false,
259 });
260 tmp_ident
261 }
262 };
263
264 let make_minus_i = |ident: &Ident, min_zero: bool| -> Expr {
265 if i == 0 {
266 ident.clone().into()
268 } else {
269 let bin: Expr = BinExpr {
271 span,
272 left: ident.clone().into(),
273 op: op!(bin, "-"),
274 right: Lit::Num(Number {
275 span,
276 value: i as f64,
277 raw: None,
278 })
279 .into(),
280 }
281 .into();
282 if !min_zero {
283 return bin;
284 }
285
286 CondExpr {
287 span,
288 test: Box::new(
289 BinExpr {
290 span,
291 left: Box::new(len_ident.clone().into()),
292 op: op!(">"),
293 right: Lit::Num(Number {
294 span,
295 value: i as _,
296 raw: None,
297 })
298 .into(),
299 }
300 .into(),
301 ),
302 cons: Box::new(bin),
303 alt: 0.into(),
304 }
305 .into()
306 }
307 };
308
309 unpack_rest = Some(
310 ForStmt {
311 span,
312 init: Some(
313 VarDecl {
314 kind: VarDeclKind::Var,
315 span,
316 decls: vec![
317 VarDeclarator {
319 span,
320 name: len_ident.clone().into(),
321 init: Some(
322 member_expr!(
323 Default::default(),
324 span,
325 arguments.length
326 )
327 .into(),
328 ),
329 definite: false,
330 },
331 VarDeclarator {
333 span,
334 name: arg.clone().into(),
335 init: Some(Box::new(Expr::New(NewExpr {
336 span,
337 callee: Box::new(
338 quote_ident!(self.unresolved_ctxt, "Array")
339 .into(),
340 ),
341 args: Some(vec![{
342 make_minus_i(&len_ident, true).as_arg()
344 }]),
345 ..Default::default()
346 }))),
347 definite: false,
348 },
349 VarDeclarator {
351 span,
352 name: idx_ident.clone().into(),
353 init: Some(Box::new(Expr::Lit(Lit::Num(Number {
354 span,
355 value: i as f64,
356 raw: None,
357 })))),
358 definite: false,
359 },
360 ],
361 declare: false,
362 ..Default::default()
363 }
364 .into(),
365 ),
366 test: Some(
368 BinExpr {
369 span,
370 left: Box::new(idx_ident.clone().into()),
371 op: op!("<"),
372 right: Box::new(len_ident.clone().into()),
373 }
374 .into(),
375 ),
376 update: Some(
378 UpdateExpr {
379 span,
380 op: op!("++"),
381 prefix: false,
382 arg: Box::new(idx_ident.clone().into()),
383 }
384 .into(),
385 ),
386 body: Box::new(Stmt::Block(BlockStmt {
387 span: DUMMY_SP,
388 stmts: vec![{
389 let prop = Box::new(Expr::Ident(idx_ident.clone()));
390 AssignExpr {
393 span,
394 left: arg
395 .computed_member(make_minus_i(&idx_ident, false))
396 .into(),
397 op: op!("="),
398 right: Box::new(
399 MemberExpr {
400 span: DUMMY_SP,
401 obj: Box::new(
402 quote_ident!(
403 Default::default(),
404 span,
405 "arguments"
406 )
407 .into(),
408 ),
409 prop: MemberProp::Computed(ComputedPropName {
410 span,
411 expr: prop,
412 }),
413 }
414 .into(),
415 ),
416 }
417 .into_stmt()
418 }],
419 ..Default::default()
420 })),
421 }
422 .into(),
423 )
424 }
425 _ => unreachable!(),
426 }
427 }
428
429 let mut iter: ArrayVec<_, 3> = Default::default();
430
431 if !decls.is_empty() {
432 iter.push(
433 VarDecl {
434 span: DUMMY_SP,
435 kind: VarDeclKind::Let,
436 decls,
437 declare: false,
438 ..Default::default()
439 }
440 .into(),
441 )
442 }
443 iter.extend(unpack_rest);
444 if !decls_after_unpack.is_empty() {
445 iter.push(
446 VarDecl {
447 span: DUMMY_SP,
448 kind: VarDeclKind::Let,
449 decls: decls_after_unpack,
450 declare: false,
451 ..Default::default()
452 }
453 .into(),
454 );
455 }
456 if (is_setter || self.c.ignore_function_length) && !loose_stmt.is_empty() {
457 loose_stmt.extend(iter);
458 prepend_stmts(&mut body.stmts, loose_stmt.into_iter());
459 } else {
460 prepend_stmts(&mut body.stmts, iter.into_iter());
461 };
462
463 *ps = params;
464 }
465}
466
467#[swc_trace]
468impl VisitMut for Params {
469 noop_visit_mut_type!(fail);
470
471 fn visit_mut_class_prop(&mut self, prop: &mut ClassProp) {
474 prop.key.visit_mut_children_with(self);
475
476 let old_in_prop = self.in_prop;
477 self.in_prop = !prop.is_static;
478 prop.value.visit_mut_with(self);
479 self.in_prop = old_in_prop;
480 }
481
482 fn visit_mut_class_method(&mut self, m: &mut ClassMethod) {
483 if let MethodKind::Setter = m.kind {
484 let f = &mut m.function;
485
486 if f.body.is_none() {
487 return;
488 }
489
490 let old_in_subclass = self.in_subclass;
491 let old_in_prop = self.in_prop;
492 self.in_subclass = false;
493 self.in_prop = false;
494
495 f.visit_mut_children_with(self);
496
497 let mut body = f.body.take().unwrap();
498 self.visit_mut_fn_like(&mut f.params, &mut body, true);
499
500 f.body = Some(body);
501
502 self.in_subclass = old_in_subclass;
503 self.in_prop = old_in_prop;
504 } else {
505 m.visit_mut_children_with(self);
506 }
507 }
508
509 fn visit_mut_private_prop(&mut self, prop: &mut PrivateProp) {
511 let old_in_prop = self.in_prop;
512 self.in_prop = !prop.is_static;
513 prop.value.visit_mut_with(self);
514 self.in_prop = old_in_prop;
515 }
516
517 fn visit_mut_block_stmt_or_expr(&mut self, body: &mut BlockStmtOrExpr) {
518 let old_rep = self.hoister.take();
519
520 body.visit_mut_children_with(self);
521
522 let decls = mem::replace(&mut self.hoister, old_rep).to_stmt();
523
524 if let Some(decls) = decls {
525 if let BlockStmtOrExpr::Expr(v) = body {
526 let mut stmts = Vec::new();
527 prepend_stmt(&mut stmts, decls);
528 stmts.push(
529 ReturnStmt {
530 span: DUMMY_SP,
531 arg: Some(v.take()),
532 }
533 .into(),
534 );
535 *body = BlockStmtOrExpr::BlockStmt(BlockStmt {
536 span: DUMMY_SP,
537 stmts,
538 ..Default::default()
539 });
540 }
541 }
542 }
543
544 fn visit_mut_catch_clause(&mut self, f: &mut CatchClause) {
545 f.visit_mut_children_with(self);
546
547 let mut params = Vec::new();
548 if f.param.is_some() {
549 params.push(Param {
550 span: DUMMY_SP,
551 decorators: Vec::new(),
552 pat: f.param.take().unwrap(),
553 });
554 }
555
556 self.visit_mut_fn_like(&mut params, &mut f.body, false);
557
558 assert!(
559 params.is_empty() || params.len() == 1,
560 "fold_fn_like should return 0 ~ 1 parameter while handling catch clause"
561 );
562
563 let param = if params.is_empty() {
564 None
565 } else {
566 Some(params.pop().unwrap())
567 };
568
569 f.param = param.map(|param| param.pat);
570 }
571
572 fn visit_mut_constructor(&mut self, f: &mut Constructor) {
573 trace!("visit_mut_constructor(parmas.len() = {})", f.params.len());
574 f.params.visit_mut_with(self);
575
576 if let Some(BlockStmt { stmts, .. }) = &mut f.body {
577 let old_rep = self.hoister.take();
578
579 stmts.visit_mut_children_with(self);
580
581 if self.in_subclass {
582 let (decl, this_id) =
583 mem::replace(&mut self.hoister, old_rep).to_stmt_in_subclass();
584
585 if let Some(stmt) = decl {
586 if let Some(this_id) = this_id {
587 init_this(stmts, &this_id)
588 }
589 prepend_stmt(stmts, stmt);
590 }
591 } else {
592 let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
593
594 if let Some(stmt) = decl {
595 prepend_stmt(stmts, stmt);
596 }
597 }
598 }
599
600 trace!(
601 "visit_mut_constructor(parmas.len() = {}, after)",
602 f.params.len()
603 );
604 }
605
606 fn visit_mut_expr(&mut self, e: &mut Expr) {
607 match e {
608 Expr::Arrow(f) => {
609 f.visit_mut_children_with(self);
610
611 let was_expr = f.body.is_expr();
612
613 let need_arrow_to_function = f.params.iter().any(|p| match p {
614 Pat::Rest(..) => true,
615 Pat::Assign(..) => !self.c.ignore_function_length,
616 _ => false,
617 });
618
619 let mut local_vars = None;
620
621 if need_arrow_to_function {
623 if !self.in_prop {
624 f.visit_mut_children_with(&mut self.hoister)
625 } else {
626 let mut hoister = FnEnvHoister::new(self.unresolved_ctxt);
627 f.visit_mut_children_with(&mut hoister);
628 local_vars = hoister.to_stmt();
629 }
630 }
631
632 let mut params = f
633 .params
634 .take()
635 .into_iter()
636 .map(|pat| Param {
637 span: DUMMY_SP,
638 decorators: Default::default(),
639 pat,
640 })
641 .collect();
642
643 let mut body = match *f.body.take() {
644 BlockStmtOrExpr::BlockStmt(block) => block,
645 BlockStmtOrExpr::Expr(expr) => BlockStmt {
646 stmts: vec![Stmt::Return(ReturnStmt {
647 span: DUMMY_SP,
648 arg: Some(expr),
649 })],
650 ..Default::default()
651 },
652 };
653
654 self.visit_mut_fn_like(&mut params, &mut body, false);
655
656 if need_arrow_to_function {
657 let func: Expr = Function {
658 params,
659 decorators: Default::default(),
660 span: f.span,
661 body: Some(body),
662 is_generator: f.is_generator,
663 is_async: f.is_async,
664 ..Default::default()
665 }
666 .into();
667 *e = match (self.in_prop, local_vars) {
668 (true, Some(var_decl)) => ArrowExpr {
669 span: f.span,
670 params: Vec::new(),
671 is_async: false,
672 is_generator: false,
673 body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
674 span: f.span,
675 stmts: vec![
676 var_decl,
677 Stmt::Return(ReturnStmt {
678 span: f.span,
679 arg: Some(Box::new(func)),
680 }),
681 ],
682 ..Default::default()
683 })),
684 ..Default::default()
685 }
686 .as_iife()
687 .into(),
688 _ => func,
689 };
690 return;
691 }
692
693 let body = if was_expr
694 && body.stmts.len() == 1
695 && matches!(
696 body.stmts[0],
697 Stmt::Return(ReturnStmt { arg: Some(..), .. })
698 ) {
699 match body.stmts.pop().unwrap() {
700 Stmt::Return(ReturnStmt { arg: Some(arg), .. }) => {
701 Box::new(BlockStmtOrExpr::Expr(arg))
702 }
703 _ => unreachable!(),
704 }
705 } else {
706 Box::new(BlockStmtOrExpr::BlockStmt(body))
707 };
708
709 *e = ArrowExpr {
710 params: params.into_iter().map(|param| param.pat).collect(),
711 body,
712 span: f.span,
713 is_async: f.is_async,
714 is_generator: f.is_generator,
715 type_params: f.type_params.take(),
716 return_type: f.return_type.take(),
717 ..Default::default()
718 }
719 .into();
720 }
721 _ => e.visit_mut_children_with(self),
722 }
723 }
724
725 fn visit_mut_function(&mut self, f: &mut Function) {
726 if f.body.is_none() {
727 return;
728 }
729
730 let old_in_subclass = self.in_subclass;
731 let old_in_prop = self.in_prop;
732 self.in_subclass = false;
733 self.in_prop = false;
734
735 f.visit_mut_children_with(self);
736
737 let mut body = f.body.take().unwrap();
738 self.visit_mut_fn_like(&mut f.params, &mut body, false);
739
740 f.body = Some(body);
741
742 self.in_subclass = old_in_subclass;
743 self.in_prop = old_in_prop;
744 }
745
746 fn visit_mut_getter_prop(&mut self, f: &mut GetterProp) {
747 if f.body.is_none() {
748 return;
749 }
750
751 f.visit_mut_children_with(self);
752
753 let mut params = Vec::new();
754 let mut body = f.body.take().unwrap();
755 self.visit_mut_fn_like(&mut params, &mut body, false);
756 debug_assert_eq!(params, Vec::new());
757
758 f.body = Some(body);
759 }
760
761 fn visit_mut_setter_prop(&mut self, f: &mut SetterProp) {
762 if f.body.is_none() {
763 return;
764 }
765
766 f.visit_mut_children_with(self);
767
768 let mut params = vec![Param {
769 span: DUMMY_SP,
770 decorators: Default::default(),
771 pat: *f.param.take(),
772 }];
773
774 let mut body = f.body.take().unwrap();
775 self.visit_mut_fn_like(&mut params, &mut body, true);
776
777 debug_assert!(params.len() == 1);
778
779 f.param = Box::new(params.pop().unwrap().pat);
780 f.body = Some(body);
781 }
782
783 fn visit_mut_class(&mut self, c: &mut Class) {
784 let old_in_subclass = self.in_subclass;
785 let old_in_prop = self.in_prop;
786
787 self.in_subclass = c.super_class.is_some();
788 self.in_prop = false;
789 c.visit_mut_children_with(self);
790
791 self.in_subclass = old_in_subclass;
792 self.in_prop = old_in_prop;
793 }
794
795 fn visit_mut_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {
796 stmts.visit_mut_children_with(self);
797
798 let decl = self.hoister.take().to_stmt();
799
800 if let Some(stmt) = decl {
801 prepend_stmt(stmts, stmt.into());
802 }
803 }
804
805 fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
806 let old_rep = self.hoister.take();
807
808 stmts.visit_mut_children_with(self);
809
810 let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
811
812 if let Some(stmt) = decl {
813 prepend_stmt(stmts, stmt);
814 }
815 }
816}
817
818fn make_arg_nth(n: usize) -> MemberExpr {
819 Ident::new_no_ctxt("arguments".into(), DUMMY_SP).computed_member(n)
820}
821
822fn check_arg_len(n: usize) -> Expr {
823 BinExpr {
824 left: Expr::Ident(Ident::new_no_ctxt("arguments".into(), DUMMY_SP))
825 .make_member(IdentName::new("length".into(), DUMMY_SP))
826 .into(),
827 op: op!(">"),
828 right: n.into(),
829 span: DUMMY_SP,
830 }
831 .into()
832}
833
834fn check_arg_len_or_undef(n: usize) -> Expr {
835 CondExpr {
836 test: Box::new(check_arg_len(n)),
837 cons: make_arg_nth(n).into(),
838 alt: Expr::undefined(DUMMY_SP),
839 span: DUMMY_SP,
840 }
841 .into()
842}