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
18pub 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 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 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
190 let stmt = ForStmt {
191 span,
192 init: Some(
193 VarDecl {
194 span: DUMMY_SP,
195 kind: VarDeclKind::Let,
196 declare: false,
197 decls,
198 ..Default::default()
199 }
200 .into(),
201 ),
202 test,
203 update,
204 body: Box::new(Stmt::Block(body)),
205 }
206 .into();
207
208 return match label {
209 Some(label) => LabeledStmt {
210 span,
211 label,
212 body: Box::new(stmt),
213 }
214 .into(),
215 _ => stmt,
216 };
217 }
218
219 if self.c.loose {
221 let iterator = private_ident!("_iterator");
222 let step = private_ident!("_step");
223
224 let decls = vec![
225 VarDeclarator {
226 span: DUMMY_SP,
227 name: iterator.clone().into(),
228 init: Some(Box::new(Expr::Call(CallExpr {
229 span: DUMMY_SP,
230 callee: helper!(create_for_of_iterator_helper_loose),
231 args: vec![right.as_arg()],
232 ..Default::default()
233 }))),
234 definite: Default::default(),
235 },
236 VarDeclarator {
237 span: DUMMY_SP,
238 name: step.clone().into(),
239 init: None,
240 definite: Default::default(),
241 },
242 ];
243
244 let mut body = match *body {
245 Stmt::Block(b) => b,
246 _ => BlockStmt {
247 stmts: vec![*body],
248 ..Default::default()
249 },
250 };
251
252 match left {
253 ForHead::VarDecl(var) => {
254 assert_eq!(
255 var.decls.len(),
256 1,
257 "Variable declarator of for of loop cannot contain multiple entries"
258 );
259 prepend_stmt(
260 &mut body.stmts,
261 VarDecl {
262 kind: var.kind,
263 decls: vec![VarDeclarator {
264 span: DUMMY_SP,
265 name: var.decls.into_iter().next().unwrap().name,
266 init: Some(step.clone().make_member(quote_ident!("value")).into()),
267 definite: false,
268 }],
269 ..Default::default()
270 }
271 .into(),
272 )
273 }
274
275 ForHead::Pat(pat) => prepend_stmt(
276 &mut body.stmts,
277 AssignExpr {
278 span: DUMMY_SP,
279 left: pat.try_into().unwrap(),
280 op: op!("="),
281 right: step.clone().make_member(quote_ident!("value")).into(),
282 }
283 .into_stmt(),
284 ),
285
286 ForHead::UsingDecl(..) => {
287 unreachable!("using declaration must be removed by previous pass")
288 }
289 }
290
291 let test = UnaryExpr {
293 span: DUMMY_SP,
294 op: op!("!"),
295 arg: AssignExpr {
296 span: DUMMY_SP,
297 op: op!("="),
298 left: step.into(),
299 right: CallExpr {
300 span: DUMMY_SP,
301 callee: iterator.as_callee(),
302 args: Vec::new(),
303 ..Default::default()
304 }
305 .into(),
306 }
307 .make_member(quote_ident!("done"))
308 .into(),
309 }
310 .into();
311
312 let stmt = ForStmt {
313 span,
314 init: Some(
315 VarDecl {
316 kind: VarDeclKind::Var,
317 decls,
318 ..Default::default()
319 }
320 .into(),
321 ),
322 test: Some(test),
323 update: None,
324 body: Box::new(Stmt::Block(body)),
325 }
326 .into();
327 return match label {
328 Some(label) => LabeledStmt {
329 span,
330 label,
331 body: Box::new(stmt),
332 }
333 .into(),
334 _ => stmt,
335 };
336 }
337
338 let var_span = left.span();
339 let var_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root()));
340
341 let mut body = match *body {
342 Stmt::Block(block) => block,
343 body => BlockStmt {
344 span: DUMMY_SP,
345 stmts: vec![body],
346 ..Default::default()
347 },
348 };
349
350 let step = quote_ident!(var_ctxt, var_span, "_step");
351 let step_value = step.clone().make_member(quote_ident!("value"));
352 body.stmts.insert(
353 0,
354 match left {
355 ForHead::VarDecl(mut var) => {
356 assert_eq!(var.decls.len(), 1);
357 VarDecl {
358 span: var.span,
359 kind: var.kind,
360 decls: vec![VarDeclarator {
361 init: Some(step_value.into()),
362 ..var.decls.pop().unwrap()
363 }],
364 declare: false,
365 ..Default::default()
366 }
367 .into()
368 }
369 ForHead::Pat(pat) => AssignExpr {
370 span: DUMMY_SP,
371 left: pat.try_into().unwrap(),
372 op: op!("="),
373 right: step_value.into(),
374 }
375 .into_stmt(),
376
377 ForHead::UsingDecl(..) => {
378 unreachable!("using declaration must be removed by previous pass")
379 }
380 },
381 );
382
383 let iterator = quote_ident!(var_ctxt, var_span, "_iterator");
384 let iterator_return = iterator.clone().make_member(quote_ident!("return")).into();
386
387 let normal_completion_ident =
388 Ident::new(atom!("_iteratorNormalCompletion"), var_span, var_ctxt);
389 self.top_level_vars.push(VarDeclarator {
390 span: DUMMY_SP,
391 name: normal_completion_ident.clone().into(),
392 init: Some(true.into()),
393 definite: false,
394 });
395 let error_flag_ident = Ident::new(atom!("_didIteratorError"), var_span, var_ctxt);
396 self.top_level_vars.push(VarDeclarator {
397 span: DUMMY_SP,
398 name: error_flag_ident.clone().into(),
399 init: Some(false.into()),
400 definite: false,
401 });
402 let error_ident = Ident::new(atom!("_iteratorError"), var_span, var_ctxt);
403 self.top_level_vars.push(VarDeclarator {
404 span: DUMMY_SP,
405 name: error_ident.clone().into(),
406 init: Some(Ident::new_no_ctxt(atom!("undefined"), DUMMY_SP).into()),
407 definite: false,
408 });
409
410 let for_stmt = ForStmt {
411 span,
412 init: Some(
413 VarDecl {
414 span: DUMMY_SP,
415 kind: VarDeclKind::Var,
416 declare: false,
417 decls: vec![
418 VarDeclarator {
419 span: DUMMY_SP,
420 name: iterator.clone().into(),
421 init: Some(Box::new(Expr::Call(CallExpr {
422 span: DUMMY_SP,
423 callee: right
424 .computed_member(member_expr!(
425 Default::default(),
426 Default::default(),
427 Symbol.iterator
428 ))
429 .as_callee(),
430 args: Vec::new(),
431 ..Default::default()
432 }))),
433 definite: false,
434 },
435 VarDeclarator {
436 span: DUMMY_SP,
437 name: step.clone().into(),
438 init: None,
439 definite: false,
440 },
441 ],
442 ..Default::default()
443 }
444 .into(),
445 ),
446 test: Some(
448 UnaryExpr {
449 span: DUMMY_SP,
450 op: op!("!"),
451 arg: {
452 let step_expr: Expr = AssignExpr {
453 span: DUMMY_SP,
454 left: step.into(),
455 op: op!("="),
456 right: Box::new(Expr::Call(CallExpr {
458 span: DUMMY_SP,
459 callee: iterator.make_member(quote_ident!("next")).as_callee(),
461 args: Vec::new(),
462 ..Default::default()
463 })),
464 }
465 .into();
466
467 Box::new(
468 AssignExpr {
469 span: DUMMY_SP,
470 left: normal_completion_ident.clone().into(),
471 op: op!("="),
472 right: step_expr.make_member(quote_ident!("done")).into(),
473 }
474 .into(),
475 )
476 },
477 }
478 .into(),
479 ),
480
481 update: Some(
483 AssignExpr {
484 span: DUMMY_SP,
485 left: normal_completion_ident.clone().into(),
486 op: op!("="),
487 right: true.into(),
488 }
489 .into(),
490 ),
491 body: Box::new(body.into()),
492 }
493 .into();
494
495 let for_stmt = match label {
496 Some(label) => LabeledStmt {
497 span,
498 label,
499 body: Box::new(for_stmt),
500 }
501 .into(),
502 None => for_stmt,
503 };
504
505 TryStmt {
506 span: DUMMY_SP,
507 block: BlockStmt {
508 span: DUMMY_SP,
509 stmts: vec![for_stmt],
510 ..Default::default()
511 },
512 handler: Some(CatchClause {
513 span: DUMMY_SP,
514 param: Some(quote_ident!("err").into()),
515 body: BlockStmt {
518 stmts: vec![
519 AssignExpr {
521 span: DUMMY_SP,
522 left: error_flag_ident.clone().into(),
523 op: op!("="),
524 right: true.into(),
525 }
526 .into_stmt(),
527 AssignExpr {
529 span: DUMMY_SP,
530 left: error_ident.clone().into(),
531 op: op!("="),
532 right: Box::new(Expr::Ident(quote_ident!("err").into())),
533 }
534 .into_stmt(),
535 ],
536 ..Default::default()
537 },
538 }),
539 finalizer: Some(BlockStmt {
540 stmts: vec![make_finally_block(
541 iterator_return,
542 &normal_completion_ident,
543 error_flag_ident,
544 error_ident,
545 )],
546 ..Default::default()
547 }),
548 }
549 .into()
550 }
551}
552
553#[tracing::instrument(level = "debug", skip_all)]
565fn make_finally_block(
566 iterator_return: Box<Expr>,
567 normal_completion_ident: &Ident,
568 error_flag_ident: Ident,
569 error_ident: Ident,
570) -> Stmt {
571 TryStmt {
572 span: DUMMY_SP,
573 block: BlockStmt {
574 span: DUMMY_SP,
575 stmts: vec![
576 Stmt::If(IfStmt {
581 span: DUMMY_SP,
582 test: Box::new(Expr::Bin(BinExpr {
583 span: DUMMY_SP,
584 left: Box::new(Expr::Unary(UnaryExpr {
585 span: DUMMY_SP,
586 op: op!("!"),
587 arg: Box::new(Expr::Ident(normal_completion_ident.clone())),
588 })),
589 op: op!("&&"),
590 right: Box::new(Expr::Bin(BinExpr {
591 span: DUMMY_SP,
592 left: iterator_return.clone(),
593 op: op!("!="),
594 right: Null { span: DUMMY_SP }.into(),
595 })),
596 })),
597 cons: Box::new(Stmt::Block(BlockStmt {
598 span: DUMMY_SP,
599 stmts: vec![CallExpr {
600 span: DUMMY_SP,
601 callee: iterator_return.as_callee(),
602 args: Vec::new(),
603 ..Default::default()
604 }
605 .into_stmt()],
606 ..Default::default()
607 })),
608 alt: None,
609 }),
610 ],
611 ..Default::default()
612 },
613 handler: None,
614 finalizer: Some(BlockStmt {
615 stmts: vec![
616 Stmt::If(IfStmt {
620 span: DUMMY_SP,
621 test: Box::new(Expr::Ident(error_flag_ident)),
622 cons: Box::new(Stmt::Block(BlockStmt {
623 stmts: vec![Stmt::Throw(ThrowStmt {
624 span: DUMMY_SP,
625 arg: Box::new(Expr::Ident(error_ident)),
626 })],
627 ..Default::default()
628 })),
629 alt: None,
630 }),
631 ],
632 ..Default::default()
633 }),
634 }
635 .into()
636}
637
638impl Parallel for ForOf {
639 fn create(&self) -> Self {
640 ForOf {
641 c: self.c,
642 top_level_vars: Default::default(),
643 }
644 }
645
646 fn merge(&mut self, other: Self) {
647 self.top_level_vars.extend(other.top_level_vars);
648 }
649}
650
651#[swc_trace]
652impl ParExplode for ForOf {
653 fn after_one_stmt(&mut self, stmts: &mut Vec<Stmt>) {
654 if !self.top_level_vars.is_empty() {
657 stmts.push(
658 VarDecl {
659 span: DUMMY_SP,
660 kind: VarDeclKind::Var,
661 decls: take(&mut self.top_level_vars),
662 declare: false,
663 ..Default::default()
664 }
665 .into(),
666 );
667 }
668 }
669
670 fn after_one_module_item(&mut self, stmts: &mut Vec<ModuleItem>) {
671 if !self.top_level_vars.is_empty() {
674 stmts.push(
675 VarDecl {
676 span: DUMMY_SP,
677 kind: VarDeclKind::Var,
678 decls: take(&mut self.top_level_vars),
679 declare: false,
680 ..Default::default()
681 }
682 .into(),
683 );
684 }
685 }
686}
687
688#[swc_trace]
689#[parallel(explode)]
690impl VisitMut for ForOf {
691 noop_visit_mut_type!(fail);
692
693 fn visit_mut_stmt(&mut self, s: &mut Stmt) {
694 match s {
695 Stmt::Labeled(LabeledStmt { label, body, .. }) => {
696 match &mut **body {
698 Stmt::ForOf(stmt) => {
699 stmt.visit_mut_children_with(self);
700
701 *s = self.fold_for_stmt(Some(label.clone()), stmt.take());
702 }
703 _ => {
704 body.visit_mut_with(self);
705 }
706 }
707 }
708 Stmt::ForOf(stmt) => {
709 stmt.visit_mut_children_with(self);
710
711 *s = self.fold_for_stmt(None, stmt.take())
712 }
713 _ => s.visit_mut_children_with(self),
714 }
715 }
716}