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
18pub 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
1237fn 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}