swc_ecma_compat_es2015/
parameters.rs

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::*;
8// use swc_ecma_transforms_base::perf::Parallel;
9// use swc_ecma_transforms_macros::parallel;
10use 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    /// Used to store `this, in case if `arguments` is used and we should
31    /// transform an arrow expression to a function expression.
32    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// impl Parallel for Params {
47//     fn create(&self) -> Self {
48//         Params {
49//             state: Default::default(),
50//             c: self.c,
51//         }
52//     }
53
54//     fn merge(&mut self, other: Self) {
55//         self.state.merge(other.state);
56//     }
57
58//     fn after_stmts(&mut self, stmts: &mut Vec<Stmt>) {
59//         let decls = self.state.take().to_stmt();
60//         if let Some(decls) = decls {
61//             prepend(stmts, decls)
62//         }
63//     }
64
65//     fn after_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {
66//         let decls = self.state.take().to_stmt();
67//         if let Some(decls) = decls {
68//             prepend(stmts, ModuleItem::Stmt(decls))
69//         }
70//     }
71// }
72
73#[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                    // arguments.length will always be 1 in setter
139                    if !(self.c.ignore_function_length || is_setter) {
140                        after_default = true;
141                        // access non-existent element in `arguments` is expensive
142                        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                    // Inject a for statement
235                    //
236                    // for(var _len = arguments.length, a1 = new Array(_len), _key = 0; _key <
237                    // _len; _key++){
238                    //      a1[_key] = arguments[_key];
239                    // }
240                    assert!(unpack_rest.is_none());
241
242                    // TODO: Optimize (use `arguments` instead of rest argument)
243
244                    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                            // `len`
268                            ident.clone().into()
269                        } else {
270                            // `len - $i`
271                            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                                        // _len = arguments.length - i
319                                        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                                        // a1 = new Array(_len - $i)
333                                        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                                                    // `len` or  `len - $i`
344                                                    make_minus_i(&len_ident, true).as_arg()
345                                                }]),
346                                                ..Default::default()
347                                            }))),
348                                            definite: false,
349                                        },
350                                        // _key = 0
351                                        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                            // `_key < _len`
368                            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                            // _key++
378                            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                                    // a1[_key - i] = arguments[_key];
392
393                                    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    // generally speaking, there won't be class field in here, but Safari 14.1
473    // still has bugs in parameters
474    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    // same for private prop
511    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                // this needs to happen before rest parameter transform
623                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                    #[cfg(swc_ast_unknown)]
654                    _ => panic!("unable to access unknown nodes"),
655                };
656
657                self.visit_mut_fn_like(&mut params, &mut body, false);
658
659                if need_arrow_to_function {
660                    let func: Expr = Function {
661                        params,
662                        decorators: Default::default(),
663                        span: f.span,
664                        body: Some(body),
665                        is_generator: f.is_generator,
666                        is_async: f.is_async,
667                        ..Default::default()
668                    }
669                    .into();
670                    *e = match (self.in_prop, local_vars) {
671                        (true, Some(var_decl)) => ArrowExpr {
672                            span: f.span,
673                            params: Vec::new(),
674                            is_async: false,
675                            is_generator: false,
676                            body: Box::new(BlockStmtOrExpr::BlockStmt(BlockStmt {
677                                span: f.span,
678                                stmts: vec![
679                                    var_decl,
680                                    Stmt::Return(ReturnStmt {
681                                        span: f.span,
682                                        arg: Some(Box::new(func)),
683                                    }),
684                                ],
685                                ..Default::default()
686                            })),
687                            ..Default::default()
688                        }
689                        .as_iife()
690                        .into(),
691                        _ => func,
692                    };
693                    return;
694                }
695
696                let body = if was_expr
697                    && body.stmts.len() == 1
698                    && matches!(
699                        body.stmts[0],
700                        Stmt::Return(ReturnStmt { arg: Some(..), .. })
701                    ) {
702                    match body.stmts.pop().unwrap() {
703                        Stmt::Return(ReturnStmt { arg: Some(arg), .. }) => {
704                            Box::new(BlockStmtOrExpr::Expr(arg))
705                        }
706                        _ => unreachable!(),
707                    }
708                } else {
709                    Box::new(BlockStmtOrExpr::BlockStmt(body))
710                };
711
712                *e = ArrowExpr {
713                    params: params.into_iter().map(|param| param.pat).collect(),
714                    body,
715                    span: f.span,
716                    is_async: f.is_async,
717                    is_generator: f.is_generator,
718                    type_params: f.type_params.take(),
719                    return_type: f.return_type.take(),
720                    ..Default::default()
721                }
722                .into();
723            }
724            _ => e.visit_mut_children_with(self),
725        }
726    }
727
728    fn visit_mut_function(&mut self, f: &mut Function) {
729        if f.body.is_none() {
730            return;
731        }
732
733        let old_in_subclass = self.in_subclass;
734        let old_in_prop = self.in_prop;
735        self.in_subclass = false;
736        self.in_prop = false;
737
738        f.visit_mut_children_with(self);
739
740        let mut body = f.body.take().unwrap();
741        self.visit_mut_fn_like(&mut f.params, &mut body, false);
742
743        f.body = Some(body);
744
745        self.in_subclass = old_in_subclass;
746        self.in_prop = old_in_prop;
747    }
748
749    fn visit_mut_getter_prop(&mut self, f: &mut GetterProp) {
750        if f.body.is_none() {
751            return;
752        }
753
754        f.visit_mut_children_with(self);
755
756        let mut params = Vec::new();
757        let mut body = f.body.take().unwrap();
758        self.visit_mut_fn_like(&mut params, &mut body, false);
759        debug_assert_eq!(params, Vec::new());
760
761        f.body = Some(body);
762    }
763
764    fn visit_mut_setter_prop(&mut self, f: &mut SetterProp) {
765        if f.body.is_none() {
766            return;
767        }
768
769        f.visit_mut_children_with(self);
770
771        let mut params = vec![Param {
772            span: DUMMY_SP,
773            decorators: Default::default(),
774            pat: *f.param.take(),
775        }];
776
777        let mut body = f.body.take().unwrap();
778        self.visit_mut_fn_like(&mut params, &mut body, true);
779
780        debug_assert!(params.len() == 1);
781
782        f.param = Box::new(params.pop().unwrap().pat);
783        f.body = Some(body);
784    }
785
786    fn visit_mut_class(&mut self, c: &mut Class) {
787        let old_in_subclass = self.in_subclass;
788        let old_in_prop = self.in_prop;
789
790        self.in_subclass = c.super_class.is_some();
791        self.in_prop = false;
792        c.visit_mut_children_with(self);
793
794        self.in_subclass = old_in_subclass;
795        self.in_prop = old_in_prop;
796    }
797
798    fn visit_mut_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {
799        stmts.visit_mut_children_with(self);
800
801        let decl = self.hoister.take().to_stmt();
802
803        if let Some(stmt) = decl {
804            prepend_stmt(stmts, stmt.into());
805        }
806    }
807
808    fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
809        let old_rep = self.hoister.take();
810
811        stmts.visit_mut_children_with(self);
812
813        let decl = mem::replace(&mut self.hoister, old_rep).to_stmt();
814
815        if let Some(stmt) = decl {
816            prepend_stmt(stmts, stmt);
817        }
818    }
819}
820
821fn make_arg_nth(n: usize) -> MemberExpr {
822    Ident::new_no_ctxt(atom!("arguments"), DUMMY_SP).computed_member(n)
823}
824
825fn check_arg_len(n: usize) -> Expr {
826    BinExpr {
827        left: Expr::Ident(Ident::new_no_ctxt(atom!("arguments"), DUMMY_SP))
828            .make_member(IdentName::new(atom!("length"), DUMMY_SP))
829            .into(),
830        op: op!(">"),
831        right: n.into(),
832        span: DUMMY_SP,
833    }
834    .into()
835}
836
837fn check_arg_len_or_undef(n: usize) -> Expr {
838    CondExpr {
839        test: Box::new(check_arg_len(n)),
840        cons: make_arg_nth(n).into(),
841        alt: Expr::undefined(DUMMY_SP),
842        span: DUMMY_SP,
843    }
844    .into()
845}