swc_ecma_compat_es2017/
async_to_generator.rs

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
19/// `@babel/plugin-transform-async-to-generator`
20///
21/// ## In
22///
23/// ```js
24/// async function foo() {
25///   await bar();
26/// }
27/// ```
28///
29/// ## Out
30///
31/// ```js
32/// var _async_to_generator = function (fn) {
33///   ...
34/// };
35/// var foo = _async_to_generator(function* () {
36///   yield bar();
37/// });
38/// ```
39pub 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            // slow path
101            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            // Errors thrown during argument evaluation must reject the resulting promise,
114            // which needs more complex code to handle
115            // https://github.com/evanw/esbuild/blob/75286c1b4fabcf93140b97c3c0488f0253158b47/internal/js_parser/js_parser_lower.go#L364
116            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        // arrow expressions cannot be generator
166        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        // `this`/`arguments`/`super` are inherited from the parent function
180        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            // slow path
189            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/// Creates
389///
390/// `_async_to_generator(function*() {})()` from `async function() {}`;
391#[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        // fn.apply(this, arguments)
417        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        // fn.call(this)
422        call_async
423            .make_member(quote_ident!("call"))
424            .as_call(DUMMY_SP, vec![this.as_arg()])
425    } else {
426        // fn()
427        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 &param.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 = _step.value;
517            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        // _iterator = _async_iterator(lol())
586        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            // var _iterator = _async_iterator(lol()), _step;
614            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            // _iteratorAbruptCompletion = !(_step = yield _iterator.next()).done
625            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            // _iteratorNormalCompletion = true
679            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        // _didIteratorError = true;
701        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        // _iteratorError = err;
713        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        // yield _iterator.return();
765        // or
766        // yield _awaitAsyncGenerator(_iterator.return());
767        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            // _iteratorAbruptCompletion && _iterator.return != null
791            test: BinExpr {
792                span: DUMMY_SP,
793                op: op!("&&"),
794                // _iteratorAbruptCompletion
795                left: Box::new(iterator_abrupt_completion.clone().into()),
796                // _iterator.return != null
797                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                // var _iteratorAbruptCompletion = false;
848                VarDeclarator {
849                    span: DUMMY_SP,
850                    name: iterator_abrupt_completion.into(),
851                    init: Some(false.into()),
852                    definite: false,
853                },
854                // var _didIteratorError = false;
855                VarDeclarator {
856                    span: DUMMY_SP,
857                    name: did_iteration_error.into(),
858                    init: Some(false.into()),
859                    definite: false,
860                },
861                // var _iteratorError;
862                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}