swc_ecma_compat_es2015/
for_of.rs

1use std::mem::take;
2
3use serde::Deserialize;
4use swc_atoms::atom;
5use swc_common::{util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
6use swc_ecma_ast::*;
7use swc_ecma_transforms_base::{
8    helper,
9    perf::{ParExplode, Parallel},
10};
11use swc_ecma_transforms_macros::parallel;
12use swc_ecma_utils::{
13    alias_if_required, member_expr, prepend_stmt, private_ident, quote_ident, ExprFactory,
14};
15use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
16use swc_trace_macro::swc_trace;
17
18/// `@babel/plugin-transform-for-of`
19///
20/// ## In
21///
22/// ```js
23/// for (var i of foo) {}
24/// ```
25///
26/// ## Out
27///
28/// ```js
29/// var _iteratorNormalCompletion = true;
30/// var _didIteratorError = false;
31/// var _iteratorError = undefined;
32///
33/// try {
34///   for (var _iterator = foo[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
35///     var i = _step.value;
36///   }
37/// } catch (err) {
38///   _didIteratorError = true;
39///   _iteratorError = err;
40/// } finally {
41///   try {
42///     if (!_iteratorNormalCompletion && _iterator.return != null) {
43///       _iterator.return();
44///     }
45///   } finally {
46///     if (_didIteratorError) {
47///       throw _iteratorError;
48///     }
49///   }
50/// }
51/// ```
52pub fn for_of(c: Config) -> impl Pass {
53    visit_mut_pass(ForOf {
54        c,
55        top_level_vars: Default::default(),
56    })
57}
58
59#[derive(Debug, Clone, Copy, Default, Deserialize)]
60#[serde(rename_all = "camelCase")]
61pub struct Config {
62    pub loose: bool,
63    pub assume_array: bool,
64}
65
66struct ForOf {
67    c: Config,
68
69    ///```js
70    /// var _iteratorNormalCompletion = true;
71    /// var _didIteratorError = false;
72    /// var _iteratorError = undefined;
73    /// ```
74    top_level_vars: Vec<VarDeclarator>,
75}
76
77#[swc_trace]
78impl ForOf {
79    fn fold_for_stmt(
80        &mut self,
81        label: Option<Ident>,
82        ForOfStmt {
83            span,
84            left,
85            right,
86            body,
87            ..
88        }: ForOfStmt,
89    ) -> Stmt {
90        if right.is_array() || (self.c.assume_array && !self.c.loose) {
91            // Convert to normal for loop if rhs is array
92            //
93            // babel's output:
94            //
95            //    for(var _i = 0, _t = t; _i < _t.length; _i++){
96            //        let o = _t[_i];
97            //        const t = o;
98            //    }
99
100            let (arr, aliased) = alias_if_required(&right, "_iter");
101
102            let i = private_ident!("_i");
103
104            let test = Some(
105                BinExpr {
106                    span: DUMMY_SP,
107                    left: Box::new(i.clone().into()),
108                    op: op!("<"),
109                    right: arr.clone().make_member(quote_ident!("length")).into(),
110                }
111                .into(),
112            );
113            let update = Some(
114                UpdateExpr {
115                    span: DUMMY_SP,
116                    prefix: false,
117                    op: op!("++"),
118                    arg: Box::new(i.clone().into()),
119                }
120                .into(),
121            );
122
123            let mut decls = Vec::with_capacity(2);
124            decls.push(VarDeclarator {
125                span: DUMMY_SP,
126                name: i.clone().into(),
127                init: Some(0.into()),
128                definite: false,
129            });
130
131            if aliased {
132                decls.push(VarDeclarator {
133                    span: DUMMY_SP,
134                    name: arr.clone().into(),
135                    init: Some(right),
136                    definite: false,
137                });
138            }
139
140            let mut body = match *body {
141                Stmt::Block(b) => b,
142                _ => BlockStmt {
143                    span: DUMMY_SP,
144                    stmts: vec![*body],
145                    ..Default::default()
146                },
147            };
148
149            match left {
150                ForHead::VarDecl(var) => {
151                    assert_eq!(
152                        var.decls.len(),
153                        1,
154                        "Variable declarator of for of loop cannot contain multiple entries"
155                    );
156                    prepend_stmt(
157                        &mut body.stmts,
158                        VarDecl {
159                            span: DUMMY_SP,
160                            kind: var.kind,
161                            declare: false,
162                            decls: vec![VarDeclarator {
163                                span: DUMMY_SP,
164                                name: var.decls.into_iter().next().unwrap().name,
165                                init: Some(arr.computed_member(i).into()),
166                                definite: false,
167                            }],
168                            ..Default::default()
169                        }
170                        .into(),
171                    )
172                }
173
174                ForHead::Pat(pat) => prepend_stmt(
175                    &mut body.stmts,
176                    AssignExpr {
177                        span: DUMMY_SP,
178                        left: pat.try_into().unwrap(),
179                        op: op!("="),
180                        right: arr.computed_member(i).into(),
181                    }
182                    .into_stmt(),
183                ),
184
185                ForHead::UsingDecl(..) => {
186                    unreachable!("using declaration must be removed by previous pass")
187                }
188
189                #[cfg(swc_ast_unknown)]
190                _ => panic!("unable to access unknown nodes"),
191            }
192
193            let stmt = ForStmt {
194                span,
195                init: Some(
196                    VarDecl {
197                        span: DUMMY_SP,
198                        kind: VarDeclKind::Let,
199                        declare: false,
200                        decls,
201                        ..Default::default()
202                    }
203                    .into(),
204                ),
205                test,
206                update,
207                body: Box::new(Stmt::Block(body)),
208            }
209            .into();
210
211            return match label {
212                Some(label) => LabeledStmt {
213                    span,
214                    label,
215                    body: Box::new(stmt),
216                }
217                .into(),
218                _ => stmt,
219            };
220        }
221
222        // Loose mode
223        if self.c.loose {
224            let iterator = private_ident!("_iterator");
225            let step = private_ident!("_step");
226
227            let decls = vec![
228                VarDeclarator {
229                    span: DUMMY_SP,
230                    name: iterator.clone().into(),
231                    init: Some(Box::new(Expr::Call(CallExpr {
232                        span: DUMMY_SP,
233                        callee: helper!(create_for_of_iterator_helper_loose),
234                        args: vec![right.as_arg()],
235                        ..Default::default()
236                    }))),
237                    definite: Default::default(),
238                },
239                VarDeclarator {
240                    span: DUMMY_SP,
241                    name: step.clone().into(),
242                    init: None,
243                    definite: Default::default(),
244                },
245            ];
246
247            let mut body = match *body {
248                Stmt::Block(b) => b,
249                _ => BlockStmt {
250                    stmts: vec![*body],
251                    ..Default::default()
252                },
253            };
254
255            match left {
256                ForHead::VarDecl(var) => {
257                    assert_eq!(
258                        var.decls.len(),
259                        1,
260                        "Variable declarator of for of loop cannot contain multiple entries"
261                    );
262                    prepend_stmt(
263                        &mut body.stmts,
264                        VarDecl {
265                            kind: var.kind,
266                            decls: vec![VarDeclarator {
267                                span: DUMMY_SP,
268                                name: var.decls.into_iter().next().unwrap().name,
269                                init: Some(step.clone().make_member(quote_ident!("value")).into()),
270                                definite: false,
271                            }],
272                            ..Default::default()
273                        }
274                        .into(),
275                    )
276                }
277
278                ForHead::Pat(pat) => prepend_stmt(
279                    &mut body.stmts,
280                    AssignExpr {
281                        span: DUMMY_SP,
282                        left: pat.try_into().unwrap(),
283                        op: op!("="),
284                        right: step.clone().make_member(quote_ident!("value")).into(),
285                    }
286                    .into_stmt(),
287                ),
288
289                ForHead::UsingDecl(..) => {
290                    unreachable!("using declaration must be removed by previous pass")
291                }
292
293                #[cfg(swc_ast_unknown)]
294                _ => panic!("unable to access unknown nodes"),
295            }
296
297            // !(_step = _iterator()).done;
298            let test = UnaryExpr {
299                span: DUMMY_SP,
300                op: op!("!"),
301                arg: AssignExpr {
302                    span: DUMMY_SP,
303                    op: op!("="),
304                    left: step.into(),
305                    right: CallExpr {
306                        span: DUMMY_SP,
307                        callee: iterator.as_callee(),
308                        args: Vec::new(),
309                        ..Default::default()
310                    }
311                    .into(),
312                }
313                .make_member(quote_ident!("done"))
314                .into(),
315            }
316            .into();
317
318            let stmt = ForStmt {
319                span,
320                init: Some(
321                    VarDecl {
322                        kind: VarDeclKind::Var,
323                        decls,
324                        ..Default::default()
325                    }
326                    .into(),
327                ),
328                test: Some(test),
329                update: None,
330                body: Box::new(Stmt::Block(body)),
331            }
332            .into();
333            return match label {
334                Some(label) => LabeledStmt {
335                    span,
336                    label,
337                    body: Box::new(stmt),
338                }
339                .into(),
340                _ => stmt,
341            };
342        }
343
344        let var_span = left.span();
345        let var_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
346
347        let mut body = match *body {
348            Stmt::Block(block) => block,
349            body => BlockStmt {
350                span: DUMMY_SP,
351                stmts: vec![body],
352                ..Default::default()
353            },
354        };
355
356        let step = quote_ident!(var_ctxt, var_span, "_step");
357        let step_value = step.clone().make_member(quote_ident!("value"));
358        body.stmts.insert(
359            0,
360            match left {
361                ForHead::VarDecl(mut var) => {
362                    assert_eq!(var.decls.len(), 1);
363                    VarDecl {
364                        span: var.span,
365                        kind: var.kind,
366                        decls: vec![VarDeclarator {
367                            init: Some(step_value.into()),
368                            ..var.decls.pop().unwrap()
369                        }],
370                        declare: false,
371                        ..Default::default()
372                    }
373                    .into()
374                }
375                ForHead::Pat(pat) => AssignExpr {
376                    span: DUMMY_SP,
377                    left: pat.try_into().unwrap(),
378                    op: op!("="),
379                    right: step_value.into(),
380                }
381                .into_stmt(),
382
383                ForHead::UsingDecl(..) => {
384                    unreachable!("using declaration must be removed by previous pass")
385                }
386                #[cfg(swc_ast_unknown)]
387                _ => panic!("unable to access unknown nodes"),
388            },
389        );
390
391        let iterator = quote_ident!(var_ctxt, var_span, "_iterator");
392        // `_iterator.return`
393        let iterator_return = iterator.clone().make_member(quote_ident!("return")).into();
394
395        let normal_completion_ident =
396            Ident::new(atom!("_iteratorNormalCompletion"), var_span, var_ctxt);
397        self.top_level_vars.push(VarDeclarator {
398            span: DUMMY_SP,
399            name: normal_completion_ident.clone().into(),
400            init: Some(true.into()),
401            definite: false,
402        });
403        let error_flag_ident = Ident::new(atom!("_didIteratorError"), var_span, var_ctxt);
404        self.top_level_vars.push(VarDeclarator {
405            span: DUMMY_SP,
406            name: error_flag_ident.clone().into(),
407            init: Some(false.into()),
408            definite: false,
409        });
410        let error_ident = Ident::new(atom!("_iteratorError"), var_span, var_ctxt);
411        self.top_level_vars.push(VarDeclarator {
412            span: DUMMY_SP,
413            name: error_ident.clone().into(),
414            init: Some(Ident::new_no_ctxt(atom!("undefined"), DUMMY_SP).into()),
415            definite: false,
416        });
417
418        let for_stmt = ForStmt {
419            span,
420            init: Some(
421                VarDecl {
422                    span: DUMMY_SP,
423                    kind: VarDeclKind::Var,
424                    declare: false,
425                    decls: vec![
426                        VarDeclarator {
427                            span: DUMMY_SP,
428                            name: iterator.clone().into(),
429                            init: Some(Box::new(Expr::Call(CallExpr {
430                                span: DUMMY_SP,
431                                callee: right
432                                    .computed_member(member_expr!(
433                                        Default::default(),
434                                        Default::default(),
435                                        Symbol.iterator
436                                    ))
437                                    .as_callee(),
438                                args: Vec::new(),
439                                ..Default::default()
440                            }))),
441                            definite: false,
442                        },
443                        VarDeclarator {
444                            span: DUMMY_SP,
445                            name: step.clone().into(),
446                            init: None,
447                            definite: false,
448                        },
449                    ],
450                    ..Default::default()
451                }
452                .into(),
453            ),
454            // !(_iteratorNormalCompletion = (_step = _iterator.next()).done)
455            test: Some(
456                UnaryExpr {
457                    span: DUMMY_SP,
458                    op: op!("!"),
459                    arg: {
460                        let step_expr: Expr = AssignExpr {
461                            span: DUMMY_SP,
462                            left: step.into(),
463                            op: op!("="),
464                            // `_iterator.next()`
465                            right: Box::new(Expr::Call(CallExpr {
466                                span: DUMMY_SP,
467                                // `_iterator.next`
468                                callee: iterator.make_member(quote_ident!("next")).as_callee(),
469                                args: Vec::new(),
470                                ..Default::default()
471                            })),
472                        }
473                        .into();
474
475                        Box::new(
476                            AssignExpr {
477                                span: DUMMY_SP,
478                                left: normal_completion_ident.clone().into(),
479                                op: op!("="),
480                                right: step_expr.make_member(quote_ident!("done")).into(),
481                            }
482                            .into(),
483                        )
484                    },
485                }
486                .into(),
487            ),
488
489            // `_iteratorNormalCompletion = true`
490            update: Some(
491                AssignExpr {
492                    span: DUMMY_SP,
493                    left: normal_completion_ident.clone().into(),
494                    op: op!("="),
495                    right: true.into(),
496                }
497                .into(),
498            ),
499            body: Box::new(body.into()),
500        }
501        .into();
502
503        let for_stmt = match label {
504            Some(label) => LabeledStmt {
505                span,
506                label,
507                body: Box::new(for_stmt),
508            }
509            .into(),
510            None => for_stmt,
511        };
512
513        TryStmt {
514            span: DUMMY_SP,
515            block: BlockStmt {
516                span: DUMMY_SP,
517                stmts: vec![for_stmt],
518                ..Default::default()
519            },
520            handler: Some(CatchClause {
521                span: DUMMY_SP,
522                param: Some(quote_ident!("err").into()),
523                // _didIteratorError = true;
524                // _iteratorError = err;
525                body: BlockStmt {
526                    stmts: vec![
527                        // _didIteratorError = true;
528                        AssignExpr {
529                            span: DUMMY_SP,
530                            left: error_flag_ident.clone().into(),
531                            op: op!("="),
532                            right: true.into(),
533                        }
534                        .into_stmt(),
535                        // _iteratorError = err;
536                        AssignExpr {
537                            span: DUMMY_SP,
538                            left: error_ident.clone().into(),
539                            op: op!("="),
540                            right: Box::new(Expr::Ident(quote_ident!("err").into())),
541                        }
542                        .into_stmt(),
543                    ],
544                    ..Default::default()
545                },
546            }),
547            finalizer: Some(BlockStmt {
548                stmts: vec![make_finally_block(
549                    iterator_return,
550                    &normal_completion_ident,
551                    error_flag_ident,
552                    error_ident,
553                )],
554                ..Default::default()
555            }),
556        }
557        .into()
558    }
559}
560
561/// ```js
562///   try {
563///     if (!_iteratorNormalCompletion && _iterator.return != null) {
564///       _iterator.return();
565///     }
566///   } finally {
567///     if (_didIteratorError) {
568///       throw _iteratorError;
569///     }
570///   }
571/// ```
572#[tracing::instrument(level = "debug", skip_all)]
573fn make_finally_block(
574    iterator_return: Box<Expr>,
575    normal_completion_ident: &Ident,
576    error_flag_ident: Ident,
577    error_ident: Ident,
578) -> Stmt {
579    TryStmt {
580        span: DUMMY_SP,
581        block: BlockStmt {
582            span: DUMMY_SP,
583            stmts: vec![
584                // if (!_iteratorNormalCompletion && _iterator.return !=
585                // null) {
586                //   _iterator.return();
587                // }
588                Stmt::If(IfStmt {
589                    span: DUMMY_SP,
590                    test: Box::new(Expr::Bin(BinExpr {
591                        span: DUMMY_SP,
592                        left: Box::new(Expr::Unary(UnaryExpr {
593                            span: DUMMY_SP,
594                            op: op!("!"),
595                            arg: Box::new(Expr::Ident(normal_completion_ident.clone())),
596                        })),
597                        op: op!("&&"),
598                        right: Box::new(Expr::Bin(BinExpr {
599                            span: DUMMY_SP,
600                            left: iterator_return.clone(),
601                            op: op!("!="),
602                            right: Null { span: DUMMY_SP }.into(),
603                        })),
604                    })),
605                    cons: Box::new(Stmt::Block(BlockStmt {
606                        span: DUMMY_SP,
607                        stmts: vec![CallExpr {
608                            span: DUMMY_SP,
609                            callee: iterator_return.as_callee(),
610                            args: Vec::new(),
611                            ..Default::default()
612                        }
613                        .into_stmt()],
614                        ..Default::default()
615                    })),
616                    alt: None,
617                }),
618            ],
619            ..Default::default()
620        },
621        handler: None,
622        finalizer: Some(BlockStmt {
623            stmts: vec![
624                // if (_didIteratorError) {
625                //   throw _iteratorError;
626                // }
627                Stmt::If(IfStmt {
628                    span: DUMMY_SP,
629                    test: Box::new(Expr::Ident(error_flag_ident)),
630                    cons: Box::new(Stmt::Block(BlockStmt {
631                        stmts: vec![Stmt::Throw(ThrowStmt {
632                            span: DUMMY_SP,
633                            arg: Box::new(Expr::Ident(error_ident)),
634                        })],
635                        ..Default::default()
636                    })),
637                    alt: None,
638                }),
639            ],
640            ..Default::default()
641        }),
642    }
643    .into()
644}
645
646impl Parallel for ForOf {
647    fn create(&self) -> Self {
648        ForOf {
649            c: self.c,
650            top_level_vars: Default::default(),
651        }
652    }
653
654    fn merge(&mut self, other: Self) {
655        self.top_level_vars.extend(other.top_level_vars);
656    }
657}
658
659#[swc_trace]
660impl ParExplode for ForOf {
661    fn after_one_stmt(&mut self, stmts: &mut Vec<Stmt>) {
662        // Add variable declaration
663        // e.g. var ref
664        if !self.top_level_vars.is_empty() {
665            stmts.push(
666                VarDecl {
667                    span: DUMMY_SP,
668                    kind: VarDeclKind::Var,
669                    decls: take(&mut self.top_level_vars),
670                    declare: false,
671                    ..Default::default()
672                }
673                .into(),
674            );
675        }
676    }
677
678    fn after_one_module_item(&mut self, stmts: &mut Vec<ModuleItem>) {
679        // Add variable declaration
680        // e.g. var ref
681        if !self.top_level_vars.is_empty() {
682            stmts.push(
683                VarDecl {
684                    span: DUMMY_SP,
685                    kind: VarDeclKind::Var,
686                    decls: take(&mut self.top_level_vars),
687                    declare: false,
688                    ..Default::default()
689                }
690                .into(),
691            );
692        }
693    }
694}
695
696#[swc_trace]
697#[parallel(explode)]
698impl VisitMut for ForOf {
699    noop_visit_mut_type!(fail);
700
701    fn visit_mut_stmt(&mut self, s: &mut Stmt) {
702        match s {
703            Stmt::Labeled(LabeledStmt { label, body, .. }) => {
704                // Handle label
705                match &mut **body {
706                    Stmt::ForOf(stmt) => {
707                        stmt.visit_mut_children_with(self);
708
709                        *s = self.fold_for_stmt(Some(label.clone()), stmt.take());
710                    }
711                    _ => {
712                        body.visit_mut_with(self);
713                    }
714                }
715            }
716            Stmt::ForOf(stmt) => {
717                stmt.visit_mut_children_with(self);
718
719                *s = self.fold_for_stmt(None, stmt.take())
720            }
721            _ => s.visit_mut_children_with(self),
722        }
723    }
724}