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