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