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