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