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