swc_ecma_compat_es2015/
generator.rs

1use std::{
2    cell::{RefCell, RefMut},
3    iter::once,
4    mem::take,
5    rc::Rc,
6};
7
8use is_macro::Is;
9use swc_atoms::Atom;
10use swc_common::{
11    comments::Comments, util::take::Take, BytePos, EqIgnoreSpan, Mark, Span, Spanned,
12    SyntaxContext, DUMMY_SP,
13};
14use swc_ecma_ast::*;
15use swc_ecma_transforms_base::helper;
16use swc_ecma_utils::{
17    function::FnEnvHoister, private_ident, prop_name_to_expr_value, quote_ident, ExprFactory,
18};
19use swc_ecma_visit::{
20    noop_visit_mut_type, noop_visit_type, visit_mut_pass, Visit, VisitMut, VisitMutWith, VisitWith,
21};
22use tracing::debug;
23
24/// Generator based on tsc generator at https://github.com/microsoft/TypeScript/blob/162224763681465b417274383317ca9a0a573835/src/compiler/transformers/generators.ts
25pub fn generator<C>(unresolved_mark: Mark, _comments: C) -> impl Pass
26where
27    C: Comments,
28{
29    visit_mut_pass(Wrapper {
30        unresolved_ctxt: SyntaxContext::empty().apply_mark(unresolved_mark),
31    })
32}
33
34/// Instead of saving state, we just create another instance of [Generator].
35struct Wrapper {
36    unresolved_ctxt: SyntaxContext,
37}
38
39macro_rules! dev_span {
40    ($($tt:tt)*) => {{
41        if cfg!(debug_assertions) {
42            Some(tracing::span!(tracing::Level::ERROR, $($tt)*).entered())
43        } else {
44            None
45        }
46    }};
47}
48
49impl VisitMut for Wrapper {
50    noop_visit_mut_type!(fail);
51
52    fn visit_mut_function(&mut self, f: &mut Function) {
53        f.visit_mut_children_with(self);
54
55        if f.is_generator {
56            let mut v = Generator::default();
57
58            let mut hoister = FnEnvHoister::new(self.unresolved_ctxt);
59            hoister.disable_super();
60            hoister.disable_this();
61
62            f.visit_mut_children_with(&mut hoister);
63
64            v.transform_and_emit_stmts(f.body.as_mut().unwrap().stmts.take(), 0);
65            f.is_generator = false;
66
67            let mut stmts = v.build_stmts();
68            stmts.visit_mut_with(&mut InvalidToLit {
69                map: v.label_exprs.as_deref(),
70            });
71            let inner_fn = Box::new(Function {
72                span: DUMMY_SP,
73                params: vec![Param {
74                    span: DUMMY_SP,
75                    decorators: Default::default(),
76                    pat: Pat::Ident(v.state.clone().into()),
77                }],
78                decorators: Default::default(),
79                body: Some(BlockStmt {
80                    stmts,
81                    ..Default::default()
82                }),
83                is_generator: false,
84                is_async: false,
85                ..Default::default()
86            });
87            let generator_object = CallExpr {
88                span: DUMMY_SP,
89                callee: helper!(ts, ts_generator),
90                args: vec![
91                    ThisExpr { span: DUMMY_SP }.as_arg(),
92                    FnExpr {
93                        ident: None,
94                        function: inner_fn,
95                    }
96                    .as_arg(),
97                ],
98                ..Default::default()
99            }
100            .into();
101            let mut stmts = Vec::new();
102            if !v.hoisted_vars.is_empty() {
103                stmts.push(
104                    VarDecl {
105                        span: DUMMY_SP,
106                        kind: VarDeclKind::Var,
107                        declare: Default::default(),
108                        decls: v.hoisted_vars.take(),
109                        ..Default::default()
110                    }
111                    .into(),
112                )
113            }
114            let vars = hoister.to_decl();
115            if !vars.is_empty() {
116                stmts.push(
117                    VarDecl {
118                        span: DUMMY_SP,
119                        kind: VarDeclKind::Var,
120                        declare: Default::default(),
121                        decls: vars,
122                        ..Default::default()
123                    }
124                    .into(),
125                )
126            }
127            stmts.extend(v.hoisted_fns.into_iter().map(Decl::Fn).map(Stmt::Decl));
128
129            stmts.push(
130                ReturnStmt {
131                    span: DUMMY_SP,
132                    arg: Some(generator_object),
133                }
134                .into(),
135            );
136            f.body.as_mut().unwrap().stmts = stmts;
137        }
138    }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142struct Label(isize);
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145enum OpCode {
146    /// No operation, used to force a new case in the state machine
147    Nop,
148    /// A regular javascript statement
149    Statement,
150    /// An assignment
151    Assign,
152    /// A break instruction used to jump to a label
153    Break,
154    /// A break instruction used to jump to a label if a condition evaluates to
155    /// true
156    BreakWhenTrue,
157    /// A break instruction used to jump to a label if a condition evaluates to
158    /// false
159    BreakWhenFalse,
160    /// A completion instruction for the `yield` keyword
161    Yield,
162    /// A completion instruction for the `yield*` keyword (not implemented, but
163    /// reserved for future use)
164    YieldStar,
165    /// A completion instruction for the `return` keyword
166    Return,
167    /// A completion instruction for the `throw` keyword
168    Throw,
169    /// Marks the end of a `finally` block
170    Endfinally,
171}
172
173#[derive(Debug, Is, Clone)]
174enum OpArgs {
175    Label(Label),
176    LabelExpr(Label, Box<Expr>),
177    Stmt(Box<Stmt>),
178    OptExpr(Option<Box<Expr>>),
179    PatAndExpr(AssignTarget, Box<Expr>),
180}
181
182/// whether a generated code block is opening or closing at the current
183/// operation for a FunctionBuilder
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185enum BlockAction {
186    Open,
187    Close,
188}
189
190/// the kind for a generated code block in a FunctionBuilder
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192enum CodeBlockKind {
193    Exception,
194    With,
195    Switch,
196    Loop,
197    Labeled,
198}
199
200/// the state for a generated code exception block
201#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
202enum ExceptionBlockState {
203    Try,
204    Catch,
205    Finally,
206    Done,
207}
208
209/// A generated code block
210#[derive(Debug)]
211enum CodeBlock {
212    Exception(ExceptionBlock),
213    Labeled(LabeledBlock),
214    Switch(SwitchBlock),
215    Loop(LoopBlock),
216    With(WithBlock),
217}
218
219impl CodeBlock {
220    fn is_script(&self) -> bool {
221        match self {
222            Self::Exception(..) => false,
223            Self::Labeled(b) => b.is_script,
224            Self::Switch(b) => b.is_script,
225            Self::Loop(b) => b.is_script,
226            Self::With(..) => false,
227        }
228    }
229
230    fn label_text(&self) -> Option<Atom> {
231        match self {
232            Self::Labeled(s) => Some(s.label_text.clone()),
233            _ => None,
234        }
235    }
236
237    fn break_label(&self) -> Option<Label> {
238        Some(match self {
239            Self::Labeled(b) => b.break_label,
240            Self::Switch(b) => b.break_label,
241            Self::Loop(b) => b.break_label,
242            _ => return None,
243        })
244    }
245
246    fn continue_label(&self) -> Option<Label> {
247        Some(match self {
248            Self::Loop(b) => b.continue_label,
249            _ => return None,
250        })
251    }
252}
253
254/// a generated exception block, used for 'try' statements
255#[derive(Debug)]
256struct ExceptionBlock {
257    state: ExceptionBlockState,
258    start_label: Label,
259    catch_variable: Option<Ident>,
260    catch_label: Option<Label>,
261    finally_label: Option<Label>,
262    end_label: Label,
263}
264
265/// A generated code that tracks the target for 'break' statements in a
266/// LabeledStatement.
267#[derive(Debug)]
268struct LabeledBlock {
269    label_text: Atom,
270    is_script: bool,
271    break_label: Label,
272}
273
274/// a generated block that tracks the target for 'break' statements in a
275/// 'switch' statement
276#[derive(Debug)]
277struct SwitchBlock {
278    is_script: bool,
279    break_label: Label,
280}
281
282/// a generated block that tracks the targets for 'break' and 'continue'
283/// statements, used for iteration statements
284
285#[derive(Debug)]
286struct LoopBlock {
287    continue_label: Label,
288    is_script: bool,
289    break_label: Label,
290}
291
292/// a generated block associated with a 'with' statement
293#[allow(unused)]
294#[derive(Debug)]
295struct WithBlock {
296    expression: Ident,
297    start_label: Label,
298    end_label: Label,
299}
300
301#[allow(dead_code)]
302#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
303enum Instruction {
304    Next = 0,
305    Throw = 1,
306    Return = 2,
307    Break = 3,
308    Yield = 4,
309    YieldStar = 5,
310    Catch = 6,
311    Endfinally = 7,
312}
313
314impl Instruction {
315    // fn name(self) -> Option<&'static str> {
316    //     match self {
317    //         Instruction::Return => Some("return"),
318    //         Instruction::Break => Some("break"),
319    //         Instruction::Yield => Some("yield"),
320    //         Instruction::YieldStar => Some("yield*"),
321    //         Instruction::Endfinally => Some("endfinally"),
322    //         _ => None,
323    //     }
324    // }
325}
326
327struct Generator {
328    in_statement_containing_yield: bool,
329
330    blocks: Option<Vec<Ptr<CodeBlock>>>,
331    block_offsets: Option<Vec<usize>>,
332    block_actions: Option<Vec<BlockAction>>,
333    /// Index to `blocks`
334    block_stack: Option<Vec<Ptr<CodeBlock>>>,
335
336    label_offsets: Option<Vec<i32>>,
337    label_exprs: Option<Vec<Vec<Loc>>>,
338    next_label_id: usize,
339
340    operations: Option<Vec<OpCode>>,
341    operation_args: Option<Vec<Option<OpArgs>>>,
342    operation_locs: Option<Vec<Span>>,
343
344    state: Ident,
345
346    block_index: usize,
347    label_number: usize,
348    label_numbers: Option<Vec<Vec<usize>>>,
349    last_operation_was_abrupt: bool,
350    last_operation_was_completion: bool,
351    clauses: Option<Vec<SwitchCase>>,
352    stmts: Option<Vec<Stmt>>,
353    /// Index to `blocks`
354    exception_block_stack: Option<Vec<Ptr<CodeBlock>>>,
355    /// Index to `blocks`
356    current_exception_block: Option<Ptr<CodeBlock>>,
357    /// Index to `blocks`
358    with_block_stack: Option<Vec<Ptr<CodeBlock>>>,
359
360    hoisted_vars: Vec<VarDeclarator>,
361    hoisted_fns: Vec<FnDecl>,
362}
363
364type Ptr<T> = Rc<RefCell<T>>;
365
366impl Default for Generator {
367    fn default() -> Self {
368        Self {
369            in_statement_containing_yield: Default::default(),
370            blocks: Default::default(),
371            block_offsets: Default::default(),
372            block_actions: Default::default(),
373            block_stack: Default::default(),
374            label_offsets: Default::default(),
375            label_exprs: Default::default(),
376            next_label_id: 1,
377            operations: Default::default(),
378            operation_args: Default::default(),
379            operation_locs: Default::default(),
380            state: private_ident!("_state"),
381            block_index: Default::default(),
382            label_number: Default::default(),
383            label_numbers: Default::default(),
384            last_operation_was_abrupt: Default::default(),
385            last_operation_was_completion: Default::default(),
386            clauses: Default::default(),
387            stmts: Default::default(),
388            exception_block_stack: Default::default(),
389            current_exception_block: Default::default(),
390            with_block_stack: Default::default(),
391            hoisted_vars: Default::default(),
392            hoisted_fns: Default::default(),
393        }
394    }
395}
396
397impl VisitMut for Generator {
398    noop_visit_mut_type!(fail);
399
400    fn visit_mut_arrow_expr(&mut self, e: &mut ArrowExpr) {
401        e.params.visit_mut_with(self);
402    }
403
404    fn visit_mut_function(&mut self, e: &mut Function) {
405        e.params.visit_mut_with(self);
406    }
407
408    fn visit_mut_getter_prop(&mut self, _: &mut GetterProp) {}
409
410    fn visit_mut_setter_prop(&mut self, e: &mut SetterProp) {
411        e.param.visit_mut_with(self);
412    }
413
414    fn visit_mut_expr(&mut self, e: &mut Expr) {
415        match e {
416            Expr::Yield(node) => {
417                // [source]
418                //      x = yield a();
419                //
420                // [intermediate]
421                //  .yield resumeLabel, (a())
422                //  .mark resumeLabel
423                //      x = %sent%;
424
425                let resume_label = self.define_label();
426                node.arg.visit_mut_with(self);
427                if node.delegate {
428                    let arg = node
429                        .arg
430                        .take()
431                        .map(|e| CallExpr {
432                            span: DUMMY_SP,
433                            callee: helper!(ts, ts_values),
434                            args: vec![e.as_arg()],
435                            ..Default::default()
436                        })
437                        .map(Expr::from)
438                        .map(Box::new);
439                    self.emit_yield_star(arg, Some(node.span))
440                } else {
441                    self.emit_yield(node.arg.take(), Some(node.span));
442                }
443
444                self.mark_label(resume_label);
445
446                *e = *self.create_generator_resume(Some(node.span));
447            }
448
449            Expr::Cond(node) => {
450                // [source]
451                //      x = a() ? yield : b();
452                //
453                // [intermediate]
454                //  .local _a
455                //  .brfalse whenFalseLabel, (a())
456                //  .yield resumeLabel
457                //  .mark resumeLabel
458                //      _a = %sent%;
459                //  .br resultLabel
460                //  .mark whenFalseLabel
461                //      _a = b();
462                //  .mark resultLabel
463                //      x = _a;
464
465                // We only need to perform a specific transformation if a
466                // `yield` expression exists in either the
467                // `whenTrue` or `whenFalse` branches. A `yield`
468                // in the condition will be handled by the normal visitor.
469
470                if contains_yield(&node.cons) || contains_yield(&node.alt) {
471                    let when_false_label = self.define_label();
472                    let result_label = self.define_label();
473                    let result_local = self.declare_local(None);
474
475                    node.test.visit_mut_with(self);
476                    let cond_span = node.test.span();
477                    self.emit_break_when_false(when_false_label, node.test.take(), Some(cond_span));
478
479                    let cons_span = node.cons.span();
480                    node.cons.visit_mut_with(self);
481                    self.emit_assignment(
482                        result_local.clone().into(),
483                        node.cons.take(),
484                        Some(cons_span),
485                    );
486                    self.emit_break(result_label, None);
487
488                    self.mark_label(when_false_label);
489                    let alt_span = node.cons.span();
490                    node.alt.visit_mut_with(self);
491                    self.emit_assignment(
492                        result_local.clone().into(),
493                        node.alt.take(),
494                        Some(alt_span),
495                    );
496
497                    self.mark_label(result_label);
498
499                    *e = result_local.into();
500                } else {
501                    node.visit_mut_with(self);
502                }
503            }
504
505            Expr::Bin(node) => {
506                if node.op == op!("**") {
507                    todo!("right-associative binary expression")
508                } else {
509                    let new = self.visit_left_associative_bin_expr(node);
510                    if let Some(new) = new {
511                        *e = new;
512                    }
513                }
514            }
515
516            Expr::Seq(node) => {
517                //     // flattened version of `visitCommaExpression`
518                let mut pending_expressions = Vec::new();
519
520                for mut elem in node.exprs.take() {
521                    if let Expr::Seq(mut elem) = *elem {
522                        elem.visit_mut_with(self);
523                        pending_expressions.extend(elem.exprs.take());
524                    } else {
525                        if contains_yield(&elem) && !pending_expressions.is_empty() {
526                            self.emit_worker(
527                                OpCode::Statement,
528                                Some(OpArgs::Stmt(Box::new(
529                                    ExprStmt {
530                                        span: DUMMY_SP,
531                                        expr: if pending_expressions.len() == 1 {
532                                            pending_expressions.remove(0)
533                                        } else {
534                                            SeqExpr {
535                                                span: DUMMY_SP,
536                                                exprs: pending_expressions.take(),
537                                            }
538                                            .into()
539                                        },
540                                    }
541                                    .into(),
542                                ))),
543                                None,
544                            );
545                        }
546                        elem.visit_mut_with(self);
547                        pending_expressions.push(elem);
548                    }
549                }
550
551                if pending_expressions.len() == 1 {
552                    *e = *pending_expressions.remove(0);
553                } else {
554                    node.exprs = pending_expressions;
555                }
556            }
557
558            Expr::Member(MemberExpr {
559                obj,
560                prop: MemberProp::Computed(prop),
561                ..
562            }) => {
563                if contains_yield(prop) {
564                    // [source]
565                    //      a = x[yield];
566                    //
567                    // [intermediate]
568                    //  .local _a
569                    //      _a = x;
570                    //  .yield resumeLabel
571                    //  .mark resumeLabel
572                    //      a = _a[%sent%]
573
574                    *obj = self.cache_expression(obj.take()).into();
575                    prop.visit_mut_with(self);
576                    return;
577                }
578
579                e.visit_mut_children_with(self);
580            }
581
582            Expr::Assign(node) if contains_yield(&node.right) => {
583                match node.left.as_mut_simple() {
584                    Some(SimpleAssignTarget::Member(left)) => {
585                        match &mut left.prop {
586                            MemberProp::Ident(..) | MemberProp::PrivateName(..) => {
587                                //      a.b = yield;
588                                //
589                                // [intermediate]
590                                //  .local _a
591                                //      _a = a;
592                                //  .yield resumeLabel
593                                //  .mark resumeLabel
594                                //      _a.b = %sent%;
595
596                                left.obj.visit_mut_with(self);
597                                let obj = self.cache_expression(left.obj.take());
598
599                                left.obj = obj.into();
600                            }
601                            MemberProp::Computed(prop) => {
602                                // [source]
603                                //      a[b] = yield;
604                                //
605                                // [intermediate]
606                                //  .local _a, _b
607                                //      _a = a;
608                                //      _b = b;
609                                //  .yield resumeLabel
610                                //  .mark resumeLabel
611                                //      _a[_b] = %sent%;
612                                let prop_span = prop.span;
613
614                                left.obj.visit_mut_with(self);
615                                let obj = self.cache_expression(left.obj.take());
616
617                                prop.visit_mut_with(self);
618                                let prop = self.cache_expression(prop.expr.take());
619
620                                left.obj = obj.into();
621                                left.prop = MemberProp::Computed(ComputedPropName {
622                                    span: prop_span,
623                                    expr: prop.into(),
624                                });
625                            }
626                            #[cfg(swc_ast_unknown)]
627                            _ => panic!("unable to access unknown nodes"),
628                        }
629                        // [source]
630                    }
631                    _ => {
632                        node.left.visit_mut_with(self);
633                    }
634                }
635                if node.op != op!("=") {
636                    let left_of_right =
637                        self.cache_expression(node.left.take().expect_simple().into());
638
639                    node.right.visit_mut_with(self);
640
641                    *e = AssignExpr {
642                        span: node.right.span(),
643                        op: node.op,
644                        left: left_of_right.into(),
645                        right: node.right.take(),
646                    }
647                    .into();
648                } else {
649                    node.right.visit_mut_with(self);
650                }
651            }
652
653            Expr::Object(node) if node.props.iter().any(contains_yield) => {
654                // [source]
655                //      o = {
656                //          a: 1,
657                //          b: yield,
658                //          c: 2
659                //      };
660                //
661                // [intermediate]
662                //  .local _a
663                //      _a = {
664                //          a: 1
665                //      };
666                //  .yield resumeLabel
667                //  .mark resumeLabel
668                //      o = (_a.b = %sent%,
669                //          _a.c = 2,
670                //          _a);
671
672                let num_initial_properties = self.count_initial_nodes_without_yield(&node.props);
673
674                let mut temp = self.declare_local(None);
675                node.props
676                    .iter_mut()
677                    .take(num_initial_properties)
678                    .for_each(|p| {
679                        p.visit_mut_with(self);
680                    });
681
682                self.emit_assignment(
683                    temp.clone().into(),
684                    ObjectLit {
685                        span: DUMMY_SP,
686                        props: node
687                            .props
688                            .iter_mut()
689                            .take(num_initial_properties)
690                            .map(|v| v.take())
691                            .collect(),
692                    }
693                    .into(),
694                    None,
695                );
696
697                let mut expressions = node
698                    .props
699                    .iter_mut()
700                    .skip(num_initial_properties)
701                    .map(|v| v.take())
702                    .fold(Vec::<CompiledProp>::new(), |mut props, p| {
703                        match p {
704                            PropOrSpread::Spread(_) => {
705                                unreachable!("spread should be removed before applying generator")
706                            }
707                            PropOrSpread::Prop(p) => match *p {
708                                Prop::Getter(p) => {
709                                    if let Some(CompiledProp::Accessor(g, _)) =
710                                        props.iter_mut().find(|prev| match prev {
711                                            CompiledProp::Accessor(_, Some(s)) => {
712                                                s.key.eq_ignore_span(&p.key)
713                                            }
714                                            _ => false,
715                                        })
716                                    {
717                                        *g = Some(p);
718                                    } else {
719                                        props.push(CompiledProp::Accessor(Some(p), None))
720                                    }
721                                }
722                                Prop::Setter(p) => {
723                                    if let Some(CompiledProp::Accessor(_, s)) =
724                                        props.iter_mut().find(|prev| match prev {
725                                            CompiledProp::Accessor(Some(prev), _) => {
726                                                prev.key.eq_ignore_span(&p.key)
727                                            }
728                                            _ => false,
729                                        })
730                                    {
731                                        *s = Some(p);
732                                    } else {
733                                        props.push(CompiledProp::Accessor(None, Some(p)))
734                                    }
735                                }
736                                p => {
737                                    props.push(CompiledProp::Prop(p));
738                                }
739                            },
740                            #[cfg(swc_ast_unknown)]
741                            _ => panic!("unable to access unknown nodes"),
742                        }
743
744                        props
745                    })
746                    .into_iter()
747                    .fold(Vec::new(), |exprs, property| {
748                        self.reduce_property(exprs, property, &mut temp)
749                    });
750
751                expressions.push(temp.into());
752
753                *e = *Expr::from_exprs(expressions);
754            }
755
756            Expr::Array(node) => {
757                *e = self.visit_elements(&mut node.elems, None, None);
758            }
759
760            _ => {
761                e.visit_mut_children_with(self);
762            }
763        }
764    }
765
766    fn visit_mut_call_expr(&mut self, node: &mut CallExpr) {
767        if !node.callee.is_import() && node.args.iter().any(contains_yield) {
768            // [source]
769            //      a.b(1, yield, 2);
770            //
771            // [intermediate]
772            //  .local _a, _b, _c
773            //      _b = (_a = a).b;
774            //      _c = [1];
775            //  .yield resumeLabel
776            //  .mark resumeLabel
777            //      _b.apply(_a, _c.concat([%sent%, 2]));
778
779            node.callee.visit_mut_with(self);
780
781            let (target, this_arg) =
782                self.create_call_binding(node.callee.take().expect_expr(), false);
783
784            let callee = self.cache_expression(target);
785
786            let mut args = node.args.take().into_iter().map(Some).collect::<Vec<_>>();
787            let arg = self.visit_elements(&mut args, None, None);
788
789            let apply = callee.make_member(quote_ident!("apply"));
790
791            *node = CallExpr {
792                span: node.span,
793                callee: apply.as_callee(),
794                args: once(this_arg.as_arg()).chain(once(arg.as_arg())).collect(),
795                ..Default::default()
796            };
797            return;
798        }
799
800        node.visit_mut_children_with(self);
801    }
802
803    fn visit_mut_new_expr(&mut self, node: &mut NewExpr) {
804        if contains_yield(&node.args) {
805            // [source]
806            //      new a.b(1, yield, 2);
807            //
808            // [intermediate]
809            //  .local _a, _b, _c
810            //      _b = (_a = a.b).bind;
811            //      _c = [1];
812            //  .yield resumeLabel
813            //  .mark resumeLabel
814            //      new (_b.apply(_a, _c.concat([%sent%, 2])));
815
816            node.callee.visit_mut_with(self);
817
818            let (target, this_arg) = self.create_call_binding(node.callee.take(), true);
819
820            let callee = self.cache_expression(target.make_member(quote_ident!("bind")).into());
821
822            let mut arg = if let Some(args) = node.args.take() {
823                let mut args = args.into_iter().map(Some).collect::<Vec<_>>();
824                Some(self.visit_elements(
825                    &mut args,
826                    Some(ExprOrSpread {
827                        spread: None,
828                        expr: Expr::undefined(DUMMY_SP),
829                    }),
830                    None,
831                ))
832            } else {
833                None
834            };
835
836            let apply = callee.apply(
837                node.span,
838                this_arg,
839                arg.take().map(|v| v.as_arg()).into_iter().collect(),
840            );
841
842            *node = NewExpr {
843                span: node.span,
844                callee: Box::new(apply),
845                args: None,
846                ..Default::default()
847            };
848            return;
849        }
850
851        node.visit_mut_children_with(self);
852    }
853
854    fn visit_mut_for_stmt(&mut self, node: &mut ForStmt) {
855        if self.in_statement_containing_yield {
856            self.begin_script_loop_block();
857        }
858
859        if let Some(VarDeclOrExpr::VarDecl(initializer)) = &mut node.init {
860            for variable in initializer.decls.iter_mut() {
861                self.hoist_variable_declaration(&Ident::from(variable.name.as_ident().unwrap()));
862            }
863
864            let variables = self.get_initialized_variables(initializer);
865
866            let mut exprs = variables
867                .into_iter()
868                .filter_map(|v| self.transform_initialized_variable(v.take()))
869                .map(Expr::from)
870                .map(Box::new)
871                .collect::<Vec<_>>();
872            node.init = if exprs.is_empty() {
873                None
874            } else {
875                Some(VarDeclOrExpr::Expr(if exprs.len() == 1 {
876                    exprs.remove(0)
877                } else {
878                    SeqExpr {
879                        span: DUMMY_SP,
880                        exprs,
881                    }
882                    .into()
883                }))
884            };
885            node.test.visit_mut_with(self);
886            node.update.visit_mut_with(self);
887            node.body.visit_mut_with(self);
888        } else {
889            node.visit_mut_children_with(self);
890        }
891
892        if self.in_statement_containing_yield {
893            self.end_loop_block();
894        }
895    }
896
897    fn visit_mut_do_while_stmt(&mut self, node: &mut DoWhileStmt) {
898        if self.in_statement_containing_yield {
899            self.begin_script_loop_block();
900            node.visit_mut_children_with(self);
901            self.end_loop_block();
902        } else {
903            node.visit_mut_children_with(self);
904        }
905    }
906
907    fn visit_mut_while_stmt(&mut self, node: &mut WhileStmt) {
908        if self.in_statement_containing_yield {
909            self.begin_script_loop_block();
910            node.visit_mut_children_with(self);
911            self.end_loop_block();
912        } else {
913            node.visit_mut_children_with(self);
914        }
915    }
916
917    fn visit_mut_return_stmt(&mut self, node: &mut ReturnStmt) {
918        node.arg.visit_mut_with(self);
919
920        *node = self.create_inline_return(node.arg.take(), Some(node.span));
921    }
922
923    fn visit_mut_switch_stmt(&mut self, node: &mut SwitchStmt) {
924        if self.in_statement_containing_yield {
925            self.begin_script_switch_block();
926        }
927
928        node.visit_mut_children_with(self);
929
930        if self.in_statement_containing_yield {
931            self.end_switch_block();
932        }
933    }
934
935    fn visit_mut_labeled_stmt(&mut self, node: &mut LabeledStmt) {
936        if self.in_statement_containing_yield {
937            self.begin_script_labeled_block(node.label.sym.clone());
938        }
939
940        node.visit_mut_children_with(self);
941
942        if self.in_statement_containing_yield {
943            self.end_labeled_block();
944        }
945    }
946
947    fn visit_mut_for_in_stmt(&mut self, node: &mut ForInStmt) {
948        // [source]
949        //      for (var x in a) {
950        //          /*body*/
951        //      }
952        //
953        // [intermediate]
954        //  .local x
955        //  .loop
956        //      for (x in a) {
957        //          /*body*/
958        //      }
959        //  .endloop
960
961        if self.in_statement_containing_yield {
962            self.begin_script_loop_block();
963        }
964
965        if let ForHead::VarDecl(initializer) = &mut node.left {
966            for variable in &initializer.decls {
967                self.hoist_variable_declaration(&Ident::from(variable.name.as_ident().unwrap()));
968            }
969
970            node.right.visit_mut_with(self);
971            node.body.visit_mut_with(self);
972        } else {
973            node.visit_mut_children_with(self);
974        }
975
976        if self.in_statement_containing_yield {
977            self.end_loop_block();
978        }
979    }
980
981    #[tracing::instrument(level = "debug", skip_all)]
982    fn visit_mut_stmt(&mut self, node: &mut Stmt) {
983        match node {
984            Stmt::Break(b) => {
985                if self.in_statement_containing_yield {
986                    let label = self.find_break_target(b.label.as_ref().map(|l| l.sym.clone()));
987                    if label.0 > 0 {
988                        *node = self.create_inline_break(label, Some(b.span)).into();
989                        return;
990                    }
991                }
992
993                node.visit_mut_children_with(self);
994            }
995            Stmt::Continue(s) => {
996                if self.in_statement_containing_yield {
997                    let label = self.find_continue_target(s.label.as_ref().map(|l| l.sym.clone()));
998                    if label.0 > 0 {
999                        *node = self.create_inline_break(label, Some(s.span)).into();
1000                        return;
1001                    }
1002                }
1003
1004                node.visit_mut_children_with(self);
1005            }
1006
1007            Stmt::Decl(Decl::Var(v)) => {
1008                if contains_yield(&*v) {
1009                    self.transform_and_emit_var_decl_list(v.take());
1010                    node.take();
1011                    return;
1012                }
1013
1014                // // Do not hoist custom prologues.
1015                // if (getEmitFlags(node) & EmitFlags.CustomPrologue) {
1016                //     return node;
1017                // }
1018
1019                for decl in v.decls.iter() {
1020                    self.hoist_variable_declaration(&Ident::from(decl.name.as_ident().unwrap()));
1021                }
1022
1023                let variables = self.get_initialized_variables(v);
1024                if variables.is_empty() {
1025                    node.take();
1026                    return;
1027                }
1028
1029                let mut exprs = variables
1030                    .into_iter()
1031                    .filter_map(|v| self.transform_initialized_variable(v.take()))
1032                    .map(Expr::from)
1033                    .map(Box::new)
1034                    .collect::<Vec<_>>();
1035
1036                if exprs.is_empty() {
1037                    node.take();
1038                    return;
1039                }
1040
1041                *node = ExprStmt {
1042                    span: v.span,
1043                    expr: if exprs.len() == 1 {
1044                        exprs.remove(0)
1045                    } else {
1046                        SeqExpr {
1047                            span: DUMMY_SP,
1048                            exprs,
1049                        }
1050                        .into()
1051                    },
1052                }
1053                .into();
1054            }
1055            Stmt::Decl(Decl::Fn(f)) => {
1056                self.hoisted_fns.push(f.take());
1057                node.take();
1058            }
1059            _ => {
1060                node.visit_mut_children_with(self);
1061            }
1062        }
1063    }
1064}
1065
1066enum CompiledProp {
1067    Prop(Prop),
1068    Accessor(Option<GetterProp>, Option<SetterProp>),
1069}
1070
1071impl Generator {
1072    fn visit_elements(
1073        &mut self,
1074        elements: &mut [Option<ExprOrSpread>],
1075        mut leading_element: Option<ExprOrSpread>,
1076        _loc: Option<Span>,
1077    ) -> Expr {
1078        // [source]
1079        //      ar = [1, yield, 2];
1080        //
1081        // [intermediate]
1082        //  .local _a
1083        //      _a = [1];
1084        //  .yield resumeLabel
1085        //  .mark resumeLabel
1086        //      ar = _a.concat([%sent%, 2]);
1087
1088        let num_initial_elements = self.count_initial_nodes_without_yield(elements);
1089
1090        let mut temp = None;
1091        if num_initial_elements > 0 {
1092            temp = Some(self.declare_local(None));
1093
1094            elements[0..num_initial_elements]
1095                .iter_mut()
1096                .for_each(|e| e.visit_mut_with(self));
1097
1098            self.emit_assignment(
1099                temp.clone().unwrap().into(),
1100                ArrayLit {
1101                    span: DUMMY_SP,
1102                    elems: leading_element
1103                        .take()
1104                        .into_iter()
1105                        .map(Some)
1106                        .chain(
1107                            elements
1108                                .iter_mut()
1109                                .take(num_initial_elements)
1110                                .map(|e| e.take()),
1111                        )
1112                        .collect(),
1113                }
1114                .into(),
1115                None,
1116            );
1117        }
1118
1119        let mut expressions = elements
1120            .iter_mut()
1121            .skip(num_initial_elements)
1122            .map(|v| v.take())
1123            .fold(Vec::new(), |exprs, element| {
1124                self.reduce_element(exprs, element, &mut leading_element, &mut temp)
1125            });
1126
1127        if let Some(temp) = temp {
1128            CallExpr {
1129                span: DUMMY_SP,
1130                callee: temp.make_member(quote_ident!("concat")).as_callee(),
1131                args: vec![ExprOrSpread {
1132                    spread: None,
1133                    expr: Box::new(Expr::Array(ArrayLit {
1134                        span: DUMMY_SP,
1135                        elems: expressions
1136                            .take()
1137                            .into_iter()
1138                            .map(|expr| match expr {
1139                                Some(expr_or_spread) => match &*expr_or_spread.expr {
1140                                    Expr::Invalid(_) => None,
1141                                    _ => Some(expr_or_spread),
1142                                },
1143                                None => None,
1144                            })
1145                            .collect(),
1146                    })),
1147                }],
1148                ..Default::default()
1149            }
1150            .into()
1151        } else {
1152            ArrayLit {
1153                span: DUMMY_SP,
1154                elems: leading_element
1155                    .take()
1156                    .into_iter()
1157                    .map(Some)
1158                    .chain(expressions.take().into_iter().map(|expr| match expr {
1159                        Some(expr_or_spread) => match &*expr_or_spread.expr {
1160                            Expr::Invalid(_) => None,
1161                            _ => Some(expr_or_spread),
1162                        },
1163                        None => None,
1164                    }))
1165                    .collect(),
1166            }
1167            .into()
1168        }
1169    }
1170
1171    fn reduce_element(
1172        &mut self,
1173        mut expressions: Vec<Option<ExprOrSpread>>,
1174        mut element: Option<ExprOrSpread>,
1175        leading_element: &mut Option<ExprOrSpread>,
1176        temp: &mut Option<Ident>,
1177    ) -> Vec<Option<ExprOrSpread>> {
1178        if contains_yield(&element) && !expressions.is_empty() {
1179            let has_assigned_temp = temp.is_some();
1180            if temp.is_none() {
1181                *temp = Some(self.declare_local(None));
1182            }
1183
1184            self.emit_assignment(
1185                temp.clone().unwrap().into(),
1186                if has_assigned_temp {
1187                    CallExpr {
1188                        span: DUMMY_SP,
1189                        callee: temp
1190                            .clone()
1191                            .unwrap()
1192                            .make_member(quote_ident!("concat"))
1193                            .as_callee(),
1194                        args: vec![Box::new(Expr::Array(ArrayLit {
1195                            span: DUMMY_SP,
1196                            elems: expressions
1197                                .take()
1198                                .into_iter()
1199                                .map(|expr| match expr {
1200                                    Some(expr_or_spread) => match &*expr_or_spread.expr {
1201                                        Expr::Invalid(_) => None,
1202                                        _ => Some(expr_or_spread),
1203                                    },
1204                                    None => None,
1205                                })
1206                                .collect(),
1207                        }))
1208                        .as_arg()],
1209                        ..Default::default()
1210                    }
1211                    .into()
1212                } else {
1213                    Box::new(
1214                        ArrayLit {
1215                            span: DUMMY_SP,
1216                            elems: leading_element
1217                                .take()
1218                                .into_iter()
1219                                .map(Some)
1220                                .chain(expressions.take().into_iter().map(|expr| match expr {
1221                                    Some(expr_or_spread) => match &*expr_or_spread.expr {
1222                                        Expr::Invalid(_) => None,
1223                                        _ => Some(expr_or_spread),
1224                                    },
1225                                    None => None,
1226                                }))
1227                                .collect(),
1228                        }
1229                        .into(),
1230                    )
1231                },
1232                None,
1233            );
1234            *leading_element = None;
1235        }
1236
1237        element.visit_mut_with(self);
1238        expressions.push(element);
1239        expressions
1240    }
1241
1242    fn reduce_property(
1243        &mut self,
1244        mut expressions: Vec<Box<Expr>>,
1245        property: CompiledProp,
1246        temp: &mut Ident,
1247    ) -> Vec<Box<Expr>> {
1248        if match &property {
1249            CompiledProp::Prop(p) => contains_yield(p),
1250            CompiledProp::Accessor(g, s) => {
1251                g.as_ref().is_some_and(contains_yield) || s.as_ref().is_some_and(contains_yield)
1252            }
1253        } && !expressions.is_empty()
1254        {
1255            self.emit_stmt(
1256                ExprStmt {
1257                    span: DUMMY_SP,
1258                    expr: Expr::from_exprs(expressions.take()),
1259                }
1260                .into(),
1261            );
1262        }
1263
1264        let mut expression: Expr = match property {
1265            CompiledProp::Prop(p) => match p {
1266                Prop::Shorthand(p) => AssignExpr {
1267                    span: p.span,
1268                    op: op!("="),
1269                    left: MemberExpr {
1270                        span: DUMMY_SP,
1271                        obj: temp.clone().into(),
1272                        prop: MemberProp::Ident(p.clone().into()),
1273                    }
1274                    .into(),
1275                    right: p.into(),
1276                }
1277                .into(),
1278                Prop::KeyValue(p) => AssignExpr {
1279                    span: DUMMY_SP,
1280                    op: op!("="),
1281                    left: MemberExpr {
1282                        span: DUMMY_SP,
1283                        obj: temp.clone().into(),
1284                        prop: p.key.into(),
1285                    }
1286                    .into(),
1287                    right: p.value,
1288                }
1289                .into(),
1290                Prop::Assign(_) => {
1291                    unreachable!("assignment property be removed before generator pass")
1292                }
1293                Prop::Getter(_) | Prop::Setter(_) => {
1294                    unreachable!("getter/setter property be compiled as CompiledProp::Accessor")
1295                }
1296                Prop::Method(p) => AssignExpr {
1297                    span: DUMMY_SP,
1298                    op: op!("="),
1299                    left: MemberExpr {
1300                        span: DUMMY_SP,
1301                        obj: temp.clone().into(),
1302                        prop: p.key.into(),
1303                    }
1304                    .into(),
1305                    right: p.function.into(),
1306                }
1307                .into(),
1308                #[cfg(swc_ast_unknown)]
1309                _ => panic!("unable to access unknown nodes"),
1310            },
1311            CompiledProp::Accessor(getter, setter) => {
1312                let key = getter
1313                    .as_ref()
1314                    .map(|v| v.key.clone())
1315                    .unwrap_or_else(|| setter.as_ref().unwrap().key.clone());
1316
1317                let desc = ObjectLit {
1318                    span: DUMMY_SP,
1319                    props: getter
1320                        .map(|g| KeyValueProp {
1321                            key: quote_ident!("get").into(),
1322                            value: Function {
1323                                params: Vec::new(),
1324                                span: g.span,
1325                                body: g.body,
1326                                is_generator: false,
1327                                is_async: false,
1328                                ..Default::default()
1329                            }
1330                            .into(),
1331                        })
1332                        .into_iter()
1333                        .chain(setter.map(|s| {
1334                            KeyValueProp {
1335                                key: quote_ident!("set").into(),
1336                                value: Function {
1337                                    params: vec![(*s.param).into()],
1338                                    span: s.span,
1339                                    body: s.body,
1340                                    is_generator: false,
1341                                    is_async: false,
1342                                    ..Default::default()
1343                                }
1344                                .into(),
1345                            }
1346                        }))
1347                        .map(Prop::KeyValue)
1348                        .map(Box::new)
1349                        .map(PropOrSpread::Prop)
1350                        .collect(),
1351                };
1352
1353                CallExpr {
1354                    span: DUMMY_SP,
1355                    callee: helper!(define_property),
1356                    args: vec![
1357                        temp.clone().as_arg(),
1358                        prop_name_to_expr_value(key).as_arg(),
1359                        desc.as_arg(),
1360                    ],
1361                    ..Default::default()
1362                }
1363                .into()
1364            }
1365        };
1366
1367        expression.visit_mut_with(self);
1368        if !expression.is_invalid() {
1369            expressions.push(Box::new(expression));
1370        }
1371        expressions
1372    }
1373
1374    fn visit_left_associative_bin_expr(&mut self, node: &mut BinExpr) -> Option<Expr> {
1375        if contains_yield(&node.right) {
1376            if matches!(node.op, op!("||") | op!("&&")) {
1377                return Some(self.visit_logical_bin_expr(node));
1378            }
1379
1380            // [source]
1381            //      a() + (yield) + c()
1382            //
1383            // [intermediate]
1384            //  .local _a
1385            //      _a = a();
1386            //  .yield resumeLabel
1387            //      _a + %sent% + c()
1388
1389            node.left.visit_mut_with(self);
1390            node.left = self.cache_expression(node.left.take()).into();
1391            node.right.visit_mut_with(self);
1392            return None;
1393        }
1394
1395        node.visit_mut_children_with(self);
1396        None
1397    }
1398
1399    fn visit_logical_bin_expr(&mut self, node: &mut BinExpr) -> Expr {
1400        // Logical binary expressions (`&&` and `||`) are shortcutting
1401        // expressions and need to be transformed as such:
1402        //
1403        // [source]
1404        //      x = a() && yield;
1405        //
1406        // [intermediate]
1407        //  .local _a
1408        //      _a = a();
1409        //  .brfalse resultLabel, (_a)
1410        //  .yield resumeLabel
1411        //  .mark resumeLabel
1412        //      _a = %sent%;
1413        //  .mark resultLabel
1414        //      x = _a;
1415        //
1416        // [source]
1417        //      x = a() || yield;
1418        //
1419        // [intermediate]
1420        //  .local _a
1421        //      _a = a();
1422        //  .brtrue resultLabel, (_a)
1423        //  .yield resumeLabel
1424        //  .mark resumeLabel
1425        //      _a = %sent%;
1426        //  .mark resultLabel
1427        //      x = _a;
1428
1429        let result_label = self.define_label();
1430        let result_local = self.declare_local(None);
1431
1432        let left_span = node.left.span();
1433        node.left.visit_mut_with(self);
1434        self.emit_assignment(
1435            result_local.clone().into(),
1436            node.left.take(),
1437            Some(left_span),
1438        );
1439
1440        if node.op == op!("&&") {
1441            // Logical `&&` shortcuts when the left-hand operand is falsey.
1442            self.emit_break_when_false(
1443                result_label,
1444                Box::new(result_local.clone().into()),
1445                Some(left_span),
1446            )
1447        } else {
1448            // Logical `||` shortcuts when the left-hand operand is truthy.
1449            self.emit_break_when_true(
1450                result_label,
1451                Box::new(result_local.clone().into()),
1452                Some(left_span),
1453            )
1454        }
1455
1456        let right_span = node.right.span();
1457        node.right.visit_mut_with(self);
1458        self.emit_assignment(
1459            result_local.clone().into(),
1460            node.right.take(),
1461            Some(right_span),
1462        );
1463        self.mark_label(result_label);
1464
1465        result_local.into()
1466    }
1467
1468    fn transform_and_emit_stmts(&mut self, stmts: Vec<Stmt>, start: usize) {
1469        for s in stmts.into_iter().skip(start) {
1470            self.transform_and_emit_stmt(s);
1471        }
1472    }
1473
1474    fn transform_and_emit_embedded_stmt(&mut self, node: Stmt) {
1475        if let Stmt::Block(block) = node {
1476            self.transform_and_emit_stmts(block.stmts, 0);
1477        } else {
1478            self.transform_and_emit_stmt(node);
1479        }
1480    }
1481
1482    fn transform_and_emit_stmt(&mut self, node: Stmt) {
1483        let _tracing = dev_span!("transform_and_emit_stmt");
1484
1485        let saved_in_statement_containing_yield = self.in_statement_containing_yield;
1486        if !self.in_statement_containing_yield {
1487            self.in_statement_containing_yield = contains_yield(&node);
1488        }
1489
1490        self.transform_and_emit_stmt_worker(node);
1491        self.in_statement_containing_yield = saved_in_statement_containing_yield;
1492    }
1493
1494    fn transform_and_emit_stmt_worker(&mut self, mut node: Stmt) {
1495        match node {
1496            Stmt::Block(s) => self.transform_and_emit_block(s),
1497            Stmt::Expr(s) => self.transform_and_emit_expr_stmt(s),
1498            Stmt::If(s) => self.transform_and_emit_if_stmt(s),
1499            Stmt::DoWhile(s) => self.transform_and_emit_do_stmt(s),
1500            Stmt::While(s) => self.transform_and_emit_while_stmt(s),
1501            Stmt::For(s) => self.transform_and_emit_for_stmt(s),
1502            Stmt::ForIn(s) => self.transform_and_emit_for_in_stmt(s),
1503            Stmt::Continue(s) => self.transform_and_emit_continue_stmt(s),
1504            Stmt::Break(s) => self.transform_and_emit_break_stmt(s),
1505            Stmt::Return(s) => self.transform_and_emit_return_stmt(s),
1506            Stmt::With(s) => self.transform_and_emit_with_stmt(s),
1507            Stmt::Switch(s) => self.transform_and_emit_switch_stmt(s),
1508            Stmt::Labeled(s) => self.transform_and_emit_labeled_stmt(s),
1509            Stmt::Throw(s) => self.transform_and_emit_throw_stmt(s),
1510            Stmt::Try(s) => self.transform_and_emit_try_stmt(*s),
1511            _ => {
1512                node.visit_mut_with(self);
1513
1514                self.emit_stmt(node);
1515            }
1516        }
1517    }
1518
1519    fn transform_and_emit_block(&mut self, mut node: BlockStmt) {
1520        if contains_yield(&node) {
1521            self.transform_and_emit_stmts(node.stmts, 0);
1522        } else {
1523            node.visit_mut_with(self);
1524            self.emit_stmt(node.into());
1525        }
1526    }
1527
1528    fn transform_and_emit_expr_stmt(&mut self, mut node: ExprStmt) {
1529        node.visit_mut_with(self);
1530
1531        self.emit_stmt(node.into());
1532    }
1533
1534    fn transform_and_emit_var_decl_list(&mut self, mut node: Box<VarDecl>) {
1535        for variable in &node.decls {
1536            self.hoist_variable_declaration(&Ident::from(variable.name.as_ident().unwrap()));
1537        }
1538
1539        let mut variables = self.get_initialized_variables(&mut node);
1540        let var_len = variables.len();
1541        let mut variables_written = 0;
1542        let mut pending_expressions = Vec::new();
1543        let mut cnt = 0;
1544
1545        while variables_written < var_len {
1546            #[cfg(debug_assertions)]
1547            debug!("variables_written: {} / {}", variables_written, var_len);
1548
1549            for (_i, variable) in variables.iter_mut().enumerate().skip(variables_written) {
1550                if contains_yield(&**variable) && cnt != 0 {
1551                    break;
1552                }
1553
1554                // We use cnt because variable.init can be None.
1555                let expr = self.transform_initialized_variable(variable.take());
1556
1557                pending_expressions.extend(expr.map(Expr::from).map(Box::new));
1558                cnt += 1;
1559            }
1560
1561            if cnt > 0 {
1562                variables_written += cnt;
1563                cnt = 0;
1564
1565                self.emit_stmt(
1566                    ExprStmt {
1567                        span: DUMMY_SP,
1568                        expr: if pending_expressions.len() == 1 {
1569                            pending_expressions.pop().unwrap()
1570                        } else {
1571                            SeqExpr {
1572                                span: DUMMY_SP,
1573                                exprs: take(&mut pending_expressions),
1574                            }
1575                            .into()
1576                        },
1577                    }
1578                    .into(),
1579                )
1580            }
1581        }
1582    }
1583
1584    fn transform_initialized_variable(&mut self, mut node: VarDeclarator) -> Option<AssignExpr> {
1585        node.init.visit_mut_with(self);
1586
1587        node.init.map(|right| AssignExpr {
1588            span: node.span,
1589            op: op!("="),
1590            left: node.name.clone().try_into().unwrap(),
1591            right,
1592        })
1593    }
1594
1595    fn transform_and_emit_if_stmt(&mut self, mut node: IfStmt) {
1596        if contains_yield(&node) {
1597            // [source]
1598            //      if (x)
1599            //          /*thenStatement*/
1600            //      else
1601            //          /*elseStatement*/
1602            //
1603            // [intermediate]
1604            //  .brfalse elseLabel, (x)
1605            //      /*thenStatement*/
1606            //  .br endLabel
1607            //  .mark elseLabel
1608            //      /*elseStatement*/
1609            //  .mark endLabel
1610
1611            if contains_yield(&node.cons) || contains_yield(&node.alt) {
1612                let end_label = self.define_label();
1613                let else_label = node.alt.as_ref().map(|_| self.define_label());
1614
1615                node.test.visit_mut_with(self);
1616                let span = node.test.span();
1617                self.emit_break_when_false(else_label.unwrap_or(end_label), node.test, Some(span));
1618
1619                self.transform_and_emit_embedded_stmt(*node.cons);
1620
1621                if let Some(alt) = node.alt {
1622                    self.emit_break(end_label, None);
1623                    self.mark_label(else_label.unwrap());
1624                    self.transform_and_emit_embedded_stmt(*alt);
1625                }
1626                self.mark_label(end_label);
1627            } else {
1628                node.visit_mut_with(self);
1629                self.emit_stmt(node.into());
1630            }
1631        } else {
1632            node.visit_mut_with(self);
1633            self.emit_stmt(node.into());
1634        }
1635    }
1636
1637    fn transform_and_emit_do_stmt(&mut self, mut node: DoWhileStmt) {
1638        if contains_yield(&node) {
1639            // [source]
1640            //      do {
1641            //          /*body*/
1642            //      }
1643            //      while (i < 10);
1644            //
1645            // [intermediate]
1646            //  .loop conditionLabel, endLabel
1647            //  .mark loopLabel
1648            //      /*body*/
1649            //  .mark conditionLabel
1650            //  .brtrue loopLabel, (i < 10)
1651            //  .endloop
1652            //  .mark endLabel
1653
1654            let condition_label = self.define_label();
1655            let loop_label = self.define_label();
1656
1657            self.begin_loop_block(condition_label);
1658            self.mark_label(loop_label);
1659            self.transform_and_emit_embedded_stmt(*node.body);
1660            self.mark_label(condition_label);
1661            node.test.visit_mut_with(self);
1662            let span = node.test.span();
1663            self.emit_break_when_true(loop_label, node.test, Some(span));
1664            self.end_loop_block();
1665        } else {
1666            node.visit_mut_with(self);
1667            self.emit_stmt(node.into());
1668        }
1669    }
1670
1671    fn transform_and_emit_while_stmt(&mut self, mut node: WhileStmt) {
1672        let _tracing = dev_span!("transform_and_emit_while_stmt");
1673
1674        if contains_yield(&node) {
1675            // [source]
1676            //      while (i < 10) {
1677            //          /*body*/
1678            //      }
1679            //
1680            // [intermediate]
1681            //  .loop loopLabel, endLabel
1682            //  .mark loopLabel
1683            //  .brfalse endLabel, (i < 10)
1684            //      /*body*/
1685            //  .br loopLabel
1686            //  .endloop
1687            //  .mark endLabel
1688
1689            let loop_label = self.define_label();
1690            let end_label = self.begin_loop_block(loop_label);
1691            self.mark_label(loop_label);
1692            node.test.visit_mut_with(self);
1693            self.emit_break_when_false(end_label, node.test, None);
1694            self.transform_and_emit_embedded_stmt(*node.body);
1695            self.emit_break(loop_label, None);
1696            self.end_loop_block();
1697        } else {
1698            node.visit_mut_with(self);
1699
1700            self.emit_stmt(node.into());
1701        }
1702    }
1703
1704    fn transform_and_emit_for_stmt(&mut self, mut node: ForStmt) {
1705        if contains_yield(&node) {
1706            // [source]
1707            //      for (var i = 0; i < 10; i++) {
1708            //          /*body*/
1709            //      }
1710            //
1711            // [intermediate]
1712            //  .local i
1713            //      i = 0;
1714            //  .loop incrementLabel, endLoopLabel
1715            //  .mark conditionLabel
1716            //  .brfalse endLoopLabel, (i < 10)
1717            //      /*body*/
1718            //  .mark incrementLabel
1719            //      i++;
1720            //  .br conditionLabel
1721            //  .endloop
1722            //  .mark endLoopLabel
1723
1724            let condition_label = self.define_label();
1725            let increment_label = self.define_label();
1726            let end_label = self.begin_loop_block(increment_label);
1727
1728            if let Some(init) = node.init {
1729                match init {
1730                    VarDeclOrExpr::VarDecl(init) => {
1731                        self.transform_and_emit_var_decl_list(init);
1732                    }
1733                    VarDeclOrExpr::Expr(mut init) => {
1734                        init.visit_mut_with(self);
1735                        self.emit_stmt(
1736                            ExprStmt {
1737                                span: init.span(),
1738                                expr: init,
1739                            }
1740                            .into(),
1741                        );
1742                    }
1743                    #[cfg(swc_ast_unknown)]
1744                    _ => panic!("unable to access unknown nodes"),
1745                }
1746            }
1747
1748            self.mark_label(condition_label);
1749
1750            if let Some(mut cond) = node.test {
1751                cond.visit_mut_with(self);
1752                self.emit_break_when_false(end_label, cond, None);
1753            }
1754
1755            self.transform_and_emit_embedded_stmt(*node.body);
1756
1757            self.mark_label(increment_label);
1758
1759            if let Some(mut incrementor) = node.update {
1760                incrementor.visit_mut_with(self);
1761
1762                self.emit_stmt(
1763                    ExprStmt {
1764                        span: incrementor.span(),
1765                        expr: incrementor,
1766                    }
1767                    .into(),
1768                );
1769            }
1770
1771            self.emit_break(condition_label, None);
1772            self.end_loop_block();
1773        } else {
1774            node.visit_mut_with(self);
1775            self.emit_stmt(node.into());
1776        }
1777    }
1778
1779    fn transform_and_emit_for_in_stmt(&mut self, mut node: ForInStmt) {
1780        if contains_yield(&node) {
1781            // [source]
1782            //      for (var p in o) {
1783            //          /*body*/
1784            //      }
1785            //
1786            // [intermediate]
1787            //  .local _a, _b, _i
1788            //      _a = [];
1789            //      for (_b in o) _a.push(_b);
1790            //      _i = 0;
1791            //  .loop incrementLabel, endLoopLabel
1792            //  .mark conditionLabel
1793            //  .brfalse endLoopLabel, (_i < _a.length)
1794            //      p = _a[_i];
1795            //      /*body*/
1796            //  .mark incrementLabel
1797            //      _b++;
1798            //  .br conditionLabel
1799            //  .endloop
1800            //  .mark endLoopLabel
1801
1802            let keys_array = self.declare_local(None);
1803            let key = self.declare_local(None);
1804            let keys_index = private_ident!("_i");
1805
1806            self.hoist_variable_declaration(&keys_index);
1807
1808            self.emit_assignment(
1809                keys_array.clone().into(),
1810                Box::new(ArrayLit::dummy().into()),
1811                None,
1812            );
1813
1814            node.right.visit_mut_with(self);
1815            self.emit_stmt(
1816                ForInStmt {
1817                    span: DUMMY_SP,
1818                    left: ForHead::Pat(key.clone().into()),
1819                    right: node.right.take(),
1820                    body: Box::new(Stmt::Expr(ExprStmt {
1821                        span: DUMMY_SP,
1822                        expr: CallExpr {
1823                            span: DUMMY_SP,
1824                            callee: keys_array
1825                                .clone()
1826                                .make_member(quote_ident!("push"))
1827                                .as_callee(),
1828                            args: vec![key.as_arg()],
1829                            ..Default::default()
1830                        }
1831                        .into(),
1832                    })),
1833                }
1834                .into(),
1835            );
1836
1837            self.emit_assignment(keys_index.clone().into(), 0.into(), None);
1838
1839            let condition_label = self.define_label();
1840            let increment_label = self.define_label();
1841            let end_label = self.begin_loop_block(increment_label);
1842
1843            self.mark_label(condition_label);
1844            self.emit_break_when_false(
1845                end_label,
1846                Box::new(keys_index.clone().make_bin(
1847                    op!("<"),
1848                    keys_array.clone().make_member(quote_ident!("length")),
1849                )),
1850                None,
1851            );
1852
1853            let variable = match node.left {
1854                ForHead::VarDecl(initializer) => {
1855                    for variable in initializer.decls.iter() {
1856                        self.hoist_variable_declaration(&Ident::from(
1857                            variable.name.as_ident().unwrap(),
1858                        ));
1859                    }
1860
1861                    initializer.decls[0].name.clone()
1862                }
1863                ForHead::Pat(mut initializer) => {
1864                    initializer.visit_mut_with(self);
1865                    *initializer
1866                }
1867
1868                ForHead::UsingDecl(..) => {
1869                    unreachable!("using declaration must be removed by previous pass")
1870                }
1871
1872                #[cfg(swc_ast_unknown)]
1873                _ => panic!("unable to access unknown nodes"),
1874            };
1875            self.emit_assignment(
1876                variable.try_into().unwrap(),
1877                MemberExpr {
1878                    span: DUMMY_SP,
1879                    obj: Box::new(keys_array.into()),
1880                    prop: MemberProp::Computed(ComputedPropName {
1881                        span: DUMMY_SP,
1882                        expr: Box::new(keys_index.clone().into()),
1883                    }),
1884                }
1885                .into(),
1886                None,
1887            );
1888            self.transform_and_emit_embedded_stmt(*node.body);
1889
1890            self.mark_label(increment_label);
1891            self.emit_stmt(
1892                ExprStmt {
1893                    span: DUMMY_SP,
1894                    expr: UpdateExpr {
1895                        span: DUMMY_SP,
1896                        prefix: false,
1897                        op: op!("++"),
1898                        arg: Box::new(keys_index.clone().into()),
1899                    }
1900                    .into(),
1901                }
1902                .into(),
1903            );
1904
1905            self.emit_break(condition_label, None);
1906            self.end_loop_block();
1907        } else {
1908            node.visit_mut_with(self);
1909            self.emit_stmt(node.into());
1910        }
1911    }
1912
1913    fn transform_and_emit_continue_stmt(&mut self, node: ContinueStmt) {
1914        let label = self.find_continue_target(node.label.as_ref().map(|l| l.sym.clone()));
1915        if label.0 > 0 {
1916            self.emit_break(label, Some(node.span));
1917        } else {
1918            // invalid continue without a containing loop. Leave the node as is,
1919            // per #17875.
1920            self.emit_stmt(node.into())
1921        }
1922    }
1923
1924    fn transform_and_emit_break_stmt(&mut self, node: BreakStmt) {
1925        let label = self.find_break_target(node.label.as_ref().map(|l| l.sym.clone()));
1926        if label.0 > 0 {
1927            self.emit_break(label, Some(node.span));
1928        } else {
1929            // invalid break without a containing loop. Leave the node as is,
1930            // per #17875.
1931            self.emit_stmt(node.into())
1932        }
1933    }
1934
1935    fn transform_and_emit_return_stmt(&mut self, mut s: ReturnStmt) {
1936        s.arg.visit_mut_with(self);
1937        self.emit_return(s.arg, Some(s.span))
1938    }
1939
1940    fn transform_and_emit_with_stmt(&mut self, mut node: WithStmt) {
1941        if contains_yield(&node) {
1942            // [source]
1943            //      with (x) {
1944            //          /*body*/
1945            //      }
1946            //
1947            // [intermediate]
1948            //  .with (x)
1949            //      /*body*/
1950            //  .endwith
1951
1952            node.obj.visit_mut_with(self);
1953            let obj = self.cache_expression(node.obj);
1954            self.begin_with_block(obj);
1955            self.transform_and_emit_embedded_stmt(*node.body);
1956            self.end_with_block();
1957        } else {
1958            node.visit_mut_with(self);
1959            self.emit_stmt(node.into());
1960        }
1961    }
1962
1963    fn transform_and_emit_switch_stmt(&mut self, mut node: SwitchStmt) {
1964        if contains_yield(&node.cases) {
1965            // [source]
1966            //      switch (x) {
1967            //          case a:
1968            //              /*caseStatements*/
1969            //          case b:
1970            //              /*caseStatements*/
1971            //          default:
1972            //              /*defaultStatements*/
1973            //      }
1974            //
1975            // [intermediate]
1976            //  .local _a
1977            //  .switch endLabel
1978            //      _a = x;
1979            //      switch (_a) {
1980            //          case a:
1981            //  .br clauseLabels[0]
1982            //      }
1983            //      switch (_a) {
1984            //          case b:
1985            //  .br clauseLabels[1]
1986            //      }
1987            //  .br clauseLabels[2]
1988            //  .mark clauseLabels[0]
1989            //      /*caseStatements*/
1990            //  .mark clauseLabels[1]
1991            //      /*caseStatements*/
1992            //  .mark clauseLabels[2]
1993            //      /*caseStatements*/
1994            //  .endswitch
1995            //  .mark endLabel
1996
1997            let end_label = self.begin_switch_block();
1998            node.discriminant.visit_mut_with(self);
1999            let expression = self.cache_expression(node.discriminant);
2000
2001            // Create labels for each clause and find the index of the first
2002            // default clause.
2003
2004            let mut clause_labels = Vec::new();
2005            let mut default_clause_index = -1i32;
2006
2007            for (i, clause) in node.cases.iter().enumerate() {
2008                clause_labels.push(self.define_label());
2009                if clause.test.is_none() && default_clause_index == -1 {
2010                    default_clause_index = i as _;
2011                }
2012            }
2013
2014            // Emit switch statements for each run of case clauses either from
2015            // the first case clause or the next case clause with a
2016            // `yield` in its expression, up to the next case clause
2017            // with a `yield` in its expression.
2018            let mut clauses_written = 0;
2019            let mut pending_clauses = Vec::new();
2020
2021            while clauses_written < node.cases.len() {
2022                #[cfg(debug_assertions)]
2023                debug!("clauses_written: {}", clauses_written);
2024
2025                let mut default_clauses_skipped = 0;
2026
2027                for (i, clause) in node.cases.iter_mut().enumerate().skip(clauses_written) {
2028                    if clause.test.is_some() {
2029                        if contains_yield(&clause.test) && !pending_clauses.is_empty() {
2030                            break;
2031                        }
2032
2033                        clause.test.visit_mut_with(self);
2034                        let span = clause.test.span();
2035                        pending_clauses.push(SwitchCase {
2036                            span: DUMMY_SP,
2037                            test: clause.test.take(),
2038                            cons: vec![self
2039                                .create_inline_break(clause_labels[i], Some(span))
2040                                .into()],
2041                        })
2042                    } else {
2043                        default_clauses_skipped += 1;
2044                    }
2045                }
2046
2047                if !pending_clauses.is_empty() {
2048                    clauses_written += pending_clauses.len();
2049                    self.emit_stmt(
2050                        SwitchStmt {
2051                            span: DUMMY_SP,
2052                            discriminant: expression.clone().into(),
2053                            cases: take(&mut pending_clauses),
2054                        }
2055                        .into(),
2056                    );
2057                }
2058
2059                if default_clauses_skipped > 0 {
2060                    clauses_written += default_clauses_skipped;
2061                }
2062            }
2063
2064            if default_clause_index >= 0 {
2065                self.emit_break(clause_labels[default_clause_index as usize], None);
2066            } else {
2067                self.emit_break(end_label, None);
2068            }
2069
2070            for (i, clause) in node.cases.into_iter().enumerate() {
2071                self.mark_label(clause_labels[i]);
2072                self.transform_and_emit_stmts(clause.cons, 0);
2073            }
2074
2075            self.end_switch_block()
2076        } else {
2077            node.visit_mut_with(self);
2078            self.emit_stmt(node.into())
2079        }
2080    }
2081
2082    fn transform_and_emit_labeled_stmt(&mut self, mut node: LabeledStmt) {
2083        #[cfg(debug_assertions)]
2084        debug!("transform_and_emit_labeled_stmt: {:?}", node.label);
2085
2086        if contains_yield(&node) {
2087            // [source]
2088            //      x: {
2089            //          /*body*/
2090            //      }
2091            //
2092            // [intermediate]
2093            //  .labeled "x", endLabel
2094            //      /*body*/
2095            //  .endlabeled
2096            //  .mark endLabel
2097            self.begin_labeled_block(node.label.sym);
2098            self.transform_and_emit_embedded_stmt(*node.body);
2099            self.end_labeled_block();
2100        } else {
2101            node.visit_mut_with(self);
2102            self.emit_stmt(node.into());
2103        }
2104    }
2105
2106    fn transform_and_emit_throw_stmt(&mut self, mut node: ThrowStmt) {
2107        node.arg.visit_mut_with(self);
2108        self.emit_throw(node.arg, Some(node.span))
2109    }
2110
2111    fn transform_and_emit_try_stmt(&mut self, mut node: TryStmt) {
2112        let _tracing = dev_span!("transform_and_emit_try_stmt");
2113
2114        if contains_yield(&node) {
2115            // [source]
2116            //      try {
2117            //          /*tryBlock*/
2118            //      }
2119            //      catch (e) {
2120            //          /*catchBlock*/
2121            //      }
2122            //      finally {
2123            //          /*finallyBlock*/
2124            //      }
2125            //
2126            // [intermediate]
2127            //  .local _a
2128            //  .try tryLabel, catchLabel, finallyLabel, endLabel
2129            //  .mark tryLabel
2130            //  .nop
2131            //      /*tryBlock*/
2132            //  .br endLabel
2133            //  .catch
2134            //  .mark catchLabel
2135            //      _a = %error%;
2136            //      /*catchBlock*/
2137            //  .br endLabel
2138            //  .finally
2139            //  .mark finallyLabel
2140            //      /*finallyBlock*/
2141            //  .endfinally
2142            //  .endtry
2143            //  .mark endLabel
2144
2145            self.begin_exception_block();
2146            self.transform_and_emit_embedded_stmt(node.block.into());
2147            if let Some(catch) = node.handler {
2148                self.begin_catch_block(VarDeclarator {
2149                    name: catch.param.clone().unwrap(),
2150                    ..Take::dummy()
2151                });
2152                self.transform_and_emit_embedded_stmt(catch.body.into());
2153            }
2154
2155            if let Some(finalizer) = node.finalizer {
2156                self.begin_finally_block();
2157                self.transform_and_emit_embedded_stmt(finalizer.into());
2158            }
2159
2160            self.end_exception_block();
2161        } else {
2162            node.visit_mut_with(self);
2163            self.emit_stmt(node.into());
2164        }
2165    }
2166
2167    fn count_initial_nodes_without_yield<N>(&self, nodes: &[N]) -> usize
2168    where
2169        N: VisitWith<YieldFinder>,
2170    {
2171        for (i, node) in nodes.iter().enumerate() {
2172            if contains_yield(node) {
2173                return i;
2174            }
2175        }
2176
2177        0
2178    }
2179
2180    fn cache_expression(&mut self, node: Box<Expr>) -> Ident {
2181        match *node {
2182            Expr::Ident(i) => i,
2183            _ => {
2184                let span = node.span();
2185
2186                let temp = self.create_temp_variable();
2187                self.emit_assignment(temp.clone().into(), node, Some(span));
2188                temp
2189            }
2190        }
2191    }
2192
2193    fn declare_local(&mut self, name: Option<Atom>) -> Ident {
2194        let temp = name
2195            .map(|name| private_ident!(name))
2196            .unwrap_or_else(|| private_ident!("_tmp"));
2197
2198        self.hoist_variable_declaration(&temp);
2199        temp
2200    }
2201
2202    /// Defines a label, uses as the target of a Break operation.
2203    fn define_label(&mut self) -> Label {
2204        if self.label_offsets.is_none() {
2205            self.label_offsets = Some(Default::default());
2206        }
2207
2208        let label = Label(self.next_label_id as _);
2209        self.next_label_id += 1;
2210        #[cfg(debug_assertions)]
2211        debug!("define_label: {:?}", label);
2212
2213        if label.0 as usize >= self.label_offsets.as_ref().unwrap().len() {
2214            self.label_offsets
2215                .as_mut()
2216                .unwrap()
2217                .resize(label.0 as usize + 1, 0);
2218        }
2219        self.label_offsets.as_mut().unwrap()[label.0 as usize] = -1;
2220        label
2221    }
2222
2223    /// Marks the current operation with the specified label.
2224    fn mark_label(&mut self, label: Label) {
2225        debug_assert!(self.label_offsets.is_some(), "No labels were defined.");
2226
2227        if label.0 as usize >= self.label_offsets.as_ref().unwrap().len() {
2228            self.label_offsets
2229                .as_mut()
2230                .unwrap()
2231                .resize(label.0 as usize + 1, Default::default());
2232        }
2233
2234        self.label_offsets.as_mut().unwrap()[label.0 as usize] =
2235            self.operations.as_deref().map_or(0, |v| v.len() as _);
2236
2237        #[cfg(debug_assertions)]
2238        debug!(
2239            "mark_label: {:?}; offset: {}",
2240            label,
2241            self.label_offsets.as_mut().unwrap()[label.0 as usize]
2242        );
2243    }
2244
2245    //// Begins a block operation (With, Break/Continue, Try/Catch/Finally)
2246    ///
2247    /// - `block`: Information about the block.
2248    fn begin_block(&mut self, block: CodeBlock) -> Ptr<CodeBlock> {
2249        if self.blocks.is_none() {
2250            self.blocks = Some(Default::default());
2251            self.block_actions = Some(Default::default());
2252            self.block_offsets = Some(Default::default());
2253            self.block_stack = Some(Default::default());
2254        }
2255
2256        #[cfg(debug_assertions)]
2257        let index = self.block_actions.as_ref().unwrap().len();
2258
2259        #[cfg(debug_assertions)]
2260        if cfg!(debug_assertions) {
2261            debug!("Begin block {}: {:?}", index, block);
2262        }
2263
2264        let block = Rc::new(RefCell::new(block));
2265
2266        self.block_actions.as_mut().unwrap().push(BlockAction::Open);
2267        self.block_offsets
2268            .as_mut()
2269            .unwrap()
2270            .push(self.operations.as_ref().map_or(0, |v| v.len()));
2271        self.blocks.as_mut().unwrap().push(block.clone());
2272        self.block_stack.as_mut().unwrap().push(block.clone());
2273
2274        block
2275    }
2276
2277    /// Ends the current block operation.
2278    fn end_block(&mut self) -> Ptr<CodeBlock> {
2279        let block = self.peek_block().expect("beginBlock was never called.");
2280
2281        #[cfg(debug_assertions)]
2282        let index = self.block_actions.as_ref().unwrap().len();
2283
2284        #[cfg(debug_assertions)]
2285        debug!("End block {}", index);
2286
2287        self.block_actions
2288            .as_mut()
2289            .unwrap()
2290            .push(BlockAction::Close);
2291        self.block_offsets
2292            .as_mut()
2293            .unwrap()
2294            .push(self.operations.as_ref().map_or(0, |v| v.len()));
2295        self.blocks.as_mut().unwrap().push(block.clone());
2296        self.block_stack.as_mut().unwrap().pop();
2297        block
2298    }
2299
2300    /// Gets the current open block.
2301    fn peek_block(&self) -> Option<Ptr<CodeBlock>> {
2302        self.block_stack.as_ref().and_then(|v| v.last().cloned())
2303    }
2304
2305    /// Gets the kind of the current open block.
2306    fn peek_block_kind(&self) -> Option<CodeBlockKind> {
2307        self.peek_block().map(|b| match &*b.borrow() {
2308            CodeBlock::With(..) => CodeBlockKind::With,
2309            CodeBlock::Exception(..) => CodeBlockKind::Exception,
2310            CodeBlock::Labeled(..) => CodeBlockKind::Labeled,
2311            CodeBlock::Switch(..) => CodeBlockKind::Switch,
2312            CodeBlock::Loop(..) => CodeBlockKind::Loop,
2313        })
2314    }
2315
2316    /// Begins a code block for a generated `with` statement.
2317    ///
2318    /// - `expression`: An identifier representing expression for the `with`
2319    fn begin_with_block(&mut self, expr: Ident) {
2320        let start_label = self.define_label();
2321        let end_label = self.define_label();
2322        self.mark_label(start_label);
2323        self.begin_block(CodeBlock::With(WithBlock {
2324            expression: expr,
2325            start_label,
2326            end_label,
2327        }));
2328    }
2329
2330    /// Ends a code block for a generated `with` statement.
2331    fn end_with_block(&mut self) {
2332        debug_assert!(self.peek_block_kind() == Some(CodeBlockKind::With));
2333        let block = self.end_block();
2334        let b = block.borrow();
2335        if let CodeBlock::With(block) = &*b {
2336            self.mark_label(block.end_label);
2337        } else {
2338            unreachable!()
2339        }
2340    }
2341
2342    /// Begins a code block for a generated `try` statement.
2343    fn begin_exception_block(&mut self) -> Label {
2344        let _tracing = dev_span!("begin_exception_block");
2345
2346        let start_label = self.define_label();
2347        let end_label = self.define_label();
2348        self.mark_label(start_label);
2349        self.begin_block(CodeBlock::Exception(ExceptionBlock {
2350            state: ExceptionBlockState::Try,
2351            start_label,
2352            end_label,
2353            catch_variable: Default::default(),
2354            catch_label: Default::default(),
2355            finally_label: Default::default(),
2356        }));
2357        self.emit_nop();
2358        end_label
2359    }
2360
2361    /**
2362     * Enters the `catch` clause of a generated `try` statement.
2363     *
2364     * @param variable The catch variable.
2365     */
2366    fn begin_catch_block(&mut self, variable: VarDeclarator) {
2367        debug_assert!(self.peek_block_kind() == Some(CodeBlockKind::Exception));
2368
2369        let name = variable.name.expect_ident().into();
2370        self.hoist_variable_declaration(&name);
2371
2372        // ExceptionBlock
2373        let peeked = self.peek_block().unwrap();
2374        let exception = peeked.borrow_mut();
2375        let mut exception = RefMut::map(exception, |v| match v {
2376            CodeBlock::Exception(v) => v,
2377            _ => unreachable!(),
2378        });
2379        debug_assert!(exception.state < ExceptionBlockState::Catch);
2380
2381        let end_label = exception.end_label;
2382        self.emit_break(end_label, None);
2383
2384        let catch_label = self.define_label();
2385        self.mark_label(catch_label);
2386        exception.state = ExceptionBlockState::Catch;
2387        exception.catch_variable = Some(name.clone());
2388        exception.catch_label = Some(catch_label);
2389
2390        self.emit_assignment(
2391            name.clone().into(),
2392            CallExpr {
2393                span: DUMMY_SP,
2394                callee: self
2395                    .state
2396                    .clone()
2397                    .make_member(quote_ident!("sent"))
2398                    .as_callee(),
2399                args: Vec::new(),
2400                ..Default::default()
2401            }
2402            .into(),
2403            None,
2404        );
2405
2406        self.emit_nop();
2407    }
2408
2409    /// Enters the `finally` block of a generated `try` statement.
2410    fn begin_finally_block(&mut self) {
2411        let _tracing = dev_span!("begin_finally_block");
2412
2413        debug_assert!(self.peek_block_kind() == Some(CodeBlockKind::Exception));
2414
2415        let block = self.peek_block().unwrap();
2416        let mut b = block.borrow_mut();
2417        if let CodeBlock::Exception(block) = &mut *b {
2418            debug_assert!(block.state < ExceptionBlockState::Finally);
2419
2420            let end_label = block.end_label;
2421            self.emit_break(end_label, None);
2422
2423            let finally_label = self.define_label();
2424            self.mark_label(finally_label);
2425            block.state = ExceptionBlockState::Finally;
2426            block.finally_label = Some(finally_label);
2427        } else {
2428            unreachable!()
2429        }
2430    }
2431
2432    /// Ends the code block for a generated `try` statement.
2433    fn end_exception_block(&mut self) {
2434        debug_assert!(self.peek_block_kind() == Some(CodeBlockKind::Exception));
2435        let block = self.end_block();
2436        let mut b = block.borrow_mut();
2437        if let CodeBlock::Exception(block) = &mut *b {
2438            let state = block.state;
2439            if state < ExceptionBlockState::Finally {
2440                self.emit_break(block.end_label, None);
2441            } else {
2442                self.emit_endfinally();
2443            }
2444            self.mark_label(block.end_label);
2445            self.emit_nop();
2446            block.state = ExceptionBlockState::Done;
2447        } else {
2448            unreachable!()
2449        }
2450    }
2451
2452    /// Begins a code block that supports `break` or `continue` statements that
2453    /// are defined in the source tree and not from generated code.
2454    fn begin_script_loop_block(&mut self) {
2455        self.begin_block(CodeBlock::Loop(LoopBlock {
2456            is_script: true,
2457            break_label: Label(-1),
2458            continue_label: Label(-1),
2459        }));
2460    }
2461
2462    /// Begins a code block that supports `break` or `continue` statements that
2463    /// are defined in generated code. Returns a label used to mark the
2464    /// operation to which to jump when a `break` statement targets this block.
2465    ///
2466    /// - `continue_label`: A Label used to mark the operation to which to jump
2467    ///   when a `continue` statement targets this block.
2468    fn begin_loop_block(&mut self, continue_label: Label) -> Label {
2469        let _tracing = dev_span!("begin_loop_block");
2470
2471        let break_label = self.define_label();
2472        self.begin_block(CodeBlock::Loop(LoopBlock {
2473            is_script: false,
2474            break_label,
2475            continue_label,
2476        }));
2477        break_label
2478    }
2479
2480    /// Ends a code block that supports `break` or `continue` statements that
2481    /// are defined in generated code or in the source tree.
2482    fn end_loop_block(&mut self) {
2483        debug_assert!(self.peek_block_kind() == Some(CodeBlockKind::Loop));
2484        let block = self.end_block();
2485        let block = block.borrow();
2486        if let CodeBlock::Loop(block) = &*block {
2487            let break_label = block.break_label;
2488            if !block.is_script {
2489                self.mark_label(break_label);
2490            }
2491        } else {
2492            unreachable!()
2493        }
2494    }
2495
2496    /// Begins a code block that supports `break` statements that are defined in
2497    /// the source tree and not from generated code.
2498    fn begin_script_switch_block(&mut self) {
2499        self.begin_block(CodeBlock::Switch(SwitchBlock {
2500            is_script: true,
2501            break_label: Label(-1),
2502        }));
2503    }
2504
2505    /// Begins a code block that supports `break` statements that are defined in
2506    /// generated code.
2507    ///
2508    /// Returns a label used to mark the operation to which to jump when a
2509    /// `break` statement targets this block.
2510    fn begin_switch_block(&mut self) -> Label {
2511        let break_label = self.define_label();
2512        self.begin_block(CodeBlock::Switch(SwitchBlock {
2513            is_script: false,
2514            break_label,
2515        }));
2516        break_label
2517    }
2518
2519    /// Ends a code block that supports `break` statements that are defined in
2520    /// generated code.
2521    fn end_switch_block(&mut self) {
2522        debug_assert!(self.peek_block_kind() == Some(CodeBlockKind::Switch));
2523        let block = self.end_block();
2524        let block = block.borrow();
2525        if let CodeBlock::Switch(block) = &*block {
2526            let break_label = block.break_label;
2527            if !block.is_script {
2528                self.mark_label(break_label);
2529            }
2530        } else {
2531            unreachable!()
2532        }
2533    }
2534
2535    fn begin_script_labeled_block(&mut self, label_text: Atom) {
2536        self.begin_block(CodeBlock::Labeled(LabeledBlock {
2537            is_script: true,
2538            label_text,
2539            break_label: Label(-1),
2540        }));
2541    }
2542
2543    fn begin_labeled_block(&mut self, label_text: Atom) {
2544        let break_label = self.define_label();
2545        self.begin_block(CodeBlock::Labeled(LabeledBlock {
2546            is_script: false,
2547            label_text,
2548            break_label,
2549        }));
2550    }
2551
2552    fn end_labeled_block(&mut self) {
2553        let block = self.end_block();
2554        if !block.borrow().is_script() {
2555            let break_label = match &*block.borrow() {
2556                CodeBlock::Labeled(block) => block.break_label,
2557                _ => unreachable!(),
2558            };
2559            self.mark_label(break_label);
2560        }
2561    }
2562
2563    /// Indicates whether the provided block supports `break` statements.
2564    fn supports_unlabeled_break(&self, block: &CodeBlock) -> bool {
2565        matches!(block, CodeBlock::Switch(..) | CodeBlock::Loop(..))
2566    }
2567
2568    /// Indicates whether the provided block supports `break` statements with
2569    /// labels.
2570    fn supports_labeled_break_or_continue(&self, block: &CodeBlock) -> bool {
2571        matches!(block, CodeBlock::Labeled(..))
2572    }
2573
2574    /// Indicates whether the provided block supports `continue` statements.
2575    fn supports_unlabeled_continue(&self, block: &CodeBlock) -> bool {
2576        matches!(block, CodeBlock::Loop(..))
2577    }
2578
2579    fn has_immediate_containing_labeled_block(&self, label_text: &Atom, start: usize) -> bool {
2580        for i in (0..=start).rev() {
2581            let block = self.block_stack.as_ref().unwrap()[i].clone();
2582            if self.supports_labeled_break_or_continue(&block.borrow()) {
2583                if let CodeBlock::Labeled(block) = &*block.borrow() {
2584                    if block.label_text == *label_text {
2585                        return true;
2586                    }
2587                } else {
2588                    unreachable!()
2589                }
2590            } else {
2591                break;
2592            }
2593        }
2594
2595        false
2596    }
2597
2598    /// Finds the label that is the target for a `break` statement.
2599    ///
2600    ///  - `label_text`: An optional name of a containing labeled statement.
2601    fn find_break_target(&self, label_text: Option<Atom>) -> Label {
2602        #[cfg(debug_assertions)]
2603        debug!("find_break_target: label_text={:?}", label_text);
2604
2605        if let Some(block_stack) = &self.block_stack {
2606            if let Some(label_text) = label_text {
2607                for i in (0..=block_stack.len() - 1).rev() {
2608                    let block = &block_stack[i];
2609                    if (self.supports_labeled_break_or_continue(&block.borrow())
2610                        && block.borrow().label_text().unwrap() == label_text)
2611                        || (self.supports_unlabeled_break(&block.borrow())
2612                            && self.has_immediate_containing_labeled_block(&label_text, i - 1))
2613                    {
2614                        return block.borrow().break_label().unwrap();
2615                    }
2616                }
2617            } else {
2618                for i in (0..=block_stack.len() - 1).rev() {
2619                    let block = &block_stack[i];
2620                    if self.supports_unlabeled_break(&block.borrow()) {
2621                        return block.borrow().break_label().unwrap();
2622                    }
2623                }
2624            }
2625        }
2626
2627        Label(0)
2628    }
2629
2630    /// Finds the label that is the target for a `continue` statement.
2631    ///
2632    /// - `labelText` An optional name of a containing labeled statement.
2633    fn find_continue_target(&self, label_text: Option<Atom>) -> Label {
2634        if let Some(block_stack) = &self.block_stack {
2635            if let Some(label_text) = label_text {
2636                for i in (0..=block_stack.len() - 1).rev() {
2637                    let block = &block_stack[i];
2638                    if self.supports_unlabeled_continue(&block.borrow())
2639                        && self.has_immediate_containing_labeled_block(&label_text, i - 1)
2640                    {
2641                        return block.borrow().continue_label().unwrap();
2642                    }
2643                }
2644            } else {
2645                for i in (0..=block_stack.len() - 1).rev() {
2646                    let block = &block_stack[i];
2647                    if self.supports_unlabeled_continue(&block.borrow()) {
2648                        return block.borrow().continue_label().unwrap();
2649                    }
2650                }
2651            }
2652        }
2653
2654        Label(0)
2655    }
2656
2657    /// Creates an expression that can be used to indicate the value for a
2658    /// label.
2659    fn create_label(&mut self, label: Option<Label>) -> Box<Expr> {
2660        if let Some(label) = label {
2661            if label.0 > 0 {
2662                #[cfg(debug_assertions)]
2663                debug!("create_label: label={:?}", label);
2664
2665                if self.label_exprs.is_none() {
2666                    self.label_exprs = Some(Default::default());
2667                }
2668                let label_expressions = self.label_exprs.as_mut().unwrap();
2669                let expr = Loc {
2670                    pos: BytePos(label.0 as _),
2671                    value: -1,
2672                };
2673                if label_expressions.get(label.0 as usize).is_none() {
2674                    if label.0 as usize >= label_expressions.len() {
2675                        label_expressions.resize(label.0 as usize + 1, Vec::new());
2676                    }
2677
2678                    label_expressions[label.0 as usize] = vec![expr];
2679                } else {
2680                    label_expressions
2681                        .get_mut(label.0 as usize)
2682                        .unwrap()
2683                        .push(expr);
2684                }
2685                return Invalid {
2686                    span: Span::new_with_checked(BytePos(label.0 as _), BytePos(label.0 as _)),
2687                }
2688                .into();
2689            }
2690        }
2691
2692        Box::new(Invalid { span: DUMMY_SP }.into())
2693    }
2694
2695    /// Creates a numeric literal for the provided instruction.
2696    fn create_instruction(&mut self, instruction: Instruction) -> Number {
2697        // TODO(kdy1):
2698        // self.add_synthetic_trailing_comment(
2699        //     literal,
2700        //     SyntaxKind::MultiLineCommentTrivia,
2701        //     get_instruction_name(instruction),
2702        // );
2703        Number {
2704            span: DUMMY_SP,
2705            value: instruction as u16 as _,
2706            raw: None,
2707        }
2708    }
2709
2710    /// Creates a statement that can be used indicate a Break operation to the
2711    /// provided label.
2712    ///
2713    /// - `label`: A label.
2714    /// - `location`: An optional source map location for the statement.
2715    fn create_inline_break(&mut self, label: Label, span: Option<Span>) -> ReturnStmt {
2716        debug_assert!(label.0 >= 0, "Invalid label");
2717        let args = vec![
2718            Some(self.create_instruction(Instruction::Break).as_arg()),
2719            Some(self.create_label(Some(label)).as_arg()),
2720        ];
2721        ReturnStmt {
2722            span: span.unwrap_or(DUMMY_SP),
2723            arg: Some(
2724                ArrayLit {
2725                    span: DUMMY_SP,
2726                    elems: args,
2727                }
2728                .into(),
2729            ),
2730        }
2731    }
2732
2733    /// Creates a statement that can be used indicate a Return operation.
2734    ///
2735    /// - `expr`: The expression for the return statement.
2736    /// - `loc`: An optional source map location for the statement.
2737    fn create_inline_return(&mut self, expr: Option<Box<Expr>>, loc: Option<Span>) -> ReturnStmt {
2738        ReturnStmt {
2739            span: loc.unwrap_or(DUMMY_SP),
2740            arg: Some(
2741                ArrayLit {
2742                    span: DUMMY_SP,
2743                    elems: match expr {
2744                        Some(expr) => vec![
2745                            Some(self.create_instruction(Instruction::Return).as_arg()),
2746                            Some(expr.as_arg()),
2747                        ],
2748                        None => vec![Some(self.create_instruction(Instruction::Return).as_arg())],
2749                    },
2750                }
2751                .into(),
2752            ),
2753        }
2754    }
2755
2756    /// Creates an expression that can be used to resume from a Yield operation.
2757    fn create_generator_resume(&mut self, loc: Option<Span>) -> Box<Expr> {
2758        CallExpr {
2759            span: loc.unwrap_or(DUMMY_SP),
2760            callee: self
2761                .state
2762                .clone()
2763                .make_member(quote_ident!("sent"))
2764                .as_callee(),
2765            args: Vec::new(),
2766            ..Default::default()
2767        }
2768        .into()
2769    }
2770
2771    /// Emits an empty instruction.
2772    fn emit_nop(&mut self) {
2773        self.emit_worker(OpCode::Nop, None, None);
2774    }
2775
2776    /// Emits a Statement.
2777    ///
2778    /// - `stmt`: A statement.
2779    fn emit_stmt(&mut self, stmt: Stmt) {
2780        if stmt.is_empty() {
2781            self.emit_nop();
2782        } else {
2783            self.emit_worker(OpCode::Statement, Some(OpArgs::Stmt(Box::new(stmt))), None);
2784        }
2785    }
2786
2787    /// Emits an Assignment operation.
2788    ///
2789    /// - `left`: The left-hand side of the assignment.
2790    /// - `right`: The right-hand side of the assignment.
2791    /// - `loc`: An optional source map location for the assignment.
2792    fn emit_assignment(&mut self, left: AssignTarget, right: Box<Expr>, loc: Option<Span>) {
2793        self.emit_worker(OpCode::Assign, Some(OpArgs::PatAndExpr(left, right)), loc);
2794    }
2795
2796    /// Emits a Break operation to the specified label.
2797    ///
2798    /// - `label`: A label.
2799    /// - `loc`: An optional source map location for the assignment.
2800    fn emit_break(&mut self, label: Label, loc: Option<Span>) {
2801        self.emit_worker(OpCode::Break, Some(OpArgs::Label(label)), loc);
2802    }
2803
2804    /// Emits a Break operation to the specified label when a condition
2805    /// evaluates to a truthy value at runtime.
2806    ///
2807    /// - `label`: A label.
2808    /// - `condition`: The condition.
2809    /// - `loc`: An optional source map location for the assignment.
2810    fn emit_break_when_true(&mut self, label: Label, condition: Box<Expr>, loc: Option<Span>) {
2811        self.emit_worker(
2812            OpCode::BreakWhenTrue,
2813            Some(OpArgs::LabelExpr(label, condition)),
2814            loc,
2815        );
2816    }
2817
2818    /// Emits a Break to the specified label when a condition evaluates to a
2819    /// falsy value at runtime
2820    ///
2821    /// - `label`: A label.
2822    /// - `condition`: The condition.
2823    /// - `loc`: An optional source map location for the assignment.
2824    fn emit_break_when_false(&mut self, label: Label, condition: Box<Expr>, loc: Option<Span>) {
2825        self.emit_worker(
2826            OpCode::BreakWhenFalse,
2827            Some(OpArgs::LabelExpr(label, condition)),
2828            loc,
2829        );
2830    }
2831
2832    /// Emits a YieldStar operation for the provided expression.
2833    ///
2834    /// - `expr`: An optional value for the yield operation.
2835    /// - `loc`: An optional source map location for the assignment.
2836    fn emit_yield_star(&mut self, expr: Option<Box<Expr>>, loc: Option<Span>) {
2837        self.emit_worker(OpCode::YieldStar, Some(OpArgs::OptExpr(expr)), loc);
2838    }
2839
2840    /// Emits a Yield operation for the provided expression.
2841    ///
2842    /// - `expr`: An optional value for the yield operation.
2843    /// - `loc`: An optional source map location for the assignment.
2844    fn emit_yield(&mut self, expr: Option<Box<Expr>>, loc: Option<Span>) {
2845        self.emit_worker(OpCode::Yield, Some(OpArgs::OptExpr(expr)), loc);
2846    }
2847
2848    ///  Emits a Return operation for the provided expression.
2849    ///
2850    /// - `expr`: An optional value for the operation.
2851    /// - `loc`: An optional source map location for the assignment.
2852    fn emit_return(&mut self, expr: Option<Box<Expr>>, loc: Option<Span>) {
2853        self.emit_worker(OpCode::Return, Some(OpArgs::OptExpr(expr)), loc);
2854    }
2855
2856    /// Emits a Throw operation for the provided expression.
2857    ///
2858    /// - `expr`: A value for the operation.
2859    /// - `loc`: An optional source map location for the assignment.
2860    fn emit_throw(&mut self, expr: Box<Expr>, loc: Option<Span>) {
2861        self.emit_worker(OpCode::Throw, Some(OpArgs::OptExpr(Some(expr))), loc);
2862    }
2863
2864    /// Emits an Endfinally operation. This is used to handle `finally` block
2865    /// semantics.
2866    fn emit_endfinally(&mut self) {
2867        self.emit_worker(OpCode::Endfinally, None, None);
2868    }
2869
2870    /// Emits an operation.
2871    ///
2872    /// - `code`: The OpCode for the operation.
2873    /// - `args`: The optional arguments for the operation.
2874    fn emit_worker(&mut self, code: OpCode, args: Option<OpArgs>, loc: Option<Span>) {
2875        if self.operations.is_none() {
2876            self.operations = Some(Vec::new());
2877            self.operation_args = Some(Vec::new());
2878            self.operation_locs = Some(Vec::new());
2879        }
2880        if self.label_offsets.is_none() {
2881            // mark entry point
2882            let label = self.define_label();
2883            self.mark_label(label);
2884        }
2885        debug_assert!(self.operations.is_some());
2886        debug_assert_eq!(
2887            self.operations.as_ref().unwrap().len(),
2888            self.operation_args.as_ref().unwrap().len()
2889        );
2890        debug_assert_eq!(
2891            self.operations.as_ref().unwrap().len(),
2892            self.operation_locs.as_ref().unwrap().len()
2893        );
2894
2895        self.operations.as_mut().unwrap().push(code);
2896        self.operation_args.as_mut().unwrap().push(args);
2897        self.operation_locs
2898            .as_mut()
2899            .unwrap()
2900            .push(loc.unwrap_or(DUMMY_SP));
2901    }
2902
2903    /// Builds the statements for the generator function body.
2904    fn build_stmts(&mut self) -> Vec<Stmt> {
2905        if let Some(ops) = self.operations.clone() {
2906            for (op_index, _) in ops.iter().enumerate() {
2907                self.write_operation(op_index);
2908            }
2909
2910            self.flush_final_label(ops.len());
2911        } else {
2912            self.flush_final_label(0);
2913        }
2914
2915        if let Some(clauses) = self.clauses.take() {
2916            let label_expr = self.state.clone().make_member(quote_ident!("label"));
2917            let switch_stmt = SwitchStmt {
2918                span: DUMMY_SP,
2919                discriminant: label_expr.into(),
2920                cases: clauses,
2921            };
2922            return vec![Stmt::Switch(switch_stmt)];
2923        }
2924
2925        if let Some(stmts) = self.stmts.take() {
2926            return stmts;
2927        }
2928
2929        Vec::new()
2930    }
2931
2932    /// Flush the current label and advance to a new label.
2933    fn flush_label(&mut self) {
2934        if self.stmts.is_none() {
2935            return;
2936        }
2937
2938        self.append_label(!self.last_operation_was_abrupt);
2939
2940        self.last_operation_was_abrupt = false;
2941        self.last_operation_was_completion = false;
2942        self.label_number += 1;
2943    }
2944
2945    /// Flush the final label of the generator function body.
2946    fn flush_final_label(&mut self, op_index: usize) {
2947        if self.is_final_label_reachable(op_index) {
2948            self.try_enter_label(op_index);
2949            self.with_block_stack = None;
2950            self.write_return(None, None);
2951        }
2952
2953        if self.stmts.is_some() && self.clauses.is_some() {
2954            self.append_label(false);
2955        }
2956
2957        self.update_label_expression();
2958    }
2959
2960    /// Tests whether the final label of the generator function body is
2961    /// reachable by user code.
2962    fn is_final_label_reachable(&self, op_index: usize) -> bool {
2963        // if the last operation was *not* a completion (return/throw) then
2964        // the final label is reachable.
2965        if !self.last_operation_was_completion {
2966            return true;
2967        }
2968
2969        // if there are no labels defined or referenced, then the final label is not
2970        // reachable.
2971        if self.label_offsets.is_none() || self.label_exprs.is_none() {
2972            return false;
2973        }
2974
2975        // if the label for this offset is referenced, then the final label
2976        // is reachable.
2977        for (label, label_offset) in self
2978            .label_offsets
2979            .as_ref()
2980            .unwrap()
2981            .iter()
2982            .copied()
2983            .enumerate()
2984        {
2985            if label_offset as usize == op_index
2986                && self.label_exprs.as_ref().unwrap().get(label).is_some()
2987            {
2988                return true;
2989            }
2990        }
2991
2992        false
2993    }
2994
2995    /// Appends a case clause for the last label and sets the new label.
2996    ///
2997    /// @param markLabelEnd Indicates that the transition between labels was a
2998    /// fall-through from a previous case clause and the change in labels should
2999    /// be reflected on the `state` object.
3000    fn append_label(&mut self, mark_label_end: bool) {
3001        if cfg!(debug_assertions) {
3002            debug!(mark_label_end = mark_label_end, "append_label");
3003        }
3004
3005        if self.clauses.is_none() {
3006            self.clauses = Some(Default::default());
3007        }
3008
3009        #[allow(clippy::manual_unwrap_or_default)]
3010        let stmts = if let Some(mut stmts) = self.stmts.take() {
3011            if self.with_block_stack.is_some() {
3012                // The previous label was nested inside one or more `with`
3013                // blocks, so we surround the statements in
3014                // generated `with` blocks to create the same environment.
3015
3016                for (_i, with_block) in self
3017                    .with_block_stack
3018                    .as_ref()
3019                    .unwrap()
3020                    .iter()
3021                    .enumerate()
3022                    .rev()
3023                {
3024                    let b = with_block.borrow();
3025                    let with_block = match &*b {
3026                        CodeBlock::With(v) => v,
3027                        _ => {
3028                            unreachable!()
3029                        }
3030                    };
3031
3032                    stmts = vec![Stmt::With(WithStmt {
3033                        span: DUMMY_SP,
3034                        obj: Box::new(Expr::Ident(with_block.expression.clone())),
3035                        body: Box::new(Stmt::Block(BlockStmt {
3036                            span: DUMMY_SP,
3037                            stmts,
3038                            ..Default::default()
3039                        })),
3040                    })];
3041                }
3042            }
3043
3044            if cfg!(debug_assertions) {
3045                debug!(
3046                    "current_exception_block = {:?}",
3047                    self.current_exception_block
3048                );
3049            }
3050
3051            if let Some(current_exception_block) = self.current_exception_block.take() {
3052                let b = current_exception_block.borrow();
3053                let ExceptionBlock {
3054                    start_label,
3055                    catch_label,
3056                    finally_label,
3057                    end_label,
3058                    ..
3059                } = match &*b {
3060                    CodeBlock::Exception(v) => v,
3061                    _ => {
3062                        unreachable!()
3063                    }
3064                };
3065
3066                let start_label = self.create_label(Some(*start_label));
3067                let catch_label = self.create_label(*catch_label);
3068                let finally_label = self.create_label(*finally_label);
3069                let end_label = self.create_label(Some(*end_label));
3070
3071                stmts.insert(
3072                    0,
3073                    ExprStmt {
3074                        span: DUMMY_SP,
3075                        expr: CallExpr {
3076                            span: DUMMY_SP,
3077                            callee: self
3078                                .state
3079                                .clone()
3080                                .make_member(quote_ident!("trys"))
3081                                .make_member(quote_ident!("push"))
3082                                .as_callee(),
3083                            args: vec![ArrayLit {
3084                                span: DUMMY_SP,
3085                                elems: vec![
3086                                    Some(start_label.as_arg()),
3087                                    Some(catch_label.as_arg()),
3088                                    Some(finally_label.as_arg()),
3089                                    Some(end_label.as_arg()),
3090                                ],
3091                            }
3092                            .as_arg()],
3093                            ..Default::default()
3094                        }
3095                        .into(),
3096                    }
3097                    .into(),
3098                );
3099            }
3100
3101            if mark_label_end {
3102                // The case clause for the last label falls through to this
3103                // label, so we add an assignment statement to
3104                // reflect the change in labels.
3105
3106                stmts.push(
3107                    ExprStmt {
3108                        span: DUMMY_SP,
3109                        expr: AssignExpr {
3110                            span: DUMMY_SP,
3111                            op: op!("="),
3112                            left: self.state.clone().make_member(quote_ident!("label")).into(),
3113                            right: (self.label_number + 1).into(),
3114                        }
3115                        .into(),
3116                    }
3117                    .into(),
3118                );
3119            }
3120
3121            stmts
3122        } else {
3123            Default::default()
3124        };
3125
3126        self.clauses.as_mut().unwrap().push(SwitchCase {
3127            span: DUMMY_SP,
3128            test: Some(self.label_number.into()),
3129            cons: stmts,
3130        });
3131    }
3132
3133    #[tracing::instrument(level = "debug", skip(self))]
3134    fn try_enter_label(&mut self, op_index: usize) {
3135        if self.label_offsets.is_none() {
3136            return;
3137        }
3138
3139        for (label, label_offset) in self.label_offsets.clone().unwrap().into_iter().enumerate() {
3140            if label_offset as usize == op_index {
3141                self.flush_label();
3142
3143                if self.label_numbers.is_none() {
3144                    self.label_numbers = Some(Vec::new());
3145                }
3146
3147                if let Some(v) = self
3148                    .label_numbers
3149                    .as_mut()
3150                    .unwrap()
3151                    .get_mut(self.label_number)
3152                {
3153                    v.push(label);
3154                } else {
3155                    if self.label_number >= self.label_numbers.as_ref().unwrap().len() {
3156                        self.label_numbers
3157                            .as_mut()
3158                            .unwrap()
3159                            .resize(self.label_number + 1, Vec::new());
3160                    }
3161
3162                    self.label_numbers.as_mut().unwrap()[self.label_number] = vec![label];
3163                }
3164            }
3165        }
3166    }
3167
3168    /// Updates literal expressions for labels with actual label numbers.
3169    fn update_label_expression(&mut self) {
3170        if self.label_exprs.is_some() && self.label_numbers.is_some() {
3171            for (label_number, labels) in self.label_numbers.as_ref().unwrap().iter().enumerate() {
3172                for &label in labels {
3173                    let exprs = self.label_exprs.as_mut().unwrap().get_mut(label);
3174                    if let Some(exprs) = exprs {
3175                        for expr in exprs {
3176                            expr.value = label_number as _;
3177                            #[cfg(debug_assertions)]
3178                            debug!("Label {:?} = {:?} ({:?})", label, expr.value, expr.pos);
3179                        }
3180                    }
3181                }
3182            }
3183        }
3184    }
3185
3186    /// Tries to enter or leave a code block.
3187    #[tracing::instrument(level = "debug", skip(self))]
3188    fn try_enter_or_leave_block(&mut self, op_index: usize) {
3189        if let Some(blocks) = &self.blocks {
3190            while self.block_index < self.block_actions.as_ref().unwrap().len()
3191                && self.block_offsets.as_ref().unwrap()[self.block_index] <= op_index
3192            {
3193                #[cfg(debug_assertions)]
3194                debug!("try_enter_or_leave_block: iter");
3195
3196                let block_index = self.block_index;
3197                self.block_index += 1;
3198
3199                if cfg!(debug_assertions) {
3200                    debug!(block_index = block_index, "try_enter_or_leave_block")
3201                }
3202
3203                //
3204                let block = blocks[block_index].clone();
3205                let block_action = self.block_actions.as_ref().unwrap()[block_index];
3206
3207                let b = block.borrow();
3208                match &*b {
3209                    CodeBlock::Exception(_) => {
3210                        if block_action == BlockAction::Open {
3211                            self.exception_block_stack
3212                                .get_or_insert_with(Default::default)
3213                                .extend(self.current_exception_block.clone());
3214
3215                            // https://github.com/swc-project/swc/issues/5913
3216                            if self.stmts.is_none() {
3217                                self.stmts = Some(Default::default());
3218                            }
3219
3220                            #[cfg(debug_assertions)]
3221                            debug!("Current exception block: open = Some({:?})", block);
3222                            self.current_exception_block = Some(block.clone());
3223                        } else if block_action == BlockAction::Close {
3224                            self.current_exception_block =
3225                                self.exception_block_stack.as_mut().unwrap().pop();
3226                            #[cfg(debug_assertions)]
3227                            debug!(
3228                                "Current exception block: close = {:?}",
3229                                self.current_exception_block
3230                            );
3231                        }
3232                    }
3233
3234                    CodeBlock::With(_) => {
3235                        if block_action == BlockAction::Open {
3236                            if self.with_block_stack.is_none() {
3237                                self.with_block_stack = Some(Default::default());
3238                            }
3239
3240                            self.with_block_stack.as_mut().unwrap().push(block.clone());
3241                        } else if block_action == BlockAction::Close {
3242                            self.with_block_stack.as_mut().unwrap().pop();
3243                        }
3244                    }
3245
3246                    _ => {}
3247                }
3248            }
3249        }
3250    }
3251
3252    /// Writes an operation as a statement to the current label's statement
3253    /// list.
3254    #[tracing::instrument(level = "debug", skip(self))]
3255    fn write_operation(&mut self, op_index: usize) {
3256        if cfg!(debug_assertions) {
3257            debug!("Writing operation {}", op_index);
3258        }
3259
3260        self.try_enter_label(op_index);
3261        self.try_enter_or_leave_block(op_index);
3262
3263        // early termination, nothing else to process in this label
3264        if self.last_operation_was_abrupt {
3265            return;
3266        }
3267
3268        self.last_operation_was_abrupt = false;
3269        self.last_operation_was_completion = false;
3270
3271        let opcode = self.operations.as_ref().unwrap()[op_index];
3272        if opcode == OpCode::Nop {
3273            return;
3274        } else if opcode == OpCode::Endfinally {
3275            self.write_end_finally();
3276            return;
3277        }
3278
3279        let args = self.operation_args.as_mut().unwrap()[op_index]
3280            .take()
3281            .expect("failed to take operation arguments");
3282        if opcode == OpCode::Statement {
3283            let args = args.expect_stmt();
3284            self.write_stmt(*args);
3285            return;
3286        }
3287
3288        let loc = self.operation_locs.as_ref().unwrap()[op_index];
3289
3290        match opcode {
3291            OpCode::Assign => {
3292                let args = args.expect_pat_and_expr();
3293                self.write_assign(args.0, args.1, Some(loc));
3294            }
3295            OpCode::Break => {
3296                let args = args.expect_label();
3297                self.write_break(args, Some(loc));
3298            }
3299            OpCode::BreakWhenTrue => {
3300                let args = args.expect_label_expr();
3301                self.write_break_when_true(args.0, args.1, Some(loc));
3302            }
3303            OpCode::BreakWhenFalse => {
3304                let args = args.expect_label_expr();
3305                self.write_break_when_false(args.0, args.1, Some(loc));
3306            }
3307            OpCode::Yield => {
3308                let args = args.expect_opt_expr();
3309
3310                self.write_yield(args, Some(loc));
3311            }
3312            OpCode::YieldStar => {
3313                let args = args.expect_opt_expr().unwrap();
3314
3315                self.write_yield_star(args, Some(loc));
3316            }
3317            OpCode::Return => {
3318                let args = args.expect_opt_expr();
3319
3320                self.write_return(args, Some(loc));
3321            }
3322            OpCode::Throw => {
3323                let args = args.expect_opt_expr().unwrap();
3324
3325                self.write_throw(args, Some(loc));
3326            }
3327            _ => {}
3328        }
3329    }
3330
3331    /// Writes a statement to the current label's statement list.
3332    fn write_stmt(&mut self, stmt: Stmt) {
3333        if stmt.is_empty() {
3334            return;
3335        }
3336        match self.stmts {
3337            Some(ref mut stmts) => stmts.push(stmt),
3338            None => self.stmts = Some(vec![stmt]),
3339        }
3340    }
3341
3342    /// Writes an Assign operation to the current label's statement list.
3343    fn write_assign(&mut self, left: AssignTarget, right: Box<Expr>, op_loc: Option<Span>) {
3344        self.write_stmt(
3345            ExprStmt {
3346                span: op_loc.unwrap_or(DUMMY_SP),
3347                expr: AssignExpr {
3348                    span: DUMMY_SP,
3349                    op: op!("="),
3350                    left,
3351                    right,
3352                }
3353                .into(),
3354            }
3355            .into(),
3356        )
3357    }
3358
3359    /// Writes a Throw operation to the current label's statement list.
3360    ///
3361    /// @param expr The value to throw
3362    /// @param operationLocation The source map location for the operation.
3363    fn write_throw(&mut self, expr: Box<Expr>, op_loc: Option<Span>) {
3364        self.last_operation_was_abrupt = true;
3365        self.last_operation_was_completion = true;
3366
3367        // let inst = self.create_instruction(Instruction::Return);
3368        self.write_stmt(
3369            ThrowStmt {
3370                span: op_loc.unwrap_or(DUMMY_SP),
3371                arg: expr,
3372            }
3373            .into(),
3374        )
3375    }
3376
3377    /// Writes a Return operation to the current label's statement list.
3378    ///
3379    /// @param label The label for the Break.
3380    /// @param operationLocation The source map location for the operation.
3381    fn write_return(&mut self, expr: Option<Box<Expr>>, op_loc: Option<Span>) {
3382        self.last_operation_was_abrupt = true;
3383        self.last_operation_was_completion = true;
3384
3385        let inst = self.create_instruction(Instruction::Return);
3386        self.write_stmt(
3387            ReturnStmt {
3388                span: op_loc.unwrap_or(DUMMY_SP),
3389                arg: Some(
3390                    ArrayLit {
3391                        span: DUMMY_SP,
3392                        elems: match expr {
3393                            Some(expr) => {
3394                                vec![Some(inst.as_arg()), Some(expr.as_arg())]
3395                            }
3396                            _ => {
3397                                vec![Some(inst.as_arg())]
3398                            }
3399                        },
3400                    }
3401                    .into(),
3402                ),
3403            }
3404            .into(),
3405        )
3406    }
3407
3408    /// Writes a Break operation to the current label's statement list.
3409    ///
3410    /// @param label The label for the Break.
3411    /// @param operationLocation The source map location for the operation.
3412    fn write_break(&mut self, label: Label, op_loc: Option<Span>) {
3413        self.last_operation_was_abrupt = true;
3414
3415        let inst = self.create_instruction(Instruction::Break);
3416        let label = self.create_label(Some(label));
3417        self.write_stmt(
3418            ReturnStmt {
3419                span: op_loc.unwrap_or(DUMMY_SP),
3420                arg: Some(
3421                    ArrayLit {
3422                        span: DUMMY_SP,
3423                        elems: vec![Some(inst.as_arg()), Some(label.as_arg())],
3424                    }
3425                    .into(),
3426                ),
3427            }
3428            .into(),
3429        )
3430    }
3431
3432    /// Writes a BreakWhenTrue operation to the current label's statement list.
3433    ///
3434    /// @param label The label for the Break.
3435    /// @param condition The condition for the Break.
3436    /// @param operationLocation The source map location for the operation.
3437    fn write_break_when_true(&mut self, label: Label, cond: Box<Expr>, op_loc: Option<Span>) {
3438        let inst = self.create_instruction(Instruction::Break);
3439        let label = self.create_label(Some(label));
3440        self.write_stmt(
3441            IfStmt {
3442                span: DUMMY_SP,
3443                test: cond,
3444                cons: Box::new(Stmt::Return(ReturnStmt {
3445                    span: op_loc.unwrap_or(DUMMY_SP),
3446                    arg: Some(
3447                        ArrayLit {
3448                            span: DUMMY_SP,
3449                            elems: vec![Some(inst.as_arg()), Some(label.as_arg())],
3450                        }
3451                        .into(),
3452                    ),
3453                })),
3454                alt: None,
3455            }
3456            .into(),
3457        )
3458    }
3459
3460    /// Writes a BreakWhenFalse operation to the current label's statement list.
3461    ///
3462    /// @param label The label for the Break.
3463    /// @param condition The condition for the Break.
3464    /// @param operationLocation The source map location for the operation.
3465    fn write_break_when_false(&mut self, label: Label, cond: Box<Expr>, op_loc: Option<Span>) {
3466        let inst = self.create_instruction(Instruction::Break);
3467        let label = self.create_label(Some(label));
3468        self.write_stmt(
3469            IfStmt {
3470                span: DUMMY_SP,
3471                test: UnaryExpr {
3472                    span: DUMMY_SP,
3473                    op: op!("!"),
3474                    arg: cond,
3475                }
3476                .into(),
3477                cons: Box::new(Stmt::Return(ReturnStmt {
3478                    span: op_loc.unwrap_or(DUMMY_SP),
3479                    arg: Some(
3480                        ArrayLit {
3481                            span: DUMMY_SP,
3482                            elems: vec![Some(inst.as_arg()), Some(label.as_arg())],
3483                        }
3484                        .into(),
3485                    ),
3486                })),
3487                alt: None,
3488            }
3489            .into(),
3490        )
3491    }
3492
3493    /// Writes a Yield operation to the current label's statement list.
3494    ///
3495    /// - expr: The expression to yield.
3496    /// - op_loc: The source map location for the operation.
3497    fn write_yield(&mut self, expr: Option<Box<Expr>>, op_loc: Option<Span>) {
3498        self.last_operation_was_abrupt = true;
3499
3500        let inst = self.create_instruction(Instruction::Yield);
3501        let elems = match expr {
3502            Some(expr) => {
3503                vec![Some(inst.as_arg()), Some(expr.as_arg())]
3504            }
3505            None => {
3506                vec![Some(inst.as_arg())]
3507            }
3508        };
3509        self.write_stmt(
3510            ReturnStmt {
3511                span: op_loc.unwrap_or(DUMMY_SP),
3512                arg: Some(
3513                    ArrayLit {
3514                        span: DUMMY_SP,
3515                        elems,
3516                    }
3517                    .into(),
3518                ),
3519            }
3520            .into(),
3521        );
3522    }
3523
3524    /// Writes a YieldStar instruction to the current label's statement list.
3525    ///
3526    /// - expr: The expression to yield.
3527    /// - op_loc: The source map location for the operation.
3528    fn write_yield_star(&mut self, expr: Box<Expr>, op_loc: Option<Span>) {
3529        self.last_operation_was_abrupt = true;
3530
3531        let arg1 = self.create_instruction(Instruction::YieldStar);
3532        self.write_stmt(
3533            ReturnStmt {
3534                span: op_loc.unwrap_or(DUMMY_SP),
3535                arg: Some(
3536                    ArrayLit {
3537                        span: DUMMY_SP,
3538                        elems: vec![Some(arg1.as_arg()), Some(expr.as_arg())],
3539                    }
3540                    .into(),
3541                ),
3542            }
3543            .into(),
3544        )
3545    }
3546
3547    /// Writes an Endfinally instruction to the current label's statement list.
3548    fn write_end_finally(&mut self) {
3549        self.last_operation_was_abrupt = true;
3550
3551        let arg = self.create_instruction(Instruction::Endfinally);
3552        self.write_stmt(
3553            ReturnStmt {
3554                span: DUMMY_SP,
3555                arg: Some(
3556                    ArrayLit {
3557                        span: DUMMY_SP,
3558                        elems: vec![Some(arg.as_arg())],
3559                    }
3560                    .into(),
3561                ),
3562            }
3563            .into(),
3564        )
3565    }
3566
3567    fn hoist_variable_declaration(&mut self, id: &Ident) {
3568        self.hoisted_vars.push(VarDeclarator {
3569            span: DUMMY_SP,
3570            name: id.clone().into(),
3571            init: None,
3572            definite: Default::default(),
3573        })
3574    }
3575
3576    fn get_initialized_variables<'a>(
3577        &self,
3578        initializer: &'a mut VarDecl,
3579    ) -> Vec<&'a mut VarDeclarator> {
3580        initializer
3581            .decls
3582            .iter_mut()
3583            .filter(|v| v.init.is_some())
3584            .collect()
3585    }
3586
3587    fn create_temp_variable(&mut self) -> Ident {
3588        let i = private_ident!("_");
3589
3590        self.hoisted_vars.push(VarDeclarator {
3591            span: DUMMY_SP,
3592            name: i.clone().into(),
3593            init: None,
3594            definite: Default::default(),
3595        });
3596
3597        i
3598    }
3599
3600    /// Returns `(target, this_arg)`
3601    fn create_call_binding(
3602        &mut self,
3603        expr: Box<Expr>,
3604        is_new_call: bool,
3605    ) -> (Box<Expr>, Box<Expr>) {
3606        let mut callee = expr;
3607
3608        match &mut *callee {
3609            Expr::Ident(..) => (
3610                callee.clone(),
3611                if is_new_call {
3612                    callee
3613                } else {
3614                    Expr::undefined(DUMMY_SP)
3615                },
3616            ),
3617
3618            Expr::Member(MemberExpr { obj, .. }) if !is_new_call => {
3619                if obj.is_ident() {
3620                    let this_arg = obj.clone();
3621                    return (callee, this_arg);
3622                }
3623
3624                let this_arg = self.create_temp_variable();
3625                *obj = Box::new(obj.take().make_assign_to(op!("="), this_arg.clone().into()));
3626
3627                (callee, this_arg.into())
3628            }
3629
3630            _ => {
3631                if !is_new_call {
3632                    (callee, Expr::undefined(DUMMY_SP))
3633                } else {
3634                    let this_arg = self.create_temp_variable();
3635                    let target = callee.make_assign_to(op!("="), this_arg.clone().into());
3636                    (Box::new(target), this_arg.into())
3637                }
3638            }
3639        }
3640    }
3641}
3642
3643fn contains_yield<N>(node: &N) -> bool
3644where
3645    N: VisitWith<YieldFinder>,
3646{
3647    let mut v = YieldFinder { found: false };
3648    node.visit_with(&mut v);
3649    v.found
3650}
3651
3652struct YieldFinder {
3653    found: bool,
3654}
3655
3656impl Visit for YieldFinder {
3657    noop_visit_type!(fail);
3658
3659    fn visit_yield_expr(&mut self, _: &YieldExpr) {
3660        self.found = true;
3661    }
3662
3663    fn visit_arrow_expr(&mut self, f: &ArrowExpr) {
3664        f.params.visit_with(self);
3665    }
3666
3667    fn visit_function(&mut self, f: &Function) {
3668        f.decorators.visit_with(self);
3669        f.params.visit_with(self);
3670    }
3671}
3672
3673#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3674pub(super) struct Loc {
3675    pos: BytePos,
3676    value: i32,
3677}
3678
3679/// Convert <invalid> to case number
3680struct InvalidToLit<'a> {
3681    // Map from loc-id to stmt index
3682    map: Option<&'a [Vec<Loc>]>,
3683}
3684
3685impl VisitMut for InvalidToLit<'_> {
3686    noop_visit_mut_type!(fail);
3687
3688    fn visit_mut_expr(&mut self, e: &mut Expr) {
3689        e.visit_mut_children_with(self);
3690
3691        if let Expr::Invalid(Invalid { span }) = e {
3692            if span.lo != BytePos(0) && span.lo == span.hi {
3693                if let Some(Loc { value, .. }) = self
3694                    .map
3695                    .iter()
3696                    .flat_map(|v| v.iter())
3697                    .flatten()
3698                    .find(|loc| loc.pos == span.lo)
3699                {
3700                    *e = (*value as usize).into();
3701                }
3702            }
3703        }
3704    }
3705
3706    fn visit_mut_seq_expr(&mut self, e: &mut SeqExpr) {
3707        e.visit_mut_children_with(self);
3708
3709        e.exprs.retain(|e| !e.is_invalid());
3710    }
3711
3712    fn visit_mut_opt_expr_or_spread(&mut self, e: &mut Option<ExprOrSpread>) {
3713        e.visit_mut_children_with(self);
3714
3715        if let Some(arg) = e {
3716            if arg.expr.is_invalid() {
3717                *e = None;
3718            }
3719        }
3720    }
3721}