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