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
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
1226fn 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}