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 should_handle_super =
187 fn_state.use_super && self.fn_state.as_ref().is_some_and(|s| !s.is_async);
188
189 let mut stmts = vec![];
190 if should_handle_super {
191 let mut fn_env_hoister = FnEnvHoister::new(self.unresolved_ctxt);
193 fn_env_hoister.disable_this();
194 fn_env_hoister.disable_arguments();
195
196 arrow_expr.body.visit_mut_with(&mut fn_env_hoister);
197
198 stmts.extend(fn_env_hoister.to_stmt());
199 }
200
201 arrow_expr.is_async = false;
202
203 let body = match *arrow_expr.body.take() {
204 BlockStmtOrExpr::BlockStmt(block_stmt) => block_stmt,
205 BlockStmtOrExpr::Expr(expr) => BlockStmt {
206 stmts: vec![ReturnStmt {
207 arg: Some(expr),
208 ..Default::default()
209 }
210 .into()],
211 ..Default::default()
212 },
213 };
214
215 let expr = make_fn_ref(&fn_state, vec![], body);
216
217 arrow_expr.body = if should_handle_super {
218 stmts.push(expr.into_stmt());
219 BlockStmtOrExpr::BlockStmt(BlockStmt {
220 stmts,
221 ..Default::default()
222 })
223 } else {
224 BlockStmtOrExpr::Expr(Box::new(expr))
225 }
226 .into()
227 }
228
229 fn visit_mut_class(&mut self, class: &mut Class) {
230 class.super_class.visit_mut_with(self);
231 let in_subclass = mem::replace(&mut self.in_subclass, class.super_class.is_some());
232 class.body.visit_mut_with(self);
233 self.in_subclass = in_subclass;
234 }
235
236 fn visit_mut_constructor(&mut self, constructor: &mut Constructor) {
237 constructor.params.visit_mut_with(self);
238
239 if let Some(BlockStmt { stmts, .. }) = &mut constructor.body {
240 if !should_work::<ShouldWork, _>(&*stmts) {
241 return;
242 }
243
244 let (decl, this_id) = if self.in_subclass {
245 let mut fn_env_hoister = FnEnvHoister::new(self.unresolved_ctxt);
246 stmts.visit_mut_with(&mut fn_env_hoister);
247 fn_env_hoister.to_stmt_in_subclass()
248 } else {
249 (None, None)
250 };
251
252 stmts.visit_mut_children_with(self);
253
254 if let Some(this_id) = this_id {
255 init_this(stmts, &this_id)
256 }
257
258 if let Some(decl) = decl {
259 prepend_stmt(stmts, decl)
260 }
261 }
262 }
263
264 fn visit_mut_getter_prop(&mut self, f: &mut GetterProp) {
265 let fn_state = self.fn_state.take();
266 f.visit_mut_children_with(self);
267 self.fn_state = fn_state;
268 }
269
270 fn visit_mut_setter_prop(&mut self, f: &mut SetterProp) {
271 f.param.visit_mut_with(self);
272 let fn_state = self.fn_state.take();
273 f.body.visit_mut_with(self);
274 self.fn_state = fn_state;
275 }
276
277 fn visit_mut_exprs(&mut self, exprs: &mut Vec<Box<Expr>>) {
278 if self.fn_state.as_ref().is_some_and(|f| !f.is_async)
279 && !should_work::<ShouldWork, _>(&*exprs)
280 {
281 return;
282 }
283
284 exprs.visit_mut_children_with(self);
285 }
286
287 fn visit_mut_expr(&mut self, expr: &mut Expr) {
288 if self.fn_state.as_ref().is_some_and(|f| !f.is_async)
289 && !should_work::<ShouldWork, _>(&*expr)
290 {
291 return;
292 }
293
294 expr.visit_mut_children_with(self);
295
296 let Some(fn_state @ FnState { is_async: true, .. }) = &mut self.fn_state else {
297 return;
298 };
299
300 match expr {
301 Expr::This(..) => {
302 fn_state.use_this = true;
303 }
304 Expr::Ident(Ident { sym, .. }) if sym == "arguments" => {
305 fn_state.use_arguments = true;
306 }
307 Expr::Await(AwaitExpr { arg, span }) => {
308 *expr = if fn_state.is_generator {
309 let callee = helper!(await_async_generator);
310 let arg = CallExpr {
311 span: *span,
312 callee,
313 args: vec![arg.take().as_arg()],
314 ..Default::default()
315 }
316 .into();
317 YieldExpr {
318 span: *span,
319 delegate: false,
320 arg: Some(arg),
321 }
322 } else {
323 YieldExpr {
324 span: *span,
325 delegate: false,
326 arg: Some(arg.take()),
327 }
328 }
329 .into();
330 }
331 Expr::Yield(YieldExpr {
332 span,
333 arg: Some(arg),
334 delegate: true,
335 }) => {
336 let async_iter =
337 helper_expr!(async_iterator).as_call(DUMMY_SP, vec![arg.take().as_arg()]);
338
339 let arg = helper_expr!(async_generator_delegate)
340 .as_call(*span, vec![async_iter.as_arg()])
341 .into();
342
343 *expr = YieldExpr {
344 span: *span,
345 delegate: true,
346 arg: Some(arg),
347 }
348 .into()
349 }
350
351 _ => {}
352 }
353 }
354
355 fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
356 if self.fn_state.as_ref().is_some_and(|f| !f.is_async)
357 && !should_work::<ShouldWork, _>(&*stmts)
358 {
359 return;
360 }
361
362 stmts.visit_mut_children_with(self);
363 }
364
365 fn visit_mut_stmt(&mut self, stmt: &mut Stmt) {
366 if self.fn_state.as_ref().is_some_and(|f| !f.is_async)
367 && !should_work::<ShouldWork, _>(&*stmt)
368 {
369 return;
370 }
371
372 stmt.visit_mut_children_with(self);
373
374 if let Some(FnState {
375 is_async: true,
376 is_generator,
377 ..
378 }) = self.fn_state
379 {
380 handle_await_for(stmt, is_generator);
381 }
382 }
383
384 fn visit_mut_super(&mut self, _: &mut Super) {
385 if let Some(FnState { use_super, .. }) = &mut self.fn_state {
386 *use_super = true;
387 }
388 }
389}
390
391#[tracing::instrument(level = "debug", skip_all)]
395fn make_fn_ref(fn_state: &FnState, params: Vec<Param>, body: BlockStmt) -> Expr {
396 let helper = if fn_state.is_generator {
397 helper_expr!(DUMMY_SP, wrap_async_generator)
398 } else {
399 helper_expr!(DUMMY_SP, async_to_generator)
400 }
401 .as_callee();
402 let this = ThisExpr { span: DUMMY_SP };
403 let arguments = quote_ident!("arguments");
404
405 let inner_fn = Function {
406 is_generator: true,
407 params,
408 body: Some(body),
409 ..Default::default()
410 };
411
412 let call_async = CallExpr {
413 callee: helper,
414 args: vec![inner_fn.as_arg()],
415 ..Default::default()
416 };
417
418 if fn_state.use_arguments {
419 call_async
421 .make_member(quote_ident!("apply"))
422 .as_call(DUMMY_SP, vec![this.as_arg(), arguments.as_arg()])
423 } else if fn_state.use_this {
424 call_async
426 .make_member(quote_ident!("call"))
427 .as_call(DUMMY_SP, vec![this.as_arg()])
428 } else {
429 call_async.as_call(DUMMY_SP, vec![])
431 }
432}
433
434#[tracing::instrument(level = "debug", skip_all)]
435fn could_potentially_throw(param: &[Param], unresolved_ctxt: SyntaxContext) -> bool {
436 for param in param {
437 debug_assert!(param.decorators.is_empty());
438
439 match ¶m.pat {
440 Pat::Ident(..) => continue,
441 Pat::Rest(RestPat { arg, .. }) if arg.is_ident() => continue,
442 Pat::Assign(assign_pat) => match &*assign_pat.right {
443 Expr::Ident(Ident { ctxt, sym, .. })
444 if sym == "undefined" && *ctxt == unresolved_ctxt =>
445 {
446 continue
447 }
448 Expr::Lit(
449 Lit::Null(..) | Lit::Bool(..) | Lit::Num(..) | Lit::BigInt(..) | Lit::Str(..),
450 )
451 | Expr::Fn(..)
452 | Expr::Arrow(..) => continue,
453
454 _ => return true,
455 },
456 _ => return true,
457 }
458 }
459
460 false
461}
462
463#[derive(Default)]
464struct ShouldWork {
465 found: bool,
466}
467
468#[swc_trace]
469impl Visit for ShouldWork {
470 noop_visit_type!(fail);
471
472 fn visit_function(&mut self, f: &Function) {
473 if f.is_async {
474 self.found = true;
475 return;
476 }
477 f.visit_children_with(self);
478 }
479
480 fn visit_arrow_expr(&mut self, f: &ArrowExpr) {
481 if f.is_async {
482 self.found = true;
483 return;
484 }
485 f.visit_children_with(self);
486 }
487}
488
489impl Check for ShouldWork {
490 fn should_handle(&self) -> bool {
491 self.found
492 }
493}
494
495#[tracing::instrument(level = "debug", skip_all)]
496fn handle_await_for(stmt: &mut Stmt, is_async_generator: bool) {
497 let s = match stmt {
498 Stmt::ForOf(s @ ForOfStmt { is_await: true, .. }) => s.take(),
499 _ => return,
500 };
501
502 let value = private_ident!("_value");
503 let iterator = private_ident!("_iterator");
504 let iterator_error = private_ident!("_iteratorError");
505 let step = private_ident!("_step");
506 let did_iteration_error = private_ident!("_didIteratorError");
507 let iterator_abrupt_completion = private_ident!("_iteratorAbruptCompletion");
508 let err_param = private_ident!("err");
509
510 let try_body = {
511 let body_span = s.body.span();
512 let orig_body = match *s.body {
513 Stmt::Block(s) => s.stmts,
514 _ => vec![*s.body],
515 };
516
517 let mut for_loop_body = Vec::new();
518 {
519 let value_var = VarDeclarator {
521 span: DUMMY_SP,
522 name: value.clone().into(),
523 init: Some(step.clone().make_member(quote_ident!("value")).into()),
524 definite: false,
525 };
526 for_loop_body.push(
527 VarDecl {
528 span: DUMMY_SP,
529 kind: VarDeclKind::Let,
530 declare: false,
531 decls: vec![value_var],
532 ..Default::default()
533 }
534 .into(),
535 );
536 }
537
538 match s.left {
539 ForHead::VarDecl(v) => {
540 let var = v.decls.into_iter().next().unwrap();
541 let var_decl = VarDeclarator {
542 span: DUMMY_SP,
543 name: var.name,
544 init: Some(value.into()),
545 definite: false,
546 };
547 for_loop_body.push(
548 VarDecl {
549 span: DUMMY_SP,
550 kind: VarDeclKind::Const,
551 declare: false,
552 decls: vec![var_decl],
553 ..Default::default()
554 }
555 .into(),
556 );
557 }
558 ForHead::Pat(p) => {
559 for_loop_body.push(
560 ExprStmt {
561 span: DUMMY_SP,
562 expr: AssignExpr {
563 span: DUMMY_SP,
564 op: op!("="),
565 left: p.try_into().unwrap(),
566 right: Box::new(value.into()),
567 }
568 .into(),
569 }
570 .into(),
571 );
572 }
573
574 ForHead::UsingDecl(..) => {
575 unreachable!("using declaration must be removed by previous pass")
576 }
577 }
578
579 for_loop_body.extend(orig_body);
580
581 let for_loop_body = BlockStmt {
582 span: body_span,
583 stmts: for_loop_body,
584 ..Default::default()
585 };
586
587 let mut init_var_decls = Vec::new();
588 init_var_decls.push(VarDeclarator {
590 span: DUMMY_SP,
591 name: iterator.clone().into(),
592 init: {
593 let callee = helper!(async_iterator);
594
595 Some(
596 CallExpr {
597 span: DUMMY_SP,
598 callee,
599 args: vec![s.right.as_arg()],
600 ..Default::default()
601 }
602 .into(),
603 )
604 },
605 definite: false,
606 });
607 init_var_decls.push(VarDeclarator {
608 span: DUMMY_SP,
609 name: step.clone().into(),
610 init: None,
611 definite: false,
612 });
613
614 let for_stmt = ForStmt {
615 span: s.span,
616 init: Some(
618 VarDecl {
619 span: DUMMY_SP,
620 kind: VarDeclKind::Var,
621 declare: false,
622 decls: init_var_decls,
623 ..Default::default()
624 }
625 .into(),
626 ),
627 test: {
629 let iter_next = iterator.clone().make_member(quote_ident!("next"));
630 let iter_next = CallExpr {
631 span: DUMMY_SP,
632 callee: iter_next.as_callee(),
633 args: Default::default(),
634 ..Default::default()
635 };
636
637 let yield_arg = if is_async_generator {
638 CallExpr {
639 span: DUMMY_SP,
640 callee: helper!(await_async_generator),
641 args: vec![iter_next.as_arg()],
642 ..Default::default()
643 }
644 .into()
645 } else {
646 iter_next.into()
647 };
648
649 let assign_to_step: Expr = AssignExpr {
650 span: DUMMY_SP,
651 op: op!("="),
652 left: step.into(),
653 right: YieldExpr {
654 span: DUMMY_SP,
655 arg: Some(yield_arg),
656 delegate: false,
657 }
658 .into(),
659 }
660 .into();
661
662 let right = UnaryExpr {
663 span: DUMMY_SP,
664 op: op!("!"),
665 arg: assign_to_step.make_member(quote_ident!("done")).into(),
666 }
667 .into();
668
669 let left = iterator_abrupt_completion.clone().into();
670
671 Some(
672 AssignExpr {
673 span: DUMMY_SP,
674 op: op!("="),
675 left,
676 right,
677 }
678 .into(),
679 )
680 },
681 update: Some(
683 AssignExpr {
684 span: DUMMY_SP,
685 op: op!("="),
686 left: iterator_abrupt_completion.clone().into(),
687 right: false.into(),
688 }
689 .into(),
690 ),
691 body: Box::new(Stmt::Block(for_loop_body)),
692 }
693 .into();
694
695 BlockStmt {
696 span: body_span,
697 stmts: vec![for_stmt],
698 ..Default::default()
699 }
700 };
701
702 let catch_clause = {
703 let mark_as_errorred = ExprStmt {
705 span: DUMMY_SP,
706 expr: AssignExpr {
707 span: DUMMY_SP,
708 op: op!("="),
709 left: did_iteration_error.clone().into(),
710 right: true.into(),
711 }
712 .into(),
713 }
714 .into();
715 let store_error = ExprStmt {
717 span: DUMMY_SP,
718 expr: AssignExpr {
719 span: DUMMY_SP,
720 op: op!("="),
721 left: iterator_error.clone().into(),
722 right: Box::new(err_param.clone().into()),
723 }
724 .into(),
725 }
726 .into();
727
728 CatchClause {
729 span: DUMMY_SP,
730 param: Some(err_param.into()),
731 body: BlockStmt {
732 stmts: vec![mark_as_errorred, store_error],
733 ..Default::default()
734 },
735 }
736 };
737
738 let finally_block = {
739 let throw_iterator_error = ThrowStmt {
740 span: DUMMY_SP,
741 arg: iterator_error.clone().into(),
742 }
743 .into();
744 let throw_iterator_error = IfStmt {
745 span: DUMMY_SP,
746 test: did_iteration_error.clone().into(),
747 cons: Box::new(Stmt::Block(BlockStmt {
748 span: DUMMY_SP,
749 stmts: vec![throw_iterator_error],
750 ..Default::default()
751 })),
752 alt: None,
753 }
754 .into();
755
756 let iterator_return: Expr = CallExpr {
757 span: DUMMY_SP,
758 callee: iterator
759 .clone()
760 .make_member(quote_ident!("return"))
761 .as_callee(),
762 args: Vec::new(),
763 ..Default::default()
764 }
765 .into();
766
767 let yield_stmt = ExprStmt {
771 span: DUMMY_SP,
772 expr: YieldExpr {
773 span: DUMMY_SP,
774 delegate: false,
775 arg: Some(if is_async_generator {
776 CallExpr {
777 span: DUMMY_SP,
778 callee: helper!(await_async_generator),
779 args: vec![iterator_return.as_arg()],
780 ..Default::default()
781 }
782 .into()
783 } else {
784 iterator_return.into()
785 }),
786 }
787 .into(),
788 }
789 .into();
790
791 let conditional_yield = IfStmt {
792 span: DUMMY_SP,
793 test: BinExpr {
795 span: DUMMY_SP,
796 op: op!("&&"),
797 left: Box::new(iterator_abrupt_completion.clone().into()),
799 right: Box::new(
801 BinExpr {
802 span: DUMMY_SP,
803 op: op!("!="),
804 left: iterator.make_member(quote_ident!("return")).into(),
805 right: Null { span: DUMMY_SP }.into(),
806 }
807 .into(),
808 ),
809 }
810 .into(),
811 cons: Box::new(Stmt::Block(BlockStmt {
812 stmts: vec![yield_stmt],
813 ..Default::default()
814 })),
815 alt: None,
816 }
817 .into();
818 let body = BlockStmt {
819 stmts: vec![conditional_yield],
820 ..Default::default()
821 };
822
823 let inner_try = TryStmt {
824 span: DUMMY_SP,
825 block: body,
826 handler: None,
827 finalizer: Some(BlockStmt {
828 stmts: vec![throw_iterator_error],
829 ..Default::default()
830 }),
831 }
832 .into();
833 BlockStmt {
834 stmts: vec![inner_try],
835 ..Default::default()
836 }
837 };
838
839 let try_stmt = TryStmt {
840 span: s.span,
841 block: try_body,
842 handler: Some(catch_clause),
843 finalizer: Some(finally_block),
844 };
845
846 let stmts = vec![
847 VarDecl {
848 kind: VarDeclKind::Var,
849 decls: vec![
850 VarDeclarator {
852 span: DUMMY_SP,
853 name: iterator_abrupt_completion.into(),
854 init: Some(false.into()),
855 definite: false,
856 },
857 VarDeclarator {
859 span: DUMMY_SP,
860 name: did_iteration_error.into(),
861 init: Some(false.into()),
862 definite: false,
863 },
864 VarDeclarator {
866 span: DUMMY_SP,
867 name: iterator_error.into(),
868 init: None,
869 definite: false,
870 },
871 ],
872 ..Default::default()
873 }
874 .into(),
875 try_stmt.into(),
876 ];
877
878 *stmt = BlockStmt {
879 span: s.span,
880 stmts,
881 ..Default::default()
882 }
883 .into()
884}