1use std::{mem, vec};
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, helper_expr,
8 perf::{should_work, Check},
9};
10use swc_ecma_utils::{
11 function::{init_this, FnEnvHoister},
12 prepend_stmt, private_ident, quote_ident, ExprFactory,
13};
14use swc_ecma_visit::{
15 noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith, VisitWith,
16};
17use swc_trace_macro::swc_trace;
18
19pub fn async_to_generator(c: Config, unresolved_mark: Mark) -> impl Pass {
40 visit_mut_pass(AsyncToGenerator {
41 c,
42 fn_state: None,
43 in_subclass: false,
44 unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
45 })
46}
47
48#[derive(Debug, Clone, Copy, Default, Deserialize)]
49#[serde(rename_all = "camelCase")]
50pub struct Config {
51 #[serde(default)]
52 pub ignore_function_length: bool,
53}
54
55#[derive(Default, Clone, Debug)]
56struct FnState {
57 is_async: bool,
58 is_generator: bool,
59 use_this: bool,
60 use_arguments: bool,
61 use_super: bool,
62}
63
64#[derive(Default, Clone)]
65struct AsyncToGenerator {
66 c: Config,
67
68 fn_state: Option<FnState>,
69
70 in_subclass: bool,
71
72 unresolved_ctxt: SyntaxContext,
73}
74
75#[swc_trace]
76impl VisitMut for AsyncToGenerator {
77 noop_visit_mut_type!(fail);
78
79 fn visit_mut_function(&mut self, function: &mut Function) {
80 let Some(body) = &mut function.body else {
81 return;
82 };
83
84 function.params.visit_mut_with(self);
85
86 let fn_state = self.fn_state.replace(FnState {
87 is_async: function.is_async,
88 is_generator: function.is_generator,
89 ..Default::default()
90 });
91 body.visit_mut_with(self);
92
93 let mut fn_state = mem::replace(&mut self.fn_state, fn_state).unwrap();
94 if !fn_state.is_async {
95 return;
96 }
97
98 let mut stmts = vec![];
99 if fn_state.use_super {
100 let mut fn_env_hoister = FnEnvHoister::new(self.unresolved_ctxt);
102 fn_env_hoister.disable_this();
103 fn_env_hoister.disable_arguments();
104
105 body.visit_mut_with(&mut fn_env_hoister);
106
107 stmts.extend(fn_env_hoister.to_stmt());
108 }
109
110 function.is_async = false;
111 function.is_generator = false;
112 if !fn_state.use_arguments {
113 fn_state.use_arguments =
117 could_potentially_throw(&function.params, self.unresolved_ctxt);
118 }
119
120 let params = if fn_state.use_arguments {
121 let mut params = vec![];
122
123 if !self.c.ignore_function_length {
124 let fn_len = function
125 .params
126 .iter()
127 .filter(|p| !matches!(p.pat, Pat::Assign(..) | Pat::Rest(..)))
128 .count();
129 for i in 0..fn_len {
130 params.push(Param {
131 pat: private_ident!(format!("_{}", i)).into(),
132 span: DUMMY_SP,
133 decorators: vec![],
134 });
135 }
136 }
137
138 mem::replace(&mut function.params, params)
139 } else {
140 vec![]
141 };
142
143 let expr = make_fn_ref(&fn_state, params, body.take());
144
145 stmts.push(
146 ReturnStmt {
147 arg: Some(expr.into()),
148 ..Default::default()
149 }
150 .into(),
151 );
152
153 function.body = Some(BlockStmt {
154 stmts,
155 ..Default::default()
156 });
157 }
158
159 fn visit_mut_arrow_expr(&mut self, arrow_expr: &mut ArrowExpr) {
160 if !arrow_expr.is_async {
161 arrow_expr.visit_mut_children_with(self);
162 return;
163 }
164
165 debug_assert!(!arrow_expr.is_generator);
167
168 arrow_expr.params.visit_mut_with(self);
169
170 let fn_state = self.fn_state.replace(FnState {
171 is_async: true,
172 is_generator: false,
173 ..Default::default()
174 });
175
176 arrow_expr.body.visit_mut_with(self);
177 let fn_state = mem::replace(&mut self.fn_state, fn_state).unwrap();
178
179 if let Some(out_fn_state) = &mut self.fn_state {
181 out_fn_state.use_this |= fn_state.use_this;
182 out_fn_state.use_arguments |= fn_state.use_arguments;
183 out_fn_state.use_super |= fn_state.use_super;
184 }
185
186 let mut stmts = vec![];
187 if fn_state.use_super {
188 let mut fn_env_hoister = FnEnvHoister::new(self.unresolved_ctxt);
190 fn_env_hoister.disable_this();
191 fn_env_hoister.disable_arguments();
192
193 arrow_expr.body.visit_mut_with(&mut fn_env_hoister);
194
195 stmts.extend(fn_env_hoister.to_stmt());
196 }
197
198 arrow_expr.is_async = false;
199
200 let body = match *arrow_expr.body.take() {
201 BlockStmtOrExpr::BlockStmt(block_stmt) => block_stmt,
202 BlockStmtOrExpr::Expr(expr) => BlockStmt {
203 stmts: vec![ReturnStmt {
204 arg: Some(expr),
205 ..Default::default()
206 }
207 .into()],
208 ..Default::default()
209 },
210 };
211
212 let expr = make_fn_ref(&fn_state, vec![], body);
213
214 arrow_expr.body = if fn_state.use_super {
215 stmts.push(expr.into_stmt());
216 BlockStmtOrExpr::BlockStmt(BlockStmt {
217 stmts,
218 ..Default::default()
219 })
220 } else {
221 BlockStmtOrExpr::Expr(Box::new(expr))
222 }
223 .into()
224 }
225
226 fn visit_mut_class(&mut self, class: &mut Class) {
227 class.super_class.visit_mut_with(self);
228 let in_subclass = mem::replace(&mut self.in_subclass, class.super_class.is_some());
229 class.body.visit_mut_with(self);
230 self.in_subclass = in_subclass;
231 }
232
233 fn visit_mut_constructor(&mut self, constructor: &mut Constructor) {
234 constructor.params.visit_mut_with(self);
235
236 if let Some(BlockStmt { stmts, .. }) = &mut constructor.body {
237 if !should_work::<ShouldWork, _>(&*stmts) {
238 return;
239 }
240
241 let (decl, this_id) = if self.in_subclass {
242 let mut fn_env_hoister = FnEnvHoister::new(self.unresolved_ctxt);
243 stmts.visit_mut_with(&mut fn_env_hoister);
244 fn_env_hoister.to_stmt_in_subclass()
245 } else {
246 (None, None)
247 };
248
249 stmts.visit_mut_children_with(self);
250
251 if let Some(this_id) = this_id {
252 init_this(stmts, &this_id)
253 }
254
255 if let Some(decl) = decl {
256 prepend_stmt(stmts, decl)
257 }
258 }
259 }
260
261 fn visit_mut_getter_prop(&mut self, f: &mut GetterProp) {
262 let fn_state = self.fn_state.take();
263 f.visit_mut_children_with(self);
264 self.fn_state = fn_state;
265 }
266
267 fn visit_mut_setter_prop(&mut self, f: &mut SetterProp) {
268 f.param.visit_mut_with(self);
269 let fn_state = self.fn_state.take();
270 f.body.visit_mut_with(self);
271 self.fn_state = fn_state;
272 }
273
274 fn visit_mut_exprs(&mut self, exprs: &mut Vec<Box<Expr>>) {
275 if self.fn_state.as_ref().is_some_and(|f| !f.is_async)
276 && !should_work::<ShouldWork, _>(&*exprs)
277 {
278 return;
279 }
280
281 exprs.visit_mut_children_with(self);
282 }
283
284 fn visit_mut_expr(&mut self, expr: &mut Expr) {
285 if self.fn_state.as_ref().is_some_and(|f| !f.is_async)
286 && !should_work::<ShouldWork, _>(&*expr)
287 {
288 return;
289 }
290
291 expr.visit_mut_children_with(self);
292
293 let Some(fn_state @ FnState { is_async: true, .. }) = &mut self.fn_state else {
294 return;
295 };
296
297 match expr {
298 Expr::This(..) => {
299 fn_state.use_this = true;
300 }
301 Expr::Ident(Ident { sym, .. }) if sym == "arguments" => {
302 fn_state.use_arguments = true;
303 }
304 Expr::Await(AwaitExpr { arg, span }) => {
305 *expr = if fn_state.is_generator {
306 let callee = helper!(await_async_generator);
307 let arg = CallExpr {
308 span: *span,
309 callee,
310 args: vec![arg.take().as_arg()],
311 ..Default::default()
312 }
313 .into();
314 YieldExpr {
315 span: *span,
316 delegate: false,
317 arg: Some(arg),
318 }
319 } else {
320 YieldExpr {
321 span: *span,
322 delegate: false,
323 arg: Some(arg.take()),
324 }
325 }
326 .into();
327 }
328 Expr::Yield(YieldExpr {
329 span,
330 arg: Some(arg),
331 delegate: true,
332 }) => {
333 let async_iter =
334 helper_expr!(async_iterator).as_call(DUMMY_SP, vec![arg.take().as_arg()]);
335
336 let arg = helper_expr!(async_generator_delegate)
337 .as_call(*span, vec![async_iter.as_arg()])
338 .into();
339
340 *expr = YieldExpr {
341 span: *span,
342 delegate: true,
343 arg: Some(arg),
344 }
345 .into()
346 }
347
348 _ => {}
349 }
350 }
351
352 fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
353 if self.fn_state.as_ref().is_some_and(|f| !f.is_async)
354 && !should_work::<ShouldWork, _>(&*stmts)
355 {
356 return;
357 }
358
359 stmts.visit_mut_children_with(self);
360 }
361
362 fn visit_mut_stmt(&mut self, stmt: &mut Stmt) {
363 if self.fn_state.as_ref().is_some_and(|f| !f.is_async)
364 && !should_work::<ShouldWork, _>(&*stmt)
365 {
366 return;
367 }
368
369 stmt.visit_mut_children_with(self);
370
371 if let Some(FnState {
372 is_async: true,
373 is_generator,
374 ..
375 }) = self.fn_state
376 {
377 handle_await_for(stmt, is_generator);
378 }
379 }
380
381 fn visit_mut_super(&mut self, _: &mut Super) {
382 if let Some(FnState { use_super, .. }) = &mut self.fn_state {
383 *use_super = true;
384 }
385 }
386}
387
388#[tracing::instrument(level = "debug", skip_all)]
392fn make_fn_ref(fn_state: &FnState, params: Vec<Param>, body: BlockStmt) -> Expr {
393 let helper = if fn_state.is_generator {
394 helper_expr!(DUMMY_SP, wrap_async_generator)
395 } else {
396 helper_expr!(DUMMY_SP, async_to_generator)
397 }
398 .as_callee();
399 let this = ThisExpr { span: DUMMY_SP };
400 let arguments = quote_ident!("arguments");
401
402 let inner_fn = Function {
403 is_generator: true,
404 params,
405 body: Some(body),
406 ..Default::default()
407 };
408
409 let call_async = CallExpr {
410 callee: helper,
411 args: vec![inner_fn.as_arg()],
412 ..Default::default()
413 };
414
415 if fn_state.use_arguments {
416 call_async
418 .make_member(quote_ident!("apply"))
419 .as_call(DUMMY_SP, vec![this.as_arg(), arguments.as_arg()])
420 } else if fn_state.use_this {
421 call_async
423 .make_member(quote_ident!("call"))
424 .as_call(DUMMY_SP, vec![this.as_arg()])
425 } else {
426 call_async.as_call(DUMMY_SP, vec![])
428 }
429}
430
431#[tracing::instrument(level = "debug", skip_all)]
432fn could_potentially_throw(param: &[Param], unresolved_ctxt: SyntaxContext) -> bool {
433 for param in param {
434 debug_assert!(param.decorators.is_empty());
435
436 match ¶m.pat {
437 Pat::Ident(..) => continue,
438 Pat::Rest(RestPat { arg, .. }) if arg.is_ident() => continue,
439 Pat::Assign(assign_pat) => match &*assign_pat.right {
440 Expr::Ident(Ident { ctxt, sym, .. })
441 if sym == "undefined" && *ctxt == unresolved_ctxt =>
442 {
443 continue
444 }
445 Expr::Lit(
446 Lit::Null(..) | Lit::Bool(..) | Lit::Num(..) | Lit::BigInt(..) | Lit::Str(..),
447 )
448 | Expr::Fn(..)
449 | Expr::Arrow(..) => continue,
450
451 _ => return true,
452 },
453 _ => return true,
454 }
455 }
456
457 false
458}
459
460#[derive(Default)]
461struct ShouldWork {
462 found: bool,
463}
464
465#[swc_trace]
466impl Visit for ShouldWork {
467 noop_visit_type!(fail);
468
469 fn visit_function(&mut self, f: &Function) {
470 if f.is_async {
471 self.found = true;
472 return;
473 }
474 f.visit_children_with(self);
475 }
476
477 fn visit_arrow_expr(&mut self, f: &ArrowExpr) {
478 if f.is_async {
479 self.found = true;
480 return;
481 }
482 f.visit_children_with(self);
483 }
484}
485
486impl Check for ShouldWork {
487 fn should_handle(&self) -> bool {
488 self.found
489 }
490}
491
492#[tracing::instrument(level = "debug", skip_all)]
493fn handle_await_for(stmt: &mut Stmt, is_async_generator: bool) {
494 let s = match stmt {
495 Stmt::ForOf(s @ ForOfStmt { is_await: true, .. }) => s.take(),
496 _ => return,
497 };
498
499 let value = private_ident!("_value");
500 let iterator = private_ident!("_iterator");
501 let iterator_error = private_ident!("_iteratorError");
502 let step = private_ident!("_step");
503 let did_iteration_error = private_ident!("_didIteratorError");
504 let iterator_abrupt_completion = private_ident!("_iteratorAbruptCompletion");
505 let err_param = private_ident!("err");
506
507 let try_body = {
508 let body_span = s.body.span();
509 let orig_body = match *s.body {
510 Stmt::Block(s) => s.stmts,
511 _ => vec![*s.body],
512 };
513
514 let mut for_loop_body = Vec::new();
515 {
516 let value_var = VarDeclarator {
518 span: DUMMY_SP,
519 name: value.clone().into(),
520 init: Some(step.clone().make_member(quote_ident!("value")).into()),
521 definite: false,
522 };
523 for_loop_body.push(
524 VarDecl {
525 span: DUMMY_SP,
526 kind: VarDeclKind::Let,
527 declare: false,
528 decls: vec![value_var],
529 ..Default::default()
530 }
531 .into(),
532 );
533 }
534
535 match s.left {
536 ForHead::VarDecl(v) => {
537 let var = v.decls.into_iter().next().unwrap();
538 let var_decl = VarDeclarator {
539 span: DUMMY_SP,
540 name: var.name,
541 init: Some(value.into()),
542 definite: false,
543 };
544 for_loop_body.push(
545 VarDecl {
546 span: DUMMY_SP,
547 kind: VarDeclKind::Const,
548 declare: false,
549 decls: vec![var_decl],
550 ..Default::default()
551 }
552 .into(),
553 );
554 }
555 ForHead::Pat(p) => {
556 for_loop_body.push(
557 ExprStmt {
558 span: DUMMY_SP,
559 expr: AssignExpr {
560 span: DUMMY_SP,
561 op: op!("="),
562 left: p.try_into().unwrap(),
563 right: Box::new(value.into()),
564 }
565 .into(),
566 }
567 .into(),
568 );
569 }
570
571 ForHead::UsingDecl(..) => {
572 unreachable!("using declaration must be removed by previous pass")
573 }
574 }
575
576 for_loop_body.extend(orig_body);
577
578 let for_loop_body = BlockStmt {
579 span: body_span,
580 stmts: for_loop_body,
581 ..Default::default()
582 };
583
584 let mut init_var_decls = Vec::new();
585 init_var_decls.push(VarDeclarator {
587 span: DUMMY_SP,
588 name: iterator.clone().into(),
589 init: {
590 let callee = helper!(async_iterator);
591
592 Some(
593 CallExpr {
594 span: DUMMY_SP,
595 callee,
596 args: vec![s.right.as_arg()],
597 ..Default::default()
598 }
599 .into(),
600 )
601 },
602 definite: false,
603 });
604 init_var_decls.push(VarDeclarator {
605 span: DUMMY_SP,
606 name: step.clone().into(),
607 init: None,
608 definite: false,
609 });
610
611 let for_stmt = ForStmt {
612 span: s.span,
613 init: Some(
615 VarDecl {
616 span: DUMMY_SP,
617 kind: VarDeclKind::Var,
618 declare: false,
619 decls: init_var_decls,
620 ..Default::default()
621 }
622 .into(),
623 ),
624 test: {
626 let iter_next = iterator.clone().make_member(quote_ident!("next"));
627 let iter_next = CallExpr {
628 span: DUMMY_SP,
629 callee: iter_next.as_callee(),
630 args: Default::default(),
631 ..Default::default()
632 };
633
634 let yield_arg = if is_async_generator {
635 CallExpr {
636 span: DUMMY_SP,
637 callee: helper!(await_async_generator),
638 args: vec![iter_next.as_arg()],
639 ..Default::default()
640 }
641 .into()
642 } else {
643 iter_next.into()
644 };
645
646 let assign_to_step: Expr = AssignExpr {
647 span: DUMMY_SP,
648 op: op!("="),
649 left: step.into(),
650 right: YieldExpr {
651 span: DUMMY_SP,
652 arg: Some(yield_arg),
653 delegate: false,
654 }
655 .into(),
656 }
657 .into();
658
659 let right = UnaryExpr {
660 span: DUMMY_SP,
661 op: op!("!"),
662 arg: assign_to_step.make_member(quote_ident!("done")).into(),
663 }
664 .into();
665
666 let left = iterator_abrupt_completion.clone().into();
667
668 Some(
669 AssignExpr {
670 span: DUMMY_SP,
671 op: op!("="),
672 left,
673 right,
674 }
675 .into(),
676 )
677 },
678 update: Some(
680 AssignExpr {
681 span: DUMMY_SP,
682 op: op!("="),
683 left: iterator_abrupt_completion.clone().into(),
684 right: false.into(),
685 }
686 .into(),
687 ),
688 body: Box::new(Stmt::Block(for_loop_body)),
689 }
690 .into();
691
692 BlockStmt {
693 span: body_span,
694 stmts: vec![for_stmt],
695 ..Default::default()
696 }
697 };
698
699 let catch_clause = {
700 let mark_as_errorred = ExprStmt {
702 span: DUMMY_SP,
703 expr: AssignExpr {
704 span: DUMMY_SP,
705 op: op!("="),
706 left: did_iteration_error.clone().into(),
707 right: true.into(),
708 }
709 .into(),
710 }
711 .into();
712 let store_error = ExprStmt {
714 span: DUMMY_SP,
715 expr: AssignExpr {
716 span: DUMMY_SP,
717 op: op!("="),
718 left: iterator_error.clone().into(),
719 right: Box::new(err_param.clone().into()),
720 }
721 .into(),
722 }
723 .into();
724
725 CatchClause {
726 span: DUMMY_SP,
727 param: Some(err_param.into()),
728 body: BlockStmt {
729 stmts: vec![mark_as_errorred, store_error],
730 ..Default::default()
731 },
732 }
733 };
734
735 let finally_block = {
736 let throw_iterator_error = ThrowStmt {
737 span: DUMMY_SP,
738 arg: iterator_error.clone().into(),
739 }
740 .into();
741 let throw_iterator_error = IfStmt {
742 span: DUMMY_SP,
743 test: did_iteration_error.clone().into(),
744 cons: Box::new(Stmt::Block(BlockStmt {
745 span: DUMMY_SP,
746 stmts: vec![throw_iterator_error],
747 ..Default::default()
748 })),
749 alt: None,
750 }
751 .into();
752
753 let iterator_return: Expr = CallExpr {
754 span: DUMMY_SP,
755 callee: iterator
756 .clone()
757 .make_member(quote_ident!("return"))
758 .as_callee(),
759 args: Vec::new(),
760 ..Default::default()
761 }
762 .into();
763
764 let yield_stmt = ExprStmt {
768 span: DUMMY_SP,
769 expr: YieldExpr {
770 span: DUMMY_SP,
771 delegate: false,
772 arg: Some(if is_async_generator {
773 CallExpr {
774 span: DUMMY_SP,
775 callee: helper!(await_async_generator),
776 args: vec![iterator_return.as_arg()],
777 ..Default::default()
778 }
779 .into()
780 } else {
781 iterator_return.into()
782 }),
783 }
784 .into(),
785 }
786 .into();
787
788 let conditional_yield = IfStmt {
789 span: DUMMY_SP,
790 test: BinExpr {
792 span: DUMMY_SP,
793 op: op!("&&"),
794 left: Box::new(iterator_abrupt_completion.clone().into()),
796 right: Box::new(
798 BinExpr {
799 span: DUMMY_SP,
800 op: op!("!="),
801 left: iterator.make_member(quote_ident!("return")).into(),
802 right: Null { span: DUMMY_SP }.into(),
803 }
804 .into(),
805 ),
806 }
807 .into(),
808 cons: Box::new(Stmt::Block(BlockStmt {
809 stmts: vec![yield_stmt],
810 ..Default::default()
811 })),
812 alt: None,
813 }
814 .into();
815 let body = BlockStmt {
816 stmts: vec![conditional_yield],
817 ..Default::default()
818 };
819
820 let inner_try = TryStmt {
821 span: DUMMY_SP,
822 block: body,
823 handler: None,
824 finalizer: Some(BlockStmt {
825 stmts: vec![throw_iterator_error],
826 ..Default::default()
827 }),
828 }
829 .into();
830 BlockStmt {
831 stmts: vec![inner_try],
832 ..Default::default()
833 }
834 };
835
836 let try_stmt = TryStmt {
837 span: s.span,
838 block: try_body,
839 handler: Some(catch_clause),
840 finalizer: Some(finally_block),
841 };
842
843 let stmts = vec![
844 VarDecl {
845 kind: VarDeclKind::Var,
846 decls: vec![
847 VarDeclarator {
849 span: DUMMY_SP,
850 name: iterator_abrupt_completion.into(),
851 init: Some(false.into()),
852 definite: false,
853 },
854 VarDeclarator {
856 span: DUMMY_SP,
857 name: did_iteration_error.into(),
858 init: Some(false.into()),
859 definite: false,
860 },
861 VarDeclarator {
863 span: DUMMY_SP,
864 name: iterator_error.into(),
865 init: None,
866 definite: false,
867 },
868 ],
869 ..Default::default()
870 }
871 .into(),
872 try_stmt.into(),
873 ];
874
875 *stmt = BlockStmt {
876 span: s.span,
877 stmts,
878 ..Default::default()
879 }
880 .into()
881}