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 #[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 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 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 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 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 right: Box::new(Expr::Call(CallExpr {
466 span: DUMMY_SP,
467 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 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 body: BlockStmt {
526 stmts: vec![
527 AssignExpr {
529 span: DUMMY_SP,
530 left: error_flag_ident.clone().into(),
531 op: op!("="),
532 right: true.into(),
533 }
534 .into_stmt(),
535 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#[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 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 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 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 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 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}