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                };
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}