swc_ecma_compat_es2015/
parameters.rs

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::*;
7// use swc_ecma_transforms_base::perf::Parallel;
8// use swc_ecma_transforms_macros::parallel;
9use 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    /// Used to store `this, in case if `arguments` is used and we should
30    /// transform an arrow expression to a function expression.
31    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// impl Parallel for Params {
46//     fn create(&self) -> Self {
47//         Params {
48//             state: Default::default(),
49//             c: self.c,
50//         }
51//     }
52
53//     fn merge(&mut self, other: Self) {
54//         self.state.merge(other.state);
55//     }
56
57//     fn after_stmts(&mut self, stmts: &mut Vec<Stmt>) {
58//         let decls = self.state.take().to_stmt();
59//         if let Some(decls) = decls {
60//             prepend(stmts, decls)
61//         }
62//     }
63
64//     fn after_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {
65//         let decls = self.state.take().to_stmt();
66//         if let Some(decls) = decls {
67//             prepend(stmts, ModuleItem::Stmt(decls))
68//         }
69//     }
70// }
71
72#[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                    // arguments.length will always be 1 in setter
138                    if !(self.c.ignore_function_length || is_setter) {
139                        after_default = true;
140                        // access non-existent element in `arguments` is expensive
141                        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                    // Inject a for statement
234                    //
235                    // for(var _len = arguments.length, a1 = new Array(_len), _key = 0; _key <
236                    // _len; _key++){
237                    //      a1[_key] = arguments[_key];
238                    // }
239                    assert!(unpack_rest.is_none());
240
241                    // TODO: Optimize (use `arguments` instead of rest argument)
242
243                    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                            // `len`
267                            ident.clone().into()
268                        } else {
269                            // `len - $i`
270                            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                                        // _len = arguments.length - i
318                                        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                                        // a1 = new Array(_len - $i)
332                                        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                                                    // `len` or  `len - $i`
343                                                    make_minus_i(&len_ident, true).as_arg()
344                                                }]),
345                                                ..Default::default()
346                                            }))),
347                                            definite: false,
348                                        },
349                                        // _key = 0
350                                        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                            // `_key < _len`
367                            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                            // _key++
377                            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                                    // a1[_key - i] = arguments[_key];
391
392                                    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    // generally speaking, there won't be class field in here, but Safari 14.1
472    // still has bugs in parameters
473    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    // same for private prop
510    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                // this needs to happen before rest parameter transform
622                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}