swc_ecma_compat_es2015/
destructuring.rs

1use std::{iter, mem};
2
3use serde::Deserialize;
4use swc_common::{util::take::Take, Span, Spanned, SyntaxContext, DUMMY_SP};
5use swc_ecma_ast::*;
6use swc_ecma_compat_common::impl_visit_mut_fn;
7use swc_ecma_transforms_base::{helper, helper_expr, perf::Check};
8use swc_ecma_transforms_macros::fast_path;
9use swc_ecma_utils::{
10    alias_ident_for, alias_if_required, has_rest_pat, is_literal, member_expr, private_ident,
11    prop_name_to_expr, quote_ident, ExprFactory, StmtLike,
12};
13use swc_ecma_visit::{
14    noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith, VisitWith,
15};
16use swc_trace_macro::swc_trace;
17
18/// `@babel/plugin-transform-destructuring`
19///
20/// # Example
21/// ## In
22/// ```js
23/// let {x, y} = obj;
24///
25/// let [a, b, ...rest] = arr;
26/// ```
27/// ## Out
28/// ```js
29/// let _obj = obj,
30///     x = _obj.x,
31///     y = _obj.y;
32///
33/// let _arr = arr,
34///     _arr2 = _to_array(_arr),
35///     a = _arr2[0],
36///     b = _arr2[1],
37///     rest = _arr2.slice(2);
38/// ```
39pub fn destructuring(c: Config) -> impl Pass {
40    visit_mut_pass(Destructuring { c })
41}
42
43struct Destructuring {
44    c: Config,
45}
46
47#[derive(Debug, Default, Clone, Copy, Deserialize)]
48pub struct Config {
49    #[serde(default)]
50    pub loose: bool,
51}
52
53macro_rules! impl_for_for_stmt {
54    ($name:ident, $T:tt) => {
55        fn $name(&mut self, for_stmt: &mut $T) {
56            for_stmt.visit_mut_children_with(self);
57
58            let (left, stmt) = match &mut for_stmt.left {
59                ForHead::VarDecl(var_decl) => {
60                    let has_complex = var_decl.decls.iter().any(|d| match d.name {
61                        Pat::Ident(_) => false,
62                        _ => true,
63                    });
64
65                    if !has_complex {
66                        return;
67                    }
68                    let ref_ident = make_ref_ident_for_for_stmt();
69                    let left = VarDecl {
70                        decls: vec![VarDeclarator {
71                            span: DUMMY_SP,
72                            name: ref_ident.clone().into(),
73                            init: None,
74                            definite: false,
75                        }],
76                        span: var_decl.span,
77                        kind: var_decl.kind,
78                        declare: var_decl.declare,
79                        ..Default::default()
80                    }
81                    .into();
82
83                    // I(kdy1) guess var_decl.len() == 1
84                    let mut decls = var_decl
85                        .decls
86                        .take()
87                        .into_iter()
88                        .map(|decl| VarDeclarator {
89                            init: Some(Box::new(Expr::Ident(ref_ident.clone()))),
90                            ..decl
91                        })
92                        .collect::<Vec<_>>();
93                    decls.visit_mut_children_with(self);
94
95                    // Unpack variables
96                    let stmt: Stmt = VarDecl {
97                        span: var_decl.span(),
98                        kind: VarDeclKind::Let,
99                        decls,
100                        ..Default::default()
101                    }
102                    .into();
103                    (left, stmt)
104                }
105                ForHead::Pat(pat) => match **pat {
106                    Pat::Ident(..) => {
107                        return;
108                    }
109                    _ => {
110                        let left_ident = make_ref_ident_for_for_stmt();
111                        let left = ForHead::Pat(left_ident.clone().into());
112                        // Unpack variables
113                        let stmt = AssignExpr {
114                            span: DUMMY_SP,
115                            left: pat.take().try_into().unwrap(),
116                            op: op!("="),
117                            right: Box::new(left_ident.into()),
118                        }
119                        .into_stmt();
120                        (left, stmt)
121                    }
122                },
123
124                ForHead::UsingDecl(..) => {
125                    unreachable!("using declaration must be removed by previous pass")
126                }
127            };
128
129            for_stmt.left = left;
130
131            for_stmt.body = Box::new(Stmt::Block(match &mut *for_stmt.body {
132                Stmt::Block(BlockStmt { span, stmts, ctxt }) => BlockStmt {
133                    span: *span,
134                    stmts: iter::once(stmt).chain(stmts.take()).collect(),
135                    ctxt: *ctxt,
136                },
137                body => BlockStmt {
138                    stmts: vec![stmt, body.take()],
139                    ..Default::default()
140                },
141            }));
142        }
143    };
144}
145
146fn make_ref_ident_for_for_stmt() -> Ident {
147    private_ident!("ref")
148}
149
150#[swc_trace]
151impl AssignFolder {
152    fn visit_mut_var_decl(&mut self, decls: &mut Vec<VarDeclarator>, decl: VarDeclarator) {
153        match decl.name {
154            Pat::Ident(..) => decls.push(decl),
155            Pat::Rest(..) => unreachable!(
156                "rest pattern should handled by array pattern handler: {:?}",
157                decl.name
158            ),
159            Pat::Array(ArrayPat { elems, .. }) => {
160                assert!(
161                    decl.init.is_some(),
162                    "destructuring pattern binding requires initializer"
163                );
164
165                let init = decl.init.unwrap();
166
167                if is_literal(&init) {
168                    match *init {
169                        Expr::Array(arr)
170                            if !elems.is_empty()
171                                && (elems.len() == arr.elems.len()
172                                    || (elems.len() < arr.elems.len() && has_rest_pat(&elems))) =>
173                        {
174                            let mut arr_elems = Some(arr.elems.into_iter());
175                            elems.into_iter().for_each(|p| match p {
176                                Some(Pat::Rest(p)) => {
177                                    self.visit_mut_var_decl(
178                                        decls,
179                                        VarDeclarator {
180                                            span: p.span(),
181                                            name: *p.arg,
182                                            init: Some(
183                                                ArrayLit {
184                                                    span: DUMMY_SP,
185                                                    elems: arr_elems
186                                                        .take()
187                                                        .expect("two rest element?")
188                                                        .collect(),
189                                                }
190                                                .into(),
191                                            ),
192                                            definite: false,
193                                        },
194                                    );
195                                }
196                                Some(p) => {
197                                    let e = arr_elems
198                                        .as_mut()
199                                        .expect("pattern after rest element?")
200                                        .next()
201                                        .unwrap();
202                                    self.visit_mut_var_decl(
203                                        decls,
204                                        VarDeclarator {
205                                            span: p.span(),
206                                            init: e.map(|e| {
207                                                debug_assert_eq!(e.spread, None);
208                                                e.expr
209                                            }),
210                                            name: p,
211                                            definite: false,
212                                        },
213                                    )
214                                }
215
216                                None => {
217                                    arr_elems
218                                        .as_mut()
219                                        .expect("pattern after rest element?")
220                                        .next();
221                                }
222                            });
223                            return;
224                        }
225                        _ => {}
226                    }
227                }
228                // Make ref var if required
229                let ref_ident = make_ref_ident_for_array(
230                    self.c,
231                    if self.exporting {
232                        &mut self.vars
233                    } else {
234                        decls
235                    },
236                    Some(init),
237                    Some(if has_rest_pat(&elems) {
238                        usize::MAX
239                    } else {
240                        elems.len()
241                    }),
242                );
243
244                for (i, elem) in elems.into_iter().enumerate() {
245                    let elem: Pat = match elem {
246                        Some(elem) => elem,
247                        None => continue,
248                    };
249
250                    let var_decl = match elem {
251                        Pat::Rest(RestPat {
252                            dot3_token, arg, ..
253                        }) => VarDeclarator {
254                            span: dot3_token,
255                            name: *arg,
256                            init: Some(
257                                CallExpr {
258                                    span: DUMMY_SP,
259                                    callee: ref_ident
260                                        .clone()
261                                        .make_member(quote_ident!("slice"))
262                                        .as_callee(),
263                                    args: vec![Number {
264                                        value: i as f64,
265                                        span: dot3_token,
266                                        raw: None,
267                                    }
268                                    .as_arg()],
269                                    ..Default::default()
270                                }
271                                .into(),
272                            ),
273                            definite: false,
274                        },
275                        _ => VarDeclarator {
276                            span: elem.span(),
277                            // This might be pattern.
278                            // So we fold it again.
279                            name: elem,
280                            init: Some(make_ref_idx_expr(&ref_ident, i).into()),
281                            definite: false,
282                        },
283                    };
284
285                    let mut var_decls = vec![var_decl];
286                    var_decls.visit_mut_with(self);
287                    decls.extend(var_decls);
288                }
289            }
290            Pat::Object(ObjectPat { span, props, .. }) if props.is_empty() => {
291                // We should convert
292                //
293                //      var {} = null;
294                //
295                // to
296                //
297                //      var _ref = null;
298                //      _object_destructuring_empty(_ref);
299                //
300
301                let expr = helper_expr!(object_destructuring_empty).as_call(
302                    DUMMY_SP,
303                    vec![decl
304                        .init
305                        .expect("destructuring must be initialized")
306                        .as_arg()],
307                );
308
309                let var_decl = VarDeclarator {
310                    span: DUMMY_SP,
311                    name: private_ident!(span, "ref").into(),
312                    init: Some(Box::new(expr)),
313                    definite: false,
314                };
315
316                decls.push(var_decl);
317            }
318
319            Pat::Object(ObjectPat { props, .. }) => {
320                assert!(
321                    decl.init.is_some(),
322                    "destructuring pattern binding requires initializer"
323                );
324
325                if props.len() == 1 {
326                    if let ObjectPatProp::Assign(p @ AssignPatProp { value: None, .. }) = &props[0]
327                    {
328                        decls.push(VarDeclarator {
329                            span: decl.span,
330                            name: p.key.clone().into(),
331                            init: Some(decl.init.unwrap().make_member(p.key.clone().into()).into()),
332                            definite: false,
333                        });
334                        return;
335                    }
336                }
337
338                let can_be_null = can_be_null(decl.init.as_ref().unwrap());
339
340                let ref_decls = if self.exporting {
341                    &mut self.vars
342                } else {
343                    &mut *decls
344                };
345
346                let ref_ident = make_ref_ident(self.c, ref_decls, decl.init);
347
348                let ref_ident = if can_be_null {
349                    let init = ref_ident.into();
350                    make_ref_ident(self.c, ref_decls, Some(init))
351                } else {
352                    ref_ident
353                };
354
355                for prop in props {
356                    let prop_span = prop.span();
357
358                    match prop {
359                        ObjectPatProp::KeyValue(KeyValuePatProp { key, value }) => {
360                            let computed = matches!(key, PropName::Computed(..));
361
362                            let var_decl = VarDeclarator {
363                                span: prop_span,
364                                name: *value,
365                                init: Some(Box::new(make_ref_prop_expr(
366                                    &ref_ident,
367                                    Box::new(prop_name_to_expr(key)),
368                                    computed,
369                                ))),
370                                definite: false,
371                            };
372
373                            let mut var_decls = vec![var_decl];
374                            var_decls.visit_mut_with(self);
375                            decls.extend(var_decls);
376                        }
377                        ObjectPatProp::Assign(AssignPatProp { key, value, .. }) => {
378                            let computed = false;
379
380                            match value {
381                                Some(value) => {
382                                    let ref_ident = make_ref_ident(
383                                        self.c,
384                                        decls,
385                                        Some(Box::new(make_ref_prop_expr(
386                                            &ref_ident,
387                                            key.clone().into(),
388                                            computed,
389                                        ))),
390                                    );
391
392                                    let var_decl = VarDeclarator {
393                                        span: prop_span,
394                                        name: key.clone().into(),
395                                        init: Some(Box::new(make_cond_expr(ref_ident, value))),
396                                        definite: false,
397                                    };
398                                    let mut var_decls = vec![var_decl];
399                                    var_decls.visit_mut_with(self);
400                                    decls.extend(var_decls);
401                                }
402                                None => {
403                                    let var_decl = VarDeclarator {
404                                        span: prop_span,
405                                        name: key.clone().into(),
406                                        init: Some(Box::new(make_ref_prop_expr(
407                                            &ref_ident,
408                                            key.clone().into(),
409                                            computed,
410                                        ))),
411                                        definite: false,
412                                    };
413                                    let mut var_decls = vec![var_decl];
414                                    var_decls.visit_mut_with(self);
415                                    decls.extend(var_decls);
416                                }
417                            }
418                        }
419                        ObjectPatProp::Rest(..) => unreachable!(
420                            "Object rest pattern should be removed by es2018::object_rest_spread \
421                             pass"
422                        ),
423                    }
424                }
425            }
426            Pat::Assign(AssignPat {
427                span,
428                left,
429                right: def_value,
430                ..
431            }) => {
432                let init = if let Some(init) = decl.init {
433                    let tmp_ident = match &*init {
434                        Expr::Ident(ref i) if i.ctxt != SyntaxContext::empty() => i.clone(),
435
436                        _ => {
437                            let tmp_ident = private_ident!(span, "tmp");
438                            decls.push(VarDeclarator {
439                                span: DUMMY_SP,
440                                name: tmp_ident.clone().into(),
441                                init: Some(init),
442                                definite: false,
443                            });
444
445                            tmp_ident
446                        }
447                    };
448
449                    // tmp === void 0 ? def_value : tmp
450                    Some(Box::new(make_cond_expr(tmp_ident, def_value)))
451                } else {
452                    Some(def_value)
453                };
454
455                let var_decl = VarDeclarator {
456                    span,
457                    name: *left,
458                    init,
459                    definite: false,
460                };
461
462                let mut var_decls = vec![var_decl];
463                var_decls.visit_mut_with(self);
464                decls.extend(var_decls);
465            }
466
467            _ => unimplemented!("Pattern {:?}", decl),
468        }
469    }
470}
471
472#[swc_trace]
473#[fast_path(DestructuringVisitor)]
474impl VisitMut for Destructuring {
475    noop_visit_mut_type!(fail);
476
477    impl_for_for_stmt!(visit_mut_for_in_stmt, ForInStmt);
478
479    impl_for_for_stmt!(visit_mut_for_of_stmt, ForOfStmt);
480
481    impl_visit_mut_fn!();
482
483    fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
484        self.visit_mut_stmt_like(n);
485    }
486
487    fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
488        self.visit_mut_stmt_like(n);
489    }
490}
491
492#[swc_trace]
493impl Destructuring {
494    fn visit_mut_fn_like(
495        &mut self,
496        ps: &mut Vec<Param>,
497        body: &mut BlockStmt,
498    ) -> (Vec<Param>, BlockStmt) {
499        let mut params = Vec::new();
500        let mut decls = Vec::new();
501
502        for param in ps.drain(..) {
503            let span = param.span();
504            match param.pat {
505                Pat::Ident(..) => params.push(param),
506                Pat::Array(..) | Pat::Object(..) | Pat::Assign(..) => {
507                    let ref_ident = private_ident!(span, "ref");
508
509                    params.push(Param {
510                        span: DUMMY_SP,
511                        decorators: Default::default(),
512                        pat: ref_ident.clone().into(),
513                    });
514                    decls.push(VarDeclarator {
515                        span,
516                        name: param.pat,
517                        init: Some(ref_ident.into()),
518                        definite: false,
519                    })
520                }
521                Pat::Rest(..) | Pat::Expr(..) => params.push(param),
522                Pat::Invalid(..) => {}
523            }
524        }
525
526        let stmts = if decls.is_empty() {
527            body.stmts.take()
528        } else {
529            let mut stmt: Stmt = VarDecl {
530                span: DUMMY_SP,
531                kind: VarDeclKind::Let,
532                decls,
533                declare: false,
534                ..Default::default()
535            }
536            .into();
537
538            stmt.visit_mut_children_with(self);
539
540            iter::once(stmt).chain(body.stmts.take()).collect()
541        };
542
543        (
544            params,
545            BlockStmt {
546                stmts,
547                ..body.take()
548            },
549        )
550    }
551}
552
553struct AssignFolder {
554    c: Config,
555    exporting: bool,
556    vars: Vec<VarDeclarator>,
557    /// Used like `.take().is_some()`.
558    ignore_return_value: Option<()>,
559}
560
561impl AssignFolder {
562    pub fn handle_assign_pat(
563        &mut self,
564        span: Span,
565        mut pat: AssignPat,
566        right: &mut Box<Expr>,
567    ) -> Expr {
568        let ref_ident = make_ref_ident(self.c, &mut self.vars, None);
569
570        let mut exprs = vec![Box::new(
571            AssignExpr {
572                span,
573                left: ref_ident.clone().into(),
574                op: op!("="),
575                right: right.take(),
576            }
577            .into(),
578        )];
579
580        let mut assign_cond_expr: Expr = AssignExpr {
581            span,
582            left: pat.left.take().try_into().unwrap(),
583            op: op!("="),
584            right: Box::new(make_cond_expr(ref_ident, pat.right.take())),
585        }
586        .into();
587
588        assign_cond_expr.visit_mut_with(self);
589        exprs.push(Box::new(assign_cond_expr));
590
591        SeqExpr {
592            span: DUMMY_SP,
593            exprs,
594        }
595        .into()
596    }
597}
598
599#[swc_trace]
600#[fast_path(DestructuringVisitor)]
601impl VisitMut for AssignFolder {
602    noop_visit_mut_type!(fail);
603
604    fn visit_mut_export_decl(&mut self, decl: &mut ExportDecl) {
605        let old = self.exporting;
606        self.exporting = true;
607        decl.visit_mut_children_with(self);
608        self.exporting = old;
609    }
610
611    fn visit_mut_function(&mut self, f: &mut Function) {
612        let exporting = mem::replace(&mut self.exporting, false);
613        f.visit_mut_children_with(self);
614        self.exporting = exporting;
615    }
616
617    fn visit_mut_class(&mut self, f: &mut Class) {
618        let exporting = mem::replace(&mut self.exporting, false);
619        f.visit_mut_children_with(self);
620        self.exporting = exporting;
621    }
622
623    fn visit_mut_object_lit(&mut self, f: &mut ObjectLit) {
624        let exporting = mem::replace(&mut self.exporting, false);
625        f.visit_mut_children_with(self);
626        self.exporting = exporting;
627    }
628
629    fn visit_mut_arrow_expr(&mut self, f: &mut ArrowExpr) {
630        let exporting = mem::replace(&mut self.exporting, false);
631        f.visit_mut_children_with(self);
632        self.exporting = exporting;
633    }
634
635    fn visit_mut_expr(&mut self, expr: &mut Expr) {
636        let ignore_return_value = self.ignore_return_value.take().is_some();
637
638        match expr {
639            // Handle iife
640            Expr::Fn(..) | Expr::Object(..) => {
641                expr.visit_mut_with(&mut Destructuring { c: self.c })
642            }
643            _ => expr.visit_mut_children_with(self),
644        };
645
646        if let Expr::Assign(AssignExpr {
647            span,
648            left: AssignTarget::Pat(pat),
649            op: op!("="),
650            right,
651        }) = expr
652        {
653            match pat {
654                // Pat::Expr(pat_expr) => {
655                //     *expr = Expr::Assign(AssignExpr {
656                //         span: *span,
657                //         left: pat_expr.take().try_into().unwrap(),
658                //         op: op!("="),
659                //         right: right.take(),
660                //     });
661                // }
662                AssignTargetPat::Array(ArrayPat { elems, .. }) => {
663                    let mut exprs = Vec::with_capacity(elems.len() + 1);
664
665                    if is_literal(right) && ignore_return_value {
666                        match &mut **right {
667                            Expr::Array(arr)
668                                if elems.len() == arr.elems.len()
669                                    || (elems.len() < arr.elems.len() && has_rest_pat(elems)) =>
670                            {
671                                let mut arr_elems = Some(arr.elems.take().into_iter());
672                                elems.iter_mut().for_each(|p| match p {
673                                    Some(Pat::Rest(p)) => {
674                                        exprs.push(
675                                            AssignExpr {
676                                                span: p.span(),
677                                                left: p.arg.take().try_into().unwrap(),
678                                                op: op!("="),
679                                                right: Box::new(
680                                                    ArrayLit {
681                                                        span: DUMMY_SP,
682                                                        elems: arr_elems
683                                                            .take()
684                                                            .expect("two rest element?")
685                                                            .collect(),
686                                                    }
687                                                    .into(),
688                                                ),
689                                            }
690                                            .into(),
691                                        );
692                                    }
693                                    Some(p) => {
694                                        let e = arr_elems
695                                            .as_mut()
696                                            .expect("pattern after rest element?")
697                                            .next()
698                                            .and_then(|v| v);
699                                        let mut right = e
700                                            .map(|e| {
701                                                debug_assert_eq!(e.spread, None);
702                                                e.expr
703                                            })
704                                            .unwrap_or_else(|| Expr::undefined(p.span()));
705
706                                        let p = p.take();
707                                        let mut expr = if let Pat::Assign(pat) = p {
708                                            self.handle_assign_pat(*span, pat, &mut right)
709                                        } else {
710                                            AssignExpr {
711                                                span: p.span(),
712                                                left: p.try_into().unwrap(),
713                                                op: op!("="),
714                                                right,
715                                            }
716                                            .into()
717                                        };
718
719                                        self.visit_mut_expr(&mut expr);
720
721                                        exprs.push(Box::new(expr));
722                                    }
723
724                                    None => {
725                                        arr_elems
726                                            .as_mut()
727                                            .expect("pattern after rest element?")
728                                            .next();
729                                    }
730                                });
731                                *expr = SeqExpr { span: *span, exprs }.into();
732                                return;
733                            }
734                            _ => {}
735                        }
736                    }
737
738                    // initialized by first element of sequence expression
739                    let ref_ident = make_ref_ident_for_array(
740                        self.c,
741                        &mut self.vars,
742                        None,
743                        Some(if has_rest_pat(elems) {
744                            usize::MAX
745                        } else {
746                            elems.len()
747                        }),
748                    );
749                    exprs.push(
750                        AssignExpr {
751                            span: DUMMY_SP,
752                            op: op!("="),
753                            left: ref_ident.clone().into(),
754                            right: if self.c.loose {
755                                right.take()
756                            } else {
757                                match &mut **right {
758                                    Expr::Ident(Ident { sym, .. }) if &**sym == "arguments" => {
759                                        Box::new(
760                                            CallExpr {
761                                                span: DUMMY_SP,
762                                                callee: member_expr!(
763                                                    Default::default(),
764                                                    Default::default(),
765                                                    Array.prototype.slice.call
766                                                )
767                                                .as_callee(),
768                                                args: vec![right.take().as_arg()],
769                                                ..Default::default()
770                                            }
771                                            .into(),
772                                        )
773                                    }
774                                    Expr::Array(..) => right.take(),
775                                    _ => {
776                                        // if left has rest then need `_to_array`
777                                        // else `_sliced_to_array`
778                                        if elems
779                                            .iter()
780                                            .any(|elem| matches!(elem, Some(Pat::Rest(..))))
781                                        {
782                                            Box::new(
783                                                CallExpr {
784                                                    span: DUMMY_SP,
785                                                    callee: helper!(to_array),
786                                                    args: vec![right.take().as_arg()],
787                                                    ..Default::default()
788                                                }
789                                                .into(),
790                                            )
791                                        } else {
792                                            Box::new(
793                                                CallExpr {
794                                                    span: DUMMY_SP,
795                                                    callee: helper!(sliced_to_array),
796                                                    args: vec![
797                                                        right.take().as_arg(),
798                                                        elems.len().as_arg(),
799                                                    ],
800                                                    ..Default::default()
801                                                }
802                                                .into(),
803                                            )
804                                        }
805                                    }
806                                }
807                            },
808                        }
809                        .into(),
810                    );
811
812                    for (i, elem) in elems.iter_mut().enumerate() {
813                        let elem = match elem {
814                            Some(elem) => elem,
815                            None => continue,
816                        };
817                        let elem_span = elem.span();
818
819                        match elem {
820                            Pat::Assign(AssignPat {
821                                span, left, right, ..
822                            }) => {
823                                // initialized by sequence expression.
824                                let assign_ref_ident = make_ref_ident(self.c, &mut self.vars, None);
825                                exprs.push(
826                                    AssignExpr {
827                                        span: DUMMY_SP,
828                                        left: assign_ref_ident.clone().into(),
829                                        op: op!("="),
830                                        right: ref_ident.clone().computed_member(i as f64).into(),
831                                    }
832                                    .into(),
833                                );
834
835                                let mut assign_expr: Expr = AssignExpr {
836                                    span: *span,
837                                    left: left.take().try_into().unwrap(),
838                                    op: op!("="),
839                                    right: Box::new(make_cond_expr(assign_ref_ident, right.take())),
840                                }
841                                .into();
842                                assign_expr.visit_mut_with(self);
843
844                                exprs.push(Box::new(assign_expr));
845                            }
846                            Pat::Rest(RestPat { arg, .. }) => {
847                                let mut assign_expr: Expr = AssignExpr {
848                                    span: elem_span,
849                                    op: op!("="),
850                                    left: arg.take().try_into().unwrap(),
851                                    right: CallExpr {
852                                        span: DUMMY_SP,
853                                        callee: ref_ident
854                                            .clone()
855                                            .make_member(quote_ident!("slice"))
856                                            .as_callee(),
857                                        args: vec![(i as f64).as_arg()],
858                                        ..Default::default()
859                                    }
860                                    .into(),
861                                }
862                                .into();
863
864                                assign_expr.visit_mut_with(self);
865                                exprs.push(Box::new(assign_expr));
866                            }
867                            _ => {
868                                let mut assign_expr: Expr = AssignExpr {
869                                    span: elem_span,
870                                    op: op!("="),
871                                    left: elem.take().try_into().unwrap(),
872                                    right: make_ref_idx_expr(&ref_ident, i).into(),
873                                }
874                                .into();
875
876                                assign_expr.visit_mut_with(self);
877                                exprs.push(Box::new(assign_expr))
878                            }
879                        }
880                    }
881
882                    // last one should be `ref`
883                    exprs.push(ref_ident.into());
884
885                    *expr = SeqExpr {
886                        span: DUMMY_SP,
887                        exprs,
888                    }
889                    .into()
890                }
891                AssignTargetPat::Object(ObjectPat { props, .. }) if props.is_empty() => {
892                    let mut right = right.take();
893                    right.visit_mut_with(self);
894
895                    *expr = helper_expr!(object_destructuring_empty)
896                        .as_call(DUMMY_SP, vec![right.as_arg()]);
897                }
898                AssignTargetPat::Object(ObjectPat { span, props, .. }) => {
899                    if props.len() == 1 {
900                        if let ObjectPatProp::Assign(p @ AssignPatProp { value: None, .. }) =
901                            &props[0]
902                        {
903                            *expr = AssignExpr {
904                                span: *span,
905                                op: op!("="),
906                                left: p.key.clone().into(),
907                                right: right.take().make_member(p.key.clone().into()).into(),
908                            }
909                            .into();
910                            return;
911                        }
912                    }
913
914                    let ref_ident = make_ref_ident(self.c, &mut self.vars, None);
915
916                    let mut exprs = vec![Box::new(Expr::Assign(AssignExpr {
917                        span: *span,
918                        left: ref_ident.clone().into(),
919                        op: op!("="),
920                        right: right.take(),
921                    }))];
922
923                    for prop in props {
924                        let span = prop.span();
925                        match prop {
926                            ObjectPatProp::KeyValue(KeyValuePatProp { key, value }) => {
927                                let computed = matches!(key, PropName::Computed(..));
928
929                                let mut right = Box::new(make_ref_prop_expr(
930                                    &ref_ident,
931                                    Box::new(prop_name_to_expr(key.take())),
932                                    computed,
933                                ));
934                                let value = value.take();
935
936                                let mut expr = if let Pat::Assign(pat) = *value {
937                                    self.handle_assign_pat(span, pat, &mut right)
938                                } else {
939                                    AssignExpr {
940                                        span,
941                                        left: value.try_into().unwrap(),
942                                        op: op!("="),
943                                        right,
944                                    }
945                                    .into()
946                                };
947
948                                expr.visit_mut_with(self);
949                                exprs.push(Box::new(expr));
950                            }
951                            ObjectPatProp::Assign(AssignPatProp { key, value, .. }) => {
952                                let computed = false;
953
954                                match value {
955                                    Some(value) => {
956                                        let prop_ident =
957                                            make_ref_ident(self.c, &mut self.vars, None);
958
959                                        exprs.push(
960                                            AssignExpr {
961                                                span,
962                                                left: prop_ident.clone().into(),
963                                                op: op!("="),
964                                                right: Box::new(make_ref_prop_expr(
965                                                    &ref_ident,
966                                                    key.clone().into(),
967                                                    computed,
968                                                )),
969                                            }
970                                            .into(),
971                                        );
972
973                                        exprs.push(
974                                            AssignExpr {
975                                                span,
976                                                left: key.clone().into(),
977                                                op: op!("="),
978                                                right: Box::new(make_cond_expr(
979                                                    prop_ident,
980                                                    value.take(),
981                                                )),
982                                            }
983                                            .into(),
984                                        );
985                                    }
986                                    None => {
987                                        exprs.push(
988                                            AssignExpr {
989                                                span,
990                                                left: key.clone().into(),
991                                                op: op!("="),
992                                                right: Box::new(make_ref_prop_expr(
993                                                    &ref_ident,
994                                                    key.clone().into(),
995                                                    computed,
996                                                )),
997                                            }
998                                            .into(),
999                                        );
1000                                    }
1001                                }
1002                            }
1003                            ObjectPatProp::Rest(_) => unreachable!(
1004                                "object rest pattern should be removed by \
1005                                 es2018::object_rest_spread pass"
1006                            ),
1007                        }
1008                    }
1009
1010                    // Last one should be object itself.
1011                    exprs.push(ref_ident.into());
1012
1013                    *expr = SeqExpr {
1014                        span: DUMMY_SP,
1015                        exprs,
1016                    }
1017                    .into();
1018                }
1019
1020                AssignTargetPat::Invalid(..) => unreachable!(),
1021            }
1022        };
1023    }
1024
1025    fn visit_mut_stmt(&mut self, s: &mut Stmt) {
1026        match s {
1027            Stmt::Expr(e) => {
1028                self.ignore_return_value = Some(());
1029                e.visit_mut_with(self);
1030                assert_eq!(self.ignore_return_value, None);
1031            }
1032            _ => s.visit_mut_children_with(self),
1033        };
1034    }
1035
1036    fn visit_mut_var_declarators(&mut self, declarators: &mut Vec<VarDeclarator>) {
1037        declarators.visit_mut_children_with(self);
1038
1039        let is_complex = declarators
1040            .iter()
1041            .any(|d| !matches!(d.name, Pat::Ident(..)));
1042        if !is_complex {
1043            return;
1044        }
1045
1046        let mut decls = Vec::with_capacity(declarators.len());
1047        for decl in declarators.drain(..) {
1048            self.visit_mut_var_decl(&mut decls, decl);
1049        }
1050
1051        *declarators = decls;
1052    }
1053
1054    fn visit_mut_var_decl(&mut self, var_decl: &mut VarDecl) {
1055        var_decl.decls.visit_mut_with(self);
1056
1057        if var_decl.kind == VarDeclKind::Const {
1058            var_decl.decls.iter_mut().for_each(|v| {
1059                if v.init.is_none() {
1060                    v.init = Some(Expr::undefined(DUMMY_SP));
1061                }
1062            })
1063        }
1064    }
1065}
1066
1067#[swc_trace]
1068impl Destructuring {
1069    fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
1070    where
1071        Vec<T>: VisitMutWith<Self>,
1072        T: StmtLike + VisitMutWith<AssignFolder>,
1073    {
1074        stmts.visit_mut_children_with(self);
1075
1076        let mut stmts_updated = Vec::with_capacity(stmts.len());
1077
1078        for stmt in stmts.drain(..) {
1079            let mut folder = AssignFolder {
1080                c: self.c,
1081                exporting: false,
1082                vars: Vec::new(),
1083                ignore_return_value: None,
1084            };
1085
1086            match stmt.try_into_stmt() {
1087                Err(mut item) => {
1088                    item.visit_mut_with(&mut folder);
1089
1090                    // Add variable declaration
1091                    // e.g. var ref
1092                    if !folder.vars.is_empty() {
1093                        stmts_updated.push(T::from(
1094                            VarDecl {
1095                                span: DUMMY_SP,
1096                                kind: VarDeclKind::Var,
1097                                decls: folder.vars,
1098                                ..Default::default()
1099                            }
1100                            .into(),
1101                        ));
1102                    }
1103
1104                    stmts_updated.push(item);
1105                }
1106                Ok(mut stmt) => {
1107                    stmt.visit_mut_with(&mut folder);
1108
1109                    // Add variable declaration
1110                    // e.g. var ref
1111                    if !folder.vars.is_empty() {
1112                        stmts_updated.push(T::from(
1113                            VarDecl {
1114                                span: DUMMY_SP,
1115                                kind: VarDeclKind::Var,
1116                                decls: folder.vars,
1117                                ..Default::default()
1118                            }
1119                            .into(),
1120                        ));
1121                    }
1122
1123                    stmts_updated.push(T::from(stmt));
1124                }
1125            }
1126        }
1127
1128        *stmts = stmts_updated;
1129    }
1130}
1131
1132fn make_ref_idx_expr(ref_ident: &Ident, i: usize) -> MemberExpr {
1133    ref_ident.clone().computed_member(i as f64)
1134}
1135
1136fn make_ref_ident(c: Config, decls: &mut Vec<VarDeclarator>, init: Option<Box<Expr>>) -> Ident {
1137    make_ref_ident_for_array(c, decls, init, None)
1138}
1139
1140#[tracing::instrument(level = "info", skip_all)]
1141fn make_ref_ident_for_array(
1142    c: Config,
1143    decls: &mut Vec<VarDeclarator>,
1144    mut init: Option<Box<Expr>>,
1145    elem_cnt: Option<usize>,
1146) -> Ident {
1147    if elem_cnt.is_none() {
1148        if let Some(e) = init {
1149            match *e {
1150                Expr::Ident(i) => return i,
1151                _ => init = Some(e),
1152            }
1153        }
1154    }
1155
1156    let span = init.span();
1157
1158    let (ref_ident, aliased) = if c.loose {
1159        if let Some(ref init) = init {
1160            alias_if_required(init, "ref")
1161        } else {
1162            (private_ident!(span, "ref"), true)
1163        }
1164    } else if let Some(ref init) = init {
1165        (alias_ident_for(init, "ref"), true)
1166    } else {
1167        (private_ident!(span, "ref"), true)
1168    };
1169
1170    if aliased {
1171        decls.push(VarDeclarator {
1172            span,
1173            name: ref_ident.clone().into(),
1174            init: init.map(|v| {
1175                if c.loose || matches!(*v, Expr::Array(..)) {
1176                    v
1177                } else {
1178                    match elem_cnt {
1179                        None => v,
1180                        Some(std::usize::MAX) => Box::new(
1181                            CallExpr {
1182                                span: DUMMY_SP,
1183                                callee: helper!(to_array),
1184                                args: vec![v.as_arg()],
1185                                ..Default::default()
1186                            }
1187                            .into(),
1188                        ),
1189                        Some(value) => Box::new(
1190                            CallExpr {
1191                                span: DUMMY_SP,
1192                                callee: helper!(sliced_to_array),
1193                                args: vec![v.as_arg(), value.as_arg()],
1194                                ..Default::default()
1195                            }
1196                            .into(),
1197                        ),
1198                    }
1199                }
1200            }),
1201            definite: false,
1202        });
1203    }
1204
1205    ref_ident
1206}
1207
1208fn make_ref_prop_expr(ref_ident: &Ident, prop: Box<Expr>, mut computed: bool) -> Expr {
1209    computed |= !matches!(*prop, Expr::Ident(..));
1210
1211    MemberExpr {
1212        span: DUMMY_SP,
1213        obj: Box::new(ref_ident.clone().into()),
1214        prop: if computed {
1215            MemberProp::Computed(ComputedPropName {
1216                span: DUMMY_SP,
1217                expr: prop,
1218            })
1219        } else {
1220            MemberProp::Ident(prop.ident().unwrap().into())
1221        },
1222    }
1223    .into()
1224}
1225
1226/// Creates `tmp === void 0 ? def_value : tmp`
1227fn make_cond_expr(tmp: Ident, def_value: Box<Expr>) -> Expr {
1228    CondExpr {
1229        span: DUMMY_SP,
1230        test: BinExpr {
1231            span: DUMMY_SP,
1232            left: Box::new(Expr::Ident(tmp.clone())),
1233            op: op!("==="),
1234            right: Box::new(Expr::Unary(UnaryExpr {
1235                span: DUMMY_SP,
1236                op: op!("void"),
1237                arg: 0.0.into(),
1238            })),
1239        }
1240        .into(),
1241        cons: def_value,
1242        alt: tmp.into(),
1243    }
1244    .into()
1245}
1246
1247fn can_be_null(e: &Expr) -> bool {
1248    match *e {
1249        Expr::Lit(Lit::Null(..))
1250        | Expr::This(..)
1251        | Expr::Ident(..)
1252        | Expr::PrivateName(..)
1253        | Expr::Member(..)
1254        | Expr::SuperProp(..)
1255        | Expr::Call(..)
1256        | Expr::OptChain(..)
1257        | Expr::New(..)
1258        | Expr::Yield(..)
1259        | Expr::Await(..)
1260        | Expr::MetaProp(..) => true,
1261
1262        Expr::Lit(..) => false,
1263
1264        Expr::Array(..)
1265        | Expr::Arrow(..)
1266        | Expr::Object(..)
1267        | Expr::Fn(..)
1268        | Expr::Class(..)
1269        | Expr::Tpl(..) => false,
1270
1271        Expr::TaggedTpl(..) => true,
1272
1273        Expr::Paren(ParenExpr { ref expr, .. }) => can_be_null(expr),
1274        Expr::Seq(SeqExpr { ref exprs, .. }) => {
1275            exprs.last().map(|e| can_be_null(e)).unwrap_or(true)
1276        }
1277        Expr::Assign(AssignExpr { ref right, .. }) => can_be_null(right),
1278        Expr::Cond(CondExpr {
1279            ref cons, ref alt, ..
1280        }) => can_be_null(cons) || can_be_null(alt),
1281
1282        Expr::Unary(..) | Expr::Update(..) | Expr::Bin(..) => true,
1283
1284        Expr::JSXMember(..)
1285        | Expr::JSXNamespacedName(..)
1286        | Expr::JSXEmpty(..)
1287        | Expr::JSXElement(..)
1288        | Expr::JSXFragment(..) => unreachable!("destructuring jsx"),
1289
1290        Expr::TsNonNull(..) => false,
1291        Expr::TsAs(TsAsExpr { ref expr, .. })
1292        | Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
1293        | Expr::TsConstAssertion(TsConstAssertion { ref expr, .. })
1294        | Expr::TsInstantiation(TsInstantiation { ref expr, .. })
1295        | Expr::TsSatisfies(TsSatisfiesExpr { ref expr, .. }) => can_be_null(expr),
1296
1297        Expr::Invalid(..) => unreachable!(),
1298    }
1299}
1300
1301#[derive(Default)]
1302struct DestructuringVisitor {
1303    found: bool,
1304}
1305
1306impl Visit for DestructuringVisitor {
1307    noop_visit_type!(fail);
1308
1309    fn visit_assign_target_pat(&mut self, _: &AssignTargetPat) {
1310        self.found = true;
1311    }
1312
1313    fn visit_pat(&mut self, node: &Pat) {
1314        node.visit_children_with(self);
1315        match *node {
1316            Pat::Ident(..) => {}
1317            _ => self.found = true,
1318        }
1319    }
1320}
1321
1322impl Check for DestructuringVisitor {
1323    fn should_handle(&self) -> bool {
1324        self.found
1325    }
1326}
1327
1328#[cfg(test)]
1329mod tests {
1330    use swc_ecma_transforms_testing::test;
1331
1332    use super::*;
1333
1334    test!(
1335        ::swc_ecma_parser::Syntax::default(),
1336        |_| destructuring(Default::default()),
1337        nested_for_of,
1338        r#"
1339            for (const [k1, v1] of Object.entries(o)){
1340                for (const [k2, v2] of Object.entries(o)){
1341                    console.log(k1, v1, k2, v2);
1342                }
1343            }        
1344        "#
1345    );
1346}