• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2namespace ts {
3    const enum ESNextSubstitutionFlags {
4        /** Enables substitutions for async methods with `super` calls. */
5        AsyncMethodsWithSuper = 1 << 0
6    }
7
8    // Facts we track as we traverse the tree
9    const enum HierarchyFacts {
10        None = 0,
11
12        //
13        // Ancestor facts
14        //
15
16        HasLexicalThis = 1 << 0,
17        IterationContainer = 1 << 1,
18        // NOTE: do not add more ancestor flags without also updating AncestorFactsMask below.
19
20        //
21        // Ancestor masks
22        //
23
24        AncestorFactsMask = (IterationContainer << 1) - 1,
25
26        SourceFileIncludes = HasLexicalThis,
27        SourceFileExcludes = IterationContainer,
28        StrictModeSourceFileIncludes = None,
29
30        ClassOrFunctionIncludes = HasLexicalThis,
31        ClassOrFunctionExcludes = IterationContainer,
32
33        ArrowFunctionIncludes = None,
34        ArrowFunctionExcludes = ClassOrFunctionExcludes,
35
36        IterationStatementIncludes = IterationContainer,
37        IterationStatementExcludes = None,
38    }
39
40    export function transformES2018(context: TransformationContext) {
41        const {
42            factory,
43            getEmitHelperFactory: emitHelpers,
44            resumeLexicalEnvironment,
45            endLexicalEnvironment,
46            hoistVariableDeclaration
47        } = context;
48
49        const resolver = context.getEmitResolver();
50        const compilerOptions = context.getCompilerOptions();
51        const languageVersion = getEmitScriptTarget(compilerOptions);
52
53        const previousOnEmitNode = context.onEmitNode;
54        context.onEmitNode = onEmitNode;
55
56        const previousOnSubstituteNode = context.onSubstituteNode;
57        context.onSubstituteNode = onSubstituteNode;
58
59        let exportedVariableStatement = false;
60        let enabledSubstitutions: ESNextSubstitutionFlags;
61        let enclosingFunctionFlags: FunctionFlags;
62        let parametersWithPrecedingObjectRestOrSpread: Set<ParameterDeclaration> | undefined;
63        let enclosingSuperContainerFlags: NodeCheckFlags = 0;
64        let hierarchyFacts: HierarchyFacts = 0;
65
66        let currentSourceFile: SourceFile;
67        let taggedTemplateStringDeclarations: VariableDeclaration[];
68
69        /** Keeps track of property names accessed on super (`super.x`) within async functions. */
70        let capturedSuperProperties: Set<__String>;
71        /** Whether the async function contains an element access on super (`super[x]`). */
72        let hasSuperElementAccess: boolean;
73        /** A set of node IDs for generated super accessors. */
74        const substitutedSuperAccessors: boolean[] = [];
75
76        return chainBundle(context, transformSourceFile);
77
78        function affectsSubtree(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) {
79            return hierarchyFacts !== (hierarchyFacts & ~excludeFacts | includeFacts);
80        }
81
82        /**
83         * Sets the `HierarchyFacts` for this node prior to visiting this node's subtree, returning the facts set prior to modification.
84         * @param excludeFacts The existing `HierarchyFacts` to reset before visiting the subtree.
85         * @param includeFacts The new `HierarchyFacts` to set before visiting the subtree.
86         */
87        function enterSubtree(excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) {
88            const ancestorFacts = hierarchyFacts;
89            hierarchyFacts = (hierarchyFacts & ~excludeFacts | includeFacts) & HierarchyFacts.AncestorFactsMask;
90            return ancestorFacts;
91        }
92
93        /**
94         * Restores the `HierarchyFacts` for this node's ancestor after visiting this node's
95         * subtree.
96         * @param ancestorFacts The `HierarchyFacts` of the ancestor to restore after visiting the subtree.
97         */
98        function exitSubtree(ancestorFacts: HierarchyFacts) {
99            hierarchyFacts = ancestorFacts;
100        }
101
102        function recordTaggedTemplateString(temp: Identifier) {
103            taggedTemplateStringDeclarations = append(
104                taggedTemplateStringDeclarations,
105                factory.createVariableDeclaration(temp));
106        }
107
108        function transformSourceFile(node: SourceFile) {
109            if (node.isDeclarationFile) {
110                return node;
111            }
112
113            currentSourceFile = node;
114            const visited = visitSourceFile(node);
115            addEmitHelpers(visited, context.readEmitHelpers());
116
117            currentSourceFile = undefined!;
118            taggedTemplateStringDeclarations = undefined!;
119            return visited;
120        }
121
122        function visitor(node: Node): VisitResult<Node> {
123            return visitorWorker(node, /*expressionResultIsUnused*/ false);
124        }
125
126        function visitorWithUnusedExpressionResult(node: Node): VisitResult<Node> {
127            return visitorWorker(node, /*expressionResultIsUnused*/ true);
128        }
129
130        function visitorNoAsyncModifier(node: Node): VisitResult<Node> {
131            if (node.kind === SyntaxKind.AsyncKeyword) {
132                return undefined;
133            }
134            return node;
135        }
136
137        function doWithHierarchyFacts<T, U>(cb: (value: T) => U, value: T, excludeFacts: HierarchyFacts, includeFacts: HierarchyFacts) {
138            if (affectsSubtree(excludeFacts, includeFacts)) {
139                const ancestorFacts = enterSubtree(excludeFacts, includeFacts);
140                const result = cb(value);
141                exitSubtree(ancestorFacts);
142                return result;
143            }
144            return cb(value);
145        }
146
147        function visitDefault(node: Node): VisitResult<Node> {
148            return visitEachChild(node, visitor, context);
149        }
150
151        /**
152         * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the
153         * expression of an `ExpressionStatement`).
154         */
155        function visitorWorker(node: Node, expressionResultIsUnused: boolean): VisitResult<Node> {
156            if ((node.transformFlags & TransformFlags.ContainsES2018) === 0) {
157                return node;
158            }
159            switch (node.kind) {
160                case SyntaxKind.AwaitExpression:
161                    return visitAwaitExpression(node as AwaitExpression);
162                case SyntaxKind.YieldExpression:
163                    return visitYieldExpression(node as YieldExpression);
164                case SyntaxKind.ReturnStatement:
165                    return visitReturnStatement(node as ReturnStatement);
166                case SyntaxKind.LabeledStatement:
167                    return visitLabeledStatement(node as LabeledStatement);
168                case SyntaxKind.ObjectLiteralExpression:
169                    return visitObjectLiteralExpression(node as ObjectLiteralExpression);
170                case SyntaxKind.BinaryExpression:
171                    return visitBinaryExpression(node as BinaryExpression, expressionResultIsUnused);
172                case SyntaxKind.CommaListExpression:
173                    return visitCommaListExpression(node as CommaListExpression, expressionResultIsUnused);
174                case SyntaxKind.CatchClause:
175                    return visitCatchClause(node as CatchClause);
176                case SyntaxKind.VariableStatement:
177                    return visitVariableStatement(node as VariableStatement);
178                case SyntaxKind.VariableDeclaration:
179                    return visitVariableDeclaration(node as VariableDeclaration);
180                case SyntaxKind.DoStatement:
181                case SyntaxKind.WhileStatement:
182                case SyntaxKind.ForInStatement:
183                    return doWithHierarchyFacts(
184                        visitDefault,
185                        node,
186                        HierarchyFacts.IterationStatementExcludes,
187                        HierarchyFacts.IterationStatementIncludes);
188                case SyntaxKind.ForOfStatement:
189                    return visitForOfStatement(node as ForOfStatement, /*outermostLabeledStatement*/ undefined);
190                case SyntaxKind.ForStatement:
191                    return doWithHierarchyFacts(
192                        visitForStatement,
193                        node as ForStatement,
194                        HierarchyFacts.IterationStatementExcludes,
195                        HierarchyFacts.IterationStatementIncludes);
196                case SyntaxKind.VoidExpression:
197                    return visitVoidExpression(node as VoidExpression);
198                case SyntaxKind.Constructor:
199                    return doWithHierarchyFacts(
200                        visitConstructorDeclaration,
201                        node as ConstructorDeclaration,
202                        HierarchyFacts.ClassOrFunctionExcludes,
203                        HierarchyFacts.ClassOrFunctionIncludes);
204                case SyntaxKind.MethodDeclaration:
205                    return doWithHierarchyFacts(
206                        visitMethodDeclaration,
207                        node as MethodDeclaration,
208                        HierarchyFacts.ClassOrFunctionExcludes,
209                        HierarchyFacts.ClassOrFunctionIncludes);
210                case SyntaxKind.GetAccessor:
211                    return doWithHierarchyFacts(
212                        visitGetAccessorDeclaration,
213                        node as GetAccessorDeclaration,
214                        HierarchyFacts.ClassOrFunctionExcludes,
215                        HierarchyFacts.ClassOrFunctionIncludes);
216                case SyntaxKind.SetAccessor:
217                    return doWithHierarchyFacts(
218                        visitSetAccessorDeclaration,
219                        node as SetAccessorDeclaration,
220                        HierarchyFacts.ClassOrFunctionExcludes,
221                        HierarchyFacts.ClassOrFunctionIncludes);
222                case SyntaxKind.FunctionDeclaration:
223                    return doWithHierarchyFacts(
224                        visitFunctionDeclaration,
225                        node as FunctionDeclaration,
226                        HierarchyFacts.ClassOrFunctionExcludes,
227                        HierarchyFacts.ClassOrFunctionIncludes);
228                case SyntaxKind.FunctionExpression:
229                    return doWithHierarchyFacts(
230                        visitFunctionExpression,
231                        node as FunctionExpression,
232                        HierarchyFacts.ClassOrFunctionExcludes,
233                        HierarchyFacts.ClassOrFunctionIncludes);
234                case SyntaxKind.ArrowFunction:
235                    return doWithHierarchyFacts(
236                        visitArrowFunction,
237                        node as ArrowFunction,
238                        HierarchyFacts.ArrowFunctionExcludes,
239                        HierarchyFacts.ArrowFunctionIncludes);
240                case SyntaxKind.Parameter:
241                    return visitParameter(node as ParameterDeclaration);
242                case SyntaxKind.ExpressionStatement:
243                    return visitExpressionStatement(node as ExpressionStatement);
244                case SyntaxKind.ParenthesizedExpression:
245                    return visitParenthesizedExpression(node as ParenthesizedExpression, expressionResultIsUnused);
246                case SyntaxKind.TaggedTemplateExpression:
247                    return visitTaggedTemplateExpression(node as TaggedTemplateExpression);
248                case SyntaxKind.PropertyAccessExpression:
249                    if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) {
250                        capturedSuperProperties.add(node.name.escapedText);
251                    }
252                    return visitEachChild(node, visitor, context);
253                case SyntaxKind.ElementAccessExpression:
254                    if (capturedSuperProperties && (node as ElementAccessExpression).expression.kind === SyntaxKind.SuperKeyword) {
255                        hasSuperElementAccess = true;
256                    }
257                    return visitEachChild(node, visitor, context);
258                case SyntaxKind.ClassDeclaration:
259                case SyntaxKind.ClassExpression:
260                    return doWithHierarchyFacts(
261                        visitDefault,
262                        node,
263                        HierarchyFacts.ClassOrFunctionExcludes,
264                        HierarchyFacts.ClassOrFunctionIncludes);
265                default:
266                    return visitEachChild(node, visitor, context);
267            }
268        }
269
270        function visitAwaitExpression(node: AwaitExpression): Expression {
271            if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) {
272                return setOriginalNode(
273                    setTextRange(
274                        factory.createYieldExpression(/*asteriskToken*/ undefined, emitHelpers().createAwaitHelper(visitNode(node.expression, visitor, isExpression))),
275                        /*location*/ node
276                    ),
277                    node
278                );
279            }
280            return visitEachChild(node, visitor, context);
281        }
282
283        function visitYieldExpression(node: YieldExpression) {
284            if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) {
285                if (node.asteriskToken) {
286                    const expression = visitNode(Debug.checkDefined(node.expression), visitor, isExpression);
287
288                    return setOriginalNode(
289                        setTextRange(
290                            factory.createYieldExpression(
291                                /*asteriskToken*/ undefined,
292                                emitHelpers().createAwaitHelper(
293                                    factory.updateYieldExpression(
294                                        node,
295                                        node.asteriskToken,
296                                        setTextRange(
297                                            emitHelpers().createAsyncDelegatorHelper(
298                                                setTextRange(
299                                                    emitHelpers().createAsyncValuesHelper(expression),
300                                                    expression
301                                                )
302                                            ),
303                                            expression
304                                        )
305                                    )
306                                )
307                            ),
308                            node
309                        ),
310                        node
311                    );
312                }
313
314                return setOriginalNode(
315                    setTextRange(
316                        factory.createYieldExpression(
317                            /*asteriskToken*/ undefined,
318                            createDownlevelAwait(
319                                node.expression
320                                    ? visitNode(node.expression, visitor, isExpression)
321                                    : factory.createVoidZero()
322                            )
323                        ),
324                        node
325                    ),
326                    node
327                );
328            }
329
330            return visitEachChild(node, visitor, context);
331        }
332
333        function visitReturnStatement(node: ReturnStatement) {
334            if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) {
335                return factory.updateReturnStatement(node, createDownlevelAwait(
336                    node.expression ? visitNode(node.expression, visitor, isExpression) : factory.createVoidZero()
337                ));
338            }
339
340            return visitEachChild(node, visitor, context);
341        }
342
343        function visitLabeledStatement(node: LabeledStatement) {
344            if (enclosingFunctionFlags & FunctionFlags.Async) {
345                const statement = unwrapInnermostStatementOfLabel(node);
346                if (statement.kind === SyntaxKind.ForOfStatement && (statement as ForOfStatement).awaitModifier) {
347                    return visitForOfStatement(statement as ForOfStatement, node);
348                }
349                return factory.restoreEnclosingLabel(visitNode(statement, visitor, isStatement, factory.liftToBlock), node);
350            }
351            return visitEachChild(node, visitor, context);
352        }
353
354        function chunkObjectLiteralElements(elements: readonly ObjectLiteralElementLike[]): Expression[] {
355            let chunkObject: ObjectLiteralElementLike[] | undefined;
356            const objects: Expression[] = [];
357            for (const e of elements) {
358                if (e.kind === SyntaxKind.SpreadAssignment) {
359                    if (chunkObject) {
360                        objects.push(factory.createObjectLiteralExpression(chunkObject));
361                        chunkObject = undefined;
362                    }
363                    const target = e.expression;
364                    objects.push(visitNode(target, visitor, isExpression));
365                }
366                else {
367                    chunkObject = append(chunkObject, e.kind === SyntaxKind.PropertyAssignment
368                        ? factory.createPropertyAssignment(e.name, visitNode(e.initializer, visitor, isExpression))
369                        : visitNode(e, visitor, isObjectLiteralElementLike));
370                }
371            }
372            if (chunkObject) {
373                objects.push(factory.createObjectLiteralExpression(chunkObject));
374            }
375
376            return objects;
377        }
378
379        function visitObjectLiteralExpression(node: ObjectLiteralExpression): Expression {
380            if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
381                // spread elements emit like so:
382                // non-spread elements are chunked together into object literals, and then all are passed to __assign:
383                //     { a, ...o, b } => __assign(__assign({a}, o), {b});
384                // If the first element is a spread element, then the first argument to __assign is {}:
385                //     { ...o, a, b, ...o2 } => __assign(__assign(__assign({}, o), {a, b}), o2)
386                //
387                // We cannot call __assign with more than two elements, since any element could cause side effects. For
388                // example:
389                //      var k = { a: 1, b: 2 };
390                //      var o = { a: 3, ...k, b: k.a++ };
391                //      // expected: { a: 1, b: 1 }
392                // If we translate the above to `__assign({ a: 3 }, k, { b: k.a++ })`, the `k.a++` will evaluate before
393                // `k` is spread and we end up with `{ a: 2, b: 1 }`.
394                //
395                // This also occurs for spread elements, not just property assignments:
396                //      var k = { a: 1, get b() { l = { z: 9 }; return 2; } };
397                //      var l = { c: 3 };
398                //      var o = { ...k, ...l };
399                //      // expected: { a: 1, b: 2, z: 9 }
400                // If we translate the above to `__assign({}, k, l)`, the `l` will evaluate before `k` is spread and we
401                // end up with `{ a: 1, b: 2, c: 3 }`
402                const objects = chunkObjectLiteralElements(node.properties);
403                if (objects.length && objects[0].kind !== SyntaxKind.ObjectLiteralExpression) {
404                    objects.unshift(factory.createObjectLiteralExpression());
405                }
406                let expression: Expression = objects[0];
407                if (objects.length > 1) {
408                    for (let i = 1; i < objects.length; i++) {
409                        expression = emitHelpers().createAssignHelper([expression, objects[i]]);
410                    }
411                    return expression;
412                }
413                else {
414                    return emitHelpers().createAssignHelper(objects);
415                }
416            }
417            return visitEachChild(node, visitor, context);
418        }
419
420        function visitExpressionStatement(node: ExpressionStatement): ExpressionStatement {
421            return visitEachChild(node, visitorWithUnusedExpressionResult, context);
422        }
423
424        /**
425         * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the
426         * expression of an `ExpressionStatement`).
427         */
428        function visitParenthesizedExpression(node: ParenthesizedExpression, expressionResultIsUnused: boolean): ParenthesizedExpression {
429            return visitEachChild(node, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, context);
430        }
431
432        function visitSourceFile(node: SourceFile): SourceFile {
433            const ancestorFacts = enterSubtree(
434                HierarchyFacts.SourceFileExcludes,
435                isEffectiveStrictModeSourceFile(node, compilerOptions) ?
436                    HierarchyFacts.StrictModeSourceFileIncludes :
437                    HierarchyFacts.SourceFileIncludes);
438            exportedVariableStatement = false;
439            const visited = visitEachChild(node, visitor, context);
440            const statement = concatenate(visited.statements, taggedTemplateStringDeclarations && [
441                factory.createVariableStatement(/*modifiers*/ undefined,
442                    factory.createVariableDeclarationList(taggedTemplateStringDeclarations))
443            ]);
444            const result = factory.updateSourceFile(visited, setTextRange(factory.createNodeArray(statement), node.statements));
445            exitSubtree(ancestorFacts);
446            return result;
447        }
448
449        function visitTaggedTemplateExpression(node: TaggedTemplateExpression) {
450            return processTaggedTemplateExpression(
451                context,
452                node,
453                visitor,
454                currentSourceFile,
455                recordTaggedTemplateString,
456                ProcessLevel.LiftRestriction
457            );
458        }
459
460        /**
461         * Visits a BinaryExpression that contains a destructuring assignment.
462         *
463         * @param node A BinaryExpression node.
464         * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the
465         * expression of an `ExpressionStatement`).
466         */
467        function visitBinaryExpression(node: BinaryExpression, expressionResultIsUnused: boolean): Expression {
468            if (isDestructuringAssignment(node) && node.left.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
469                return flattenDestructuringAssignment(
470                    node,
471                    visitor,
472                    context,
473                    FlattenLevel.ObjectRest,
474                    !expressionResultIsUnused
475                );
476            }
477            if (node.operatorToken.kind === SyntaxKind.CommaToken) {
478                return factory.updateBinaryExpression(
479                    node,
480                    visitNode(node.left, visitorWithUnusedExpressionResult, isExpression),
481                    node.operatorToken,
482                    visitNode(node.right, expressionResultIsUnused ? visitorWithUnusedExpressionResult : visitor, isExpression)
483                );
484            }
485            return visitEachChild(node, visitor, context);
486        }
487
488        /**
489         * @param expressionResultIsUnused Indicates the result of an expression is unused by the parent node (i.e., the left side of a comma or the
490         * expression of an `ExpressionStatement`).
491         */
492        function visitCommaListExpression(node: CommaListExpression, expressionResultIsUnused: boolean): Expression {
493            if (expressionResultIsUnused) {
494                return visitEachChild(node, visitorWithUnusedExpressionResult, context);
495            }
496            let result: Expression[] | undefined;
497            for (let i = 0; i < node.elements.length; i++) {
498                const element = node.elements[i];
499                const visited = visitNode(element, i < node.elements.length - 1 ? visitorWithUnusedExpressionResult : visitor, isExpression);
500                if (result || visited !== element) {
501                    result ||= node.elements.slice(0, i);
502                    result.push(visited);
503                }
504            }
505            const elements = result ? setTextRange(factory.createNodeArray(result), node.elements) : node.elements;
506            return factory.updateCommaListExpression(node, elements);
507        }
508
509        function visitCatchClause(node: CatchClause) {
510            if (node.variableDeclaration &&
511                isBindingPattern(node.variableDeclaration.name) &&
512                node.variableDeclaration.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
513                const name = factory.getGeneratedNameForNode(node.variableDeclaration.name);
514                const updatedDecl = factory.updateVariableDeclaration(node.variableDeclaration, node.variableDeclaration.name, /*exclamationToken*/ undefined, /*type*/ undefined, name);
515                const visitedBindings = flattenDestructuringBinding(updatedDecl, visitor, context, FlattenLevel.ObjectRest);
516                let block = visitNode(node.block, visitor, isBlock);
517                if (some(visitedBindings)) {
518                    block = factory.updateBlock(block, [
519                        factory.createVariableStatement(/*modifiers*/ undefined, visitedBindings),
520                        ...block.statements,
521                    ]);
522                }
523                return factory.updateCatchClause(
524                    node,
525                    factory.updateVariableDeclaration(node.variableDeclaration, name, /*exclamationToken*/ undefined, /*type*/ undefined, /*initializer*/ undefined),
526                    block);
527            }
528            return visitEachChild(node, visitor, context);
529        }
530
531        function visitVariableStatement(node: VariableStatement): VisitResult<VariableStatement> {
532            if (hasSyntacticModifier(node, ModifierFlags.Export)) {
533                const savedExportedVariableStatement = exportedVariableStatement;
534                exportedVariableStatement = true;
535                const visited = visitEachChild(node, visitor, context);
536                exportedVariableStatement = savedExportedVariableStatement;
537                return visited;
538            }
539            return visitEachChild(node, visitor, context);
540        }
541
542        /**
543         * Visits a VariableDeclaration node with a binding pattern.
544         *
545         * @param node A VariableDeclaration node.
546         */
547        function visitVariableDeclaration(node: VariableDeclaration): VisitResult<VariableDeclaration> {
548            if (exportedVariableStatement) {
549                const savedExportedVariableStatement = exportedVariableStatement;
550                exportedVariableStatement = false;
551                const visited = visitVariableDeclarationWorker(node, /*exportedVariableStatement*/ true);
552                exportedVariableStatement = savedExportedVariableStatement;
553                return visited;
554            }
555            return visitVariableDeclarationWorker(node, /*exportedVariableStatement*/ false);
556        }
557
558        function visitVariableDeclarationWorker(node: VariableDeclaration, exportedVariableStatement: boolean): VisitResult<VariableDeclaration> {
559            // If we are here it is because the name contains a binding pattern with a rest somewhere in it.
560            if (isBindingPattern(node.name) && node.name.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
561                return flattenDestructuringBinding(
562                    node,
563                    visitor,
564                    context,
565                    FlattenLevel.ObjectRest,
566                    /*rval*/ undefined,
567                    exportedVariableStatement
568                );
569            }
570            return visitEachChild(node, visitor, context);
571        }
572
573        function visitForStatement(node: ForStatement): VisitResult<Statement> {
574            return factory.updateForStatement(
575                node,
576                visitNode(node.initializer, visitorWithUnusedExpressionResult, isForInitializer),
577                visitNode(node.condition, visitor, isExpression),
578                visitNode(node.incrementor, visitorWithUnusedExpressionResult, isExpression),
579                visitIterationBody(node.statement, visitor, context)
580            );
581        }
582
583        function visitVoidExpression(node: VoidExpression) {
584            return visitEachChild(node, visitorWithUnusedExpressionResult, context);
585        }
586
587        /**
588         * Visits a ForOfStatement and converts it into a ES2015-compatible ForOfStatement.
589         *
590         * @param node A ForOfStatement.
591         */
592        function visitForOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined): VisitResult<Statement> {
593            const ancestorFacts = enterSubtree(HierarchyFacts.IterationStatementExcludes, HierarchyFacts.IterationStatementIncludes);
594            if (node.initializer.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
595                node = transformForOfStatementWithObjectRest(node);
596            }
597            const result = node.awaitModifier ?
598                transformForAwaitOfStatement(node, outermostLabeledStatement, ancestorFacts) :
599                factory.restoreEnclosingLabel(visitEachChild(node, visitor, context), outermostLabeledStatement);
600            exitSubtree(ancestorFacts);
601            return result;
602        }
603
604        function transformForOfStatementWithObjectRest(node: ForOfStatement) {
605            const initializerWithoutParens = skipParentheses(node.initializer) as ForInitializer;
606            if (isVariableDeclarationList(initializerWithoutParens) || isAssignmentPattern(initializerWithoutParens)) {
607                let bodyLocation: TextRange | undefined;
608                let statementsLocation: TextRange | undefined;
609                const temp = factory.createTempVariable(/*recordTempVariable*/ undefined);
610                const statements: Statement[] = [createForOfBindingStatement(factory, initializerWithoutParens, temp)];
611                if (isBlock(node.statement)) {
612                    addRange(statements, node.statement.statements);
613                    bodyLocation = node.statement;
614                    statementsLocation = node.statement.statements;
615                }
616                else if (node.statement) {
617                    append(statements, node.statement);
618                    bodyLocation = node.statement;
619                    statementsLocation = node.statement;
620                }
621                return factory.updateForOfStatement(
622                    node,
623                    node.awaitModifier,
624                    setTextRange(
625                        factory.createVariableDeclarationList(
626                            [
627                                setTextRange(factory.createVariableDeclaration(temp), node.initializer)
628                            ],
629                            NodeFlags.Let
630                        ),
631                        node.initializer
632                    ),
633                    node.expression,
634                    setTextRange(
635                        factory.createBlock(
636                            setTextRange(factory.createNodeArray(statements), statementsLocation),
637                            /*multiLine*/ true
638                        ),
639                        bodyLocation
640                    )
641                );
642            }
643            return node;
644        }
645
646        function convertForOfStatementHead(node: ForOfStatement, boundValue: Expression, nonUserCode: Identifier) {
647            const value = factory.createTempVariable(hoistVariableDeclaration);
648            const iteratorValueExpression = factory.createAssignment(value, boundValue);
649            const iteratorValueStatement = factory.createExpressionStatement(iteratorValueExpression);
650            setSourceMapRange(iteratorValueStatement, node.expression);
651
652            const exitNonUserCodeExpression = factory.createAssignment(nonUserCode, factory.createFalse());
653            const exitNonUserCodeStatement = factory.createExpressionStatement(exitNonUserCodeExpression);
654            setSourceMapRange(exitNonUserCodeStatement, node.expression);
655
656            const enterNonUserCodeExpression = factory.createAssignment(nonUserCode, factory.createTrue());
657            const enterNonUserCodeStatement = factory.createExpressionStatement(enterNonUserCodeExpression);
658            setSourceMapRange(exitNonUserCodeStatement, node.expression);
659
660            const statements: Statement[] = [];
661            const binding = createForOfBindingStatement(factory, node.initializer, value);
662            statements.push(visitNode(binding, visitor, isStatement));
663
664            let bodyLocation: TextRange | undefined;
665            let statementsLocation: TextRange | undefined;
666            const statement = visitIterationBody(node.statement, visitor, context);
667            if (isBlock(statement)) {
668                addRange(statements, statement.statements);
669                bodyLocation = statement;
670                statementsLocation = statement.statements;
671            }
672            else {
673                statements.push(statement);
674            }
675
676            const body = setEmitFlags(
677                setTextRange(
678                    factory.createBlock(
679                        setTextRange(factory.createNodeArray(statements), statementsLocation),
680                        /*multiLine*/ true
681                    ),
682                    bodyLocation
683                ),
684                EmitFlags.NoSourceMap | EmitFlags.NoTokenSourceMaps
685            );
686
687            return factory.createBlock([
688                iteratorValueStatement,
689                exitNonUserCodeStatement,
690                factory.createTryStatement(
691                    body,
692                    /*catchClause*/ undefined,
693                    factory.createBlock([
694                        enterNonUserCodeStatement
695                    ])
696                )
697            ]);
698        }
699
700        function createDownlevelAwait(expression: Expression) {
701            return enclosingFunctionFlags & FunctionFlags.Generator
702                ? factory.createYieldExpression(/*asteriskToken*/ undefined, emitHelpers().createAwaitHelper(expression))
703                : factory.createAwaitExpression(expression);
704        }
705
706        function transformForAwaitOfStatement(node: ForOfStatement, outermostLabeledStatement: LabeledStatement | undefined, ancestorFacts: HierarchyFacts) {
707            const expression = visitNode(node.expression, visitor, isExpression);
708            const iterator = isIdentifier(expression) ? factory.getGeneratedNameForNode(expression) : factory.createTempVariable(/*recordTempVariable*/ undefined);
709            const result = isIdentifier(expression) ? factory.getGeneratedNameForNode(iterator) : factory.createTempVariable(/*recordTempVariable*/ undefined);
710            const nonUserCode = factory.createTempVariable(/*recordTempVariable*/ undefined);
711            const done = factory.createTempVariable(hoistVariableDeclaration);
712            const errorRecord = factory.createUniqueName("e");
713            const catchVariable = factory.getGeneratedNameForNode(errorRecord);
714            const returnMethod = factory.createTempVariable(/*recordTempVariable*/ undefined);
715            const callValues = setTextRange(emitHelpers().createAsyncValuesHelper(expression), node.expression);
716            const callNext = factory.createCallExpression(factory.createPropertyAccessExpression(iterator, "next"), /*typeArguments*/ undefined, []);
717            const getDone = factory.createPropertyAccessExpression(result, "done");
718            const getValue = factory.createPropertyAccessExpression(result, "value");
719            const callReturn = factory.createFunctionCallCall(returnMethod, iterator, []);
720
721            hoistVariableDeclaration(errorRecord);
722            hoistVariableDeclaration(returnMethod);
723
724            // if we are enclosed in an outer loop ensure we reset 'errorRecord' per each iteration
725            const initializer = ancestorFacts & HierarchyFacts.IterationContainer ?
726                factory.inlineExpressions([factory.createAssignment(errorRecord, factory.createVoidZero()), callValues]) :
727                callValues;
728
729            const forStatement = setEmitFlags(
730                setTextRange(
731                    factory.createForStatement(
732                        /*initializer*/ setEmitFlags(
733                            setTextRange(
734                                factory.createVariableDeclarationList([
735                                    factory.createVariableDeclaration(nonUserCode, /*exclamationToken*/ undefined, /*type*/ undefined, factory.createTrue()),
736                                    setTextRange(factory.createVariableDeclaration(iterator, /*exclamationToken*/ undefined, /*type*/ undefined, initializer), node.expression),
737                                    factory.createVariableDeclaration(result)
738                                ]),
739                                node.expression
740                            ),
741                            EmitFlags.NoHoisting
742                        ),
743                        /*condition*/ factory.inlineExpressions([
744                            factory.createAssignment(result, createDownlevelAwait(callNext)),
745                            factory.createAssignment(done, getDone),
746                            factory.createLogicalNot(done)
747                        ]),
748                        /*incrementor*/ undefined,
749                        /*statement*/ convertForOfStatementHead(node, getValue, nonUserCode)
750                    ),
751                    /*location*/ node
752                ),
753                EmitFlags.NoTokenTrailingSourceMaps
754            );
755            setOriginalNode(forStatement, node);
756
757            return factory.createTryStatement(
758                factory.createBlock([
759                    factory.restoreEnclosingLabel(
760                        forStatement,
761                        outermostLabeledStatement
762                    )
763                ]),
764                factory.createCatchClause(
765                    factory.createVariableDeclaration(catchVariable),
766                    setEmitFlags(
767                        factory.createBlock([
768                            factory.createExpressionStatement(
769                                factory.createAssignment(
770                                    errorRecord,
771                                    factory.createObjectLiteralExpression([
772                                        factory.createPropertyAssignment("error", catchVariable)
773                                    ])
774                                )
775                            )
776                        ]),
777                        EmitFlags.SingleLine
778                    )
779                ),
780                factory.createBlock([
781                    factory.createTryStatement(
782                        /*tryBlock*/ factory.createBlock([
783                            setEmitFlags(
784                                factory.createIfStatement(
785                                    factory.createLogicalAnd(
786                                        factory.createLogicalAnd(
787                                            factory.createLogicalNot(nonUserCode),
788                                            factory.createLogicalNot(done),
789                                        ),
790                                        factory.createAssignment(
791                                            returnMethod,
792                                            factory.createPropertyAccessExpression(iterator, "return")
793                                        )
794                                    ),
795                                    factory.createExpressionStatement(createDownlevelAwait(callReturn))
796                                ),
797                                EmitFlags.SingleLine
798                            )
799                        ]),
800                        /*catchClause*/ undefined,
801                        /*finallyBlock*/ setEmitFlags(
802                            factory.createBlock([
803                                setEmitFlags(
804                                    factory.createIfStatement(
805                                        errorRecord,
806                                        factory.createThrowStatement(
807                                            factory.createPropertyAccessExpression(errorRecord, "error")
808                                        )
809                                    ),
810                                    EmitFlags.SingleLine
811                                )
812                            ]),
813                            EmitFlags.SingleLine
814                        )
815                    )
816                ])
817            );
818        }
819
820        function parameterVisitor(node: Node) {
821            Debug.assertNode(node, isParameter);
822            return visitParameter(node);
823        }
824
825        function visitParameter(node: ParameterDeclaration): ParameterDeclaration {
826            if (parametersWithPrecedingObjectRestOrSpread?.has(node)) {
827                return factory.updateParameterDeclaration(
828                    node,
829                    /*modifiers*/ undefined,
830                    node.dotDotDotToken,
831                    isBindingPattern(node.name) ? factory.getGeneratedNameForNode(node) : node.name,
832                    /*questionToken*/ undefined,
833                    /*type*/ undefined,
834                    /*initializer*/ undefined
835                );
836            }
837            if (node.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
838                // Binding patterns are converted into a generated name and are
839                // evaluated inside the function body.
840                return factory.updateParameterDeclaration(
841                    node,
842                    /*modifiers*/ undefined,
843                    node.dotDotDotToken,
844                    factory.getGeneratedNameForNode(node),
845                    /*questionToken*/ undefined,
846                    /*type*/ undefined,
847                    visitNode(node.initializer, visitor, isExpression)
848                );
849            }
850            return visitEachChild(node, visitor, context);
851        }
852
853        function collectParametersWithPrecedingObjectRestOrSpread(node: SignatureDeclaration) {
854            let parameters: Set<ParameterDeclaration> | undefined;
855            for (const parameter of node.parameters) {
856                if (parameters) {
857                    parameters.add(parameter);
858                }
859                else if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
860                    parameters = new Set();
861                }
862            }
863            return parameters;
864        }
865
866        function visitConstructorDeclaration(node: ConstructorDeclaration) {
867            const savedEnclosingFunctionFlags = enclosingFunctionFlags;
868            const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
869            enclosingFunctionFlags = getFunctionFlags(node);
870            parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
871            const updated = factory.updateConstructorDeclaration(
872                node,
873                node.modifiers,
874                visitParameterList(node.parameters, parameterVisitor, context),
875                transformFunctionBody(node)
876            );
877            enclosingFunctionFlags = savedEnclosingFunctionFlags;
878            parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
879            return updated;
880        }
881
882        function visitGetAccessorDeclaration(node: GetAccessorDeclaration) {
883            const savedEnclosingFunctionFlags = enclosingFunctionFlags;
884            const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
885            enclosingFunctionFlags = getFunctionFlags(node);
886            parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
887            const updated = factory.updateGetAccessorDeclaration(
888                node,
889                node.modifiers,
890                visitNode(node.name, visitor, isPropertyName),
891                visitParameterList(node.parameters, parameterVisitor, context),
892                /*type*/ undefined,
893                transformFunctionBody(node)
894            );
895            enclosingFunctionFlags = savedEnclosingFunctionFlags;
896            parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
897            return updated;
898        }
899
900        function visitSetAccessorDeclaration(node: SetAccessorDeclaration) {
901            const savedEnclosingFunctionFlags = enclosingFunctionFlags;
902            const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
903            enclosingFunctionFlags = getFunctionFlags(node);
904            parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
905            const updated = factory.updateSetAccessorDeclaration(
906                node,
907                node.modifiers,
908                visitNode(node.name, visitor, isPropertyName),
909                visitParameterList(node.parameters, parameterVisitor, context),
910                transformFunctionBody(node)
911            );
912            enclosingFunctionFlags = savedEnclosingFunctionFlags;
913            parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
914            return updated;
915        }
916
917        function visitMethodDeclaration(node: MethodDeclaration) {
918            const savedEnclosingFunctionFlags = enclosingFunctionFlags;
919            const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
920            enclosingFunctionFlags = getFunctionFlags(node);
921            parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
922            const updated = factory.updateMethodDeclaration(
923                node,
924                enclosingFunctionFlags & FunctionFlags.Generator
925                    ? visitNodes(node.modifiers, visitorNoAsyncModifier, isModifierLike)
926                    : node.modifiers,
927                enclosingFunctionFlags & FunctionFlags.Async
928                    ? undefined
929                    : node.asteriskToken,
930                visitNode(node.name, visitor, isPropertyName),
931                visitNode<Token<SyntaxKind.QuestionToken>>(/*questionToken*/ undefined, visitor, isToken),
932                /*typeParameters*/ undefined,
933                visitParameterList(node.parameters, parameterVisitor, context),
934                /*type*/ undefined,
935                enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
936                    ? transformAsyncGeneratorFunctionBody(node)
937                    : transformFunctionBody(node)
938            );
939            enclosingFunctionFlags = savedEnclosingFunctionFlags;
940            parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
941            return updated;
942        }
943
944        function visitFunctionDeclaration(node: FunctionDeclaration) {
945            const savedEnclosingFunctionFlags = enclosingFunctionFlags;
946            const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
947            enclosingFunctionFlags = getFunctionFlags(node);
948            parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
949            const updated = factory.updateFunctionDeclaration(
950                node,
951                enclosingFunctionFlags & FunctionFlags.Generator
952                    ? visitNodes(node.modifiers, visitorNoAsyncModifier, isModifier)
953                    : node.modifiers,
954                enclosingFunctionFlags & FunctionFlags.Async
955                    ? undefined
956                    : node.asteriskToken,
957                node.name,
958                /*typeParameters*/ undefined,
959                visitParameterList(node.parameters, parameterVisitor, context),
960                /*type*/ undefined,
961                enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
962                    ? transformAsyncGeneratorFunctionBody(node)
963                    : transformFunctionBody(node)
964            );
965            enclosingFunctionFlags = savedEnclosingFunctionFlags;
966            parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
967            return updated;
968        }
969
970        function visitArrowFunction(node: ArrowFunction) {
971            const savedEnclosingFunctionFlags = enclosingFunctionFlags;
972            const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
973            enclosingFunctionFlags = getFunctionFlags(node);
974            parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
975            const updated = factory.updateArrowFunction(
976                node,
977                node.modifiers,
978                /*typeParameters*/ undefined,
979                visitParameterList(node.parameters, parameterVisitor, context),
980                /*type*/ undefined,
981                node.equalsGreaterThanToken,
982                transformFunctionBody(node),
983            );
984            enclosingFunctionFlags = savedEnclosingFunctionFlags;
985            parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
986            return updated;
987        }
988
989        function visitFunctionExpression(node: FunctionExpression) {
990            const savedEnclosingFunctionFlags = enclosingFunctionFlags;
991            const savedParametersWithPrecedingObjectRestOrSpread = parametersWithPrecedingObjectRestOrSpread;
992            enclosingFunctionFlags = getFunctionFlags(node);
993            parametersWithPrecedingObjectRestOrSpread = collectParametersWithPrecedingObjectRestOrSpread(node);
994            const updated = factory.updateFunctionExpression(
995                node,
996                enclosingFunctionFlags & FunctionFlags.Generator
997                    ? visitNodes(node.modifiers, visitorNoAsyncModifier, isModifier)
998                    : node.modifiers,
999                enclosingFunctionFlags & FunctionFlags.Async
1000                    ? undefined
1001                    : node.asteriskToken,
1002                node.name,
1003                /*typeParameters*/ undefined,
1004                visitParameterList(node.parameters, parameterVisitor, context),
1005                /*type*/ undefined,
1006                enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator
1007                    ? transformAsyncGeneratorFunctionBody(node)
1008                    : transformFunctionBody(node)
1009            );
1010            enclosingFunctionFlags = savedEnclosingFunctionFlags;
1011            parametersWithPrecedingObjectRestOrSpread = savedParametersWithPrecedingObjectRestOrSpread;
1012            return updated;
1013        }
1014
1015        function transformAsyncGeneratorFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody {
1016            resumeLexicalEnvironment();
1017            const statements: Statement[] = [];
1018            const statementOffset = factory.copyPrologue(node.body!.statements, statements, /*ensureUseStrict*/ false, visitor);
1019            appendObjectRestAssignmentsIfNeeded(statements, node);
1020
1021            const savedCapturedSuperProperties = capturedSuperProperties;
1022            const savedHasSuperElementAccess = hasSuperElementAccess;
1023            capturedSuperProperties = new Set();
1024            hasSuperElementAccess = false;
1025
1026            const returnStatement = factory.createReturnStatement(
1027                emitHelpers().createAsyncGeneratorHelper(
1028                    factory.createFunctionExpression(
1029                        /*modifiers*/ undefined,
1030                        factory.createToken(SyntaxKind.AsteriskToken),
1031                        node.name && factory.getGeneratedNameForNode(node.name),
1032                        /*typeParameters*/ undefined,
1033                        /*parameters*/ [],
1034                        /*type*/ undefined,
1035                        factory.updateBlock(
1036                            node.body!,
1037                            visitLexicalEnvironment(node.body!.statements, visitor, context, statementOffset)
1038                        )
1039                    ),
1040                    !!(hierarchyFacts & HierarchyFacts.HasLexicalThis)
1041                )
1042            );
1043
1044            // Minor optimization, emit `_super` helper to capture `super` access in an arrow.
1045            // This step isn't needed if we eventually transform this to ES5.
1046            const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync);
1047
1048            if (emitSuperHelpers) {
1049                enableSubstitutionForAsyncMethodsWithSuper();
1050                const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties);
1051                substitutedSuperAccessors[getNodeId(variableStatement)] = true;
1052                insertStatementsAfterStandardPrologue(statements, [variableStatement]);
1053            }
1054
1055            statements.push(returnStatement);
1056
1057            insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
1058            const block = factory.updateBlock(node.body!, statements);
1059
1060            if (emitSuperHelpers && hasSuperElementAccess) {
1061                if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
1062                    addEmitHelper(block, advancedAsyncSuperHelper);
1063                }
1064                else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) {
1065                    addEmitHelper(block, asyncSuperHelper);
1066                }
1067            }
1068
1069            capturedSuperProperties = savedCapturedSuperProperties;
1070            hasSuperElementAccess = savedHasSuperElementAccess;
1071
1072            return block;
1073        }
1074
1075        function transformFunctionBody(node: FunctionDeclaration | FunctionExpression | ConstructorDeclaration | MethodDeclaration | AccessorDeclaration): FunctionBody;
1076        function transformFunctionBody(node: ArrowFunction): ConciseBody;
1077        function transformFunctionBody(node: FunctionLikeDeclaration): ConciseBody {
1078            resumeLexicalEnvironment();
1079            let statementOffset = 0;
1080            const statements: Statement[] = [];
1081            const body = visitNode(node.body, visitor, isConciseBody) ?? factory.createBlock([]);
1082            if (isBlock(body)) {
1083                statementOffset = factory.copyPrologue(body.statements, statements, /*ensureUseStrict*/ false, visitor);
1084            }
1085            addRange(statements, appendObjectRestAssignmentsIfNeeded(/*statements*/ undefined, node));
1086
1087            const leadingStatements = endLexicalEnvironment();
1088            if (statementOffset > 0 || some(statements) || some(leadingStatements)) {
1089                const block = factory.converters.convertToFunctionBlock(body, /*multiLine*/ true);
1090                insertStatementsAfterStandardPrologue(statements, leadingStatements);
1091                addRange(statements, block.statements.slice(statementOffset));
1092                return factory.updateBlock(block, setTextRange(factory.createNodeArray(statements), block.statements));
1093            }
1094            return body;
1095        }
1096
1097        function appendObjectRestAssignmentsIfNeeded(statements: Statement[] | undefined, node: FunctionLikeDeclaration): Statement[] | undefined {
1098            let containsPrecedingObjectRestOrSpread = false;
1099            for (const parameter of node.parameters) {
1100                if (containsPrecedingObjectRestOrSpread) {
1101                    if (isBindingPattern(parameter.name)) {
1102                        // In cases where a binding pattern is simply '[]' or '{}',
1103                        // we usually don't want to emit a var declaration; however, in the presence
1104                        // of an initializer, we must emit that expression to preserve side effects.
1105                        //
1106                        // NOTE: see `insertDefaultValueAssignmentForBindingPattern` in es2015.ts
1107                        if (parameter.name.elements.length > 0) {
1108                            const declarations = flattenDestructuringBinding(
1109                                parameter,
1110                                visitor,
1111                                context,
1112                                FlattenLevel.All,
1113                                factory.getGeneratedNameForNode(parameter));
1114                            if (some(declarations)) {
1115                                const declarationList = factory.createVariableDeclarationList(declarations);
1116                                const statement = factory.createVariableStatement(/*modifiers*/ undefined, declarationList);
1117                                setEmitFlags(statement, EmitFlags.CustomPrologue);
1118                                statements = append(statements, statement);
1119                            }
1120                        }
1121                        else if (parameter.initializer) {
1122                            const name = factory.getGeneratedNameForNode(parameter);
1123                            const initializer = visitNode(parameter.initializer, visitor, isExpression);
1124                            const assignment = factory.createAssignment(name, initializer);
1125                            const statement = factory.createExpressionStatement(assignment);
1126                            setEmitFlags(statement, EmitFlags.CustomPrologue);
1127                            statements = append(statements, statement);
1128                        }
1129                    }
1130                    else if (parameter.initializer) {
1131                        // Converts a parameter initializer into a function body statement, i.e.:
1132                        //
1133                        //  function f(x = 1) { }
1134                        //
1135                        // becomes
1136                        //
1137                        //  function f(x) {
1138                        //    if (typeof x === "undefined") { x = 1; }
1139                        //  }
1140
1141                        const name = factory.cloneNode(parameter.name);
1142                        setTextRange(name, parameter.name);
1143                        setEmitFlags(name, EmitFlags.NoSourceMap);
1144
1145                        const initializer = visitNode(parameter.initializer, visitor, isExpression);
1146                        addEmitFlags(initializer, EmitFlags.NoSourceMap | EmitFlags.NoComments);
1147
1148                        const assignment = factory.createAssignment(name, initializer);
1149                        setTextRange(assignment, parameter);
1150                        setEmitFlags(assignment, EmitFlags.NoComments);
1151
1152                        const block = factory.createBlock([factory.createExpressionStatement(assignment)]);
1153                        setTextRange(block, parameter);
1154                        setEmitFlags(block, EmitFlags.SingleLine | EmitFlags.NoTrailingSourceMap | EmitFlags.NoTokenSourceMaps | EmitFlags.NoComments);
1155
1156                        const typeCheck = factory.createTypeCheck(factory.cloneNode(parameter.name), "undefined");
1157                        const statement = factory.createIfStatement(typeCheck, block);
1158                        startOnNewLine(statement);
1159                        setTextRange(statement, parameter);
1160                        setEmitFlags(statement, EmitFlags.NoTokenSourceMaps | EmitFlags.NoTrailingSourceMap | EmitFlags.CustomPrologue | EmitFlags.NoComments);
1161                        statements = append(statements, statement);
1162                    }
1163                }
1164                else if (parameter.transformFlags & TransformFlags.ContainsObjectRestOrSpread) {
1165                    containsPrecedingObjectRestOrSpread = true;
1166                    const declarations = flattenDestructuringBinding(
1167                        parameter,
1168                        visitor,
1169                        context,
1170                        FlattenLevel.ObjectRest,
1171                        factory.getGeneratedNameForNode(parameter),
1172                        /*doNotRecordTempVariablesInLine*/ false,
1173                        /*skipInitializer*/ true,
1174                    );
1175                    if (some(declarations)) {
1176                        const declarationList = factory.createVariableDeclarationList(declarations);
1177                        const statement = factory.createVariableStatement(/*modifiers*/ undefined, declarationList);
1178                        setEmitFlags(statement, EmitFlags.CustomPrologue);
1179                        statements = append(statements, statement);
1180                    }
1181                }
1182            }
1183            return statements;
1184        }
1185
1186        function enableSubstitutionForAsyncMethodsWithSuper() {
1187            if ((enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper) === 0) {
1188                enabledSubstitutions |= ESNextSubstitutionFlags.AsyncMethodsWithSuper;
1189
1190                // We need to enable substitutions for call, property access, and element access
1191                // if we need to rewrite super calls.
1192                context.enableSubstitution(SyntaxKind.CallExpression);
1193                context.enableSubstitution(SyntaxKind.PropertyAccessExpression);
1194                context.enableSubstitution(SyntaxKind.ElementAccessExpression);
1195
1196                // We need to be notified when entering and exiting declarations that bind super.
1197                context.enableEmitNotification(SyntaxKind.ClassDeclaration);
1198                context.enableEmitNotification(SyntaxKind.MethodDeclaration);
1199                context.enableEmitNotification(SyntaxKind.GetAccessor);
1200                context.enableEmitNotification(SyntaxKind.SetAccessor);
1201                context.enableEmitNotification(SyntaxKind.Constructor);
1202                // We need to be notified when entering the generated accessor arrow functions.
1203                context.enableEmitNotification(SyntaxKind.VariableStatement);
1204            }
1205        }
1206
1207        /**
1208         * Called by the printer just before a node is printed.
1209         *
1210         * @param hint A hint as to the intended usage of the node.
1211         * @param node The node to be printed.
1212         * @param emitCallback The callback used to emit the node.
1213         */
1214        function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void) {
1215            // If we need to support substitutions for `super` in an async method,
1216            // we should track it here.
1217            if (enabledSubstitutions & ESNextSubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
1218                const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAccessInAsync | NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync);
1219                if (superContainerFlags !== enclosingSuperContainerFlags) {
1220                    const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
1221                    enclosingSuperContainerFlags = superContainerFlags;
1222                    previousOnEmitNode(hint, node, emitCallback);
1223                    enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
1224                    return;
1225                }
1226            }
1227            // Disable substitution in the generated super accessor itself.
1228            else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) {
1229                const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
1230                enclosingSuperContainerFlags = 0 as NodeCheckFlags;
1231                previousOnEmitNode(hint, node, emitCallback);
1232                enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
1233                return;
1234            }
1235
1236            previousOnEmitNode(hint, node, emitCallback);
1237        }
1238
1239        /**
1240         * Hooks node substitutions.
1241         *
1242         * @param hint The context for the emitter.
1243         * @param node The node to substitute.
1244         */
1245        function onSubstituteNode(hint: EmitHint, node: Node) {
1246            node = previousOnSubstituteNode(hint, node);
1247            if (hint === EmitHint.Expression && enclosingSuperContainerFlags) {
1248                return substituteExpression(node as Expression);
1249            }
1250            return node;
1251        }
1252
1253        function substituteExpression(node: Expression) {
1254            switch (node.kind) {
1255                case SyntaxKind.PropertyAccessExpression:
1256                    return substitutePropertyAccessExpression(node as PropertyAccessExpression);
1257                case SyntaxKind.ElementAccessExpression:
1258                    return substituteElementAccessExpression(node as ElementAccessExpression);
1259                case SyntaxKind.CallExpression:
1260                    return substituteCallExpression(node as CallExpression);
1261            }
1262            return node;
1263        }
1264
1265        function substitutePropertyAccessExpression(node: PropertyAccessExpression) {
1266            if (node.expression.kind === SyntaxKind.SuperKeyword) {
1267                return setTextRange(
1268                    factory.createPropertyAccessExpression(
1269                        factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel),
1270                        node.name),
1271                    node
1272                );
1273            }
1274            return node;
1275        }
1276
1277        function substituteElementAccessExpression(node: ElementAccessExpression) {
1278            if (node.expression.kind === SyntaxKind.SuperKeyword) {
1279                return createSuperElementAccessInAsyncMethod(
1280                    node.argumentExpression,
1281                    node
1282                );
1283            }
1284            return node;
1285        }
1286
1287        function substituteCallExpression(node: CallExpression): Expression {
1288            const expression = node.expression;
1289            if (isSuperProperty(expression)) {
1290                const argumentExpression = isPropertyAccessExpression(expression)
1291                    ? substitutePropertyAccessExpression(expression)
1292                    : substituteElementAccessExpression(expression);
1293                return factory.createCallExpression(
1294                    factory.createPropertyAccessExpression(argumentExpression, "call"),
1295                    /*typeArguments*/ undefined,
1296                    [
1297                        factory.createThis(),
1298                        ...node.arguments
1299                    ]
1300                );
1301            }
1302            return node;
1303        }
1304
1305        function isSuperContainer(node: Node) {
1306            const kind = node.kind;
1307            return kind === SyntaxKind.ClassDeclaration
1308                || kind === SyntaxKind.Constructor
1309                || kind === SyntaxKind.MethodDeclaration
1310                || kind === SyntaxKind.GetAccessor
1311                || kind === SyntaxKind.SetAccessor;
1312        }
1313
1314        function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression {
1315            if (enclosingSuperContainerFlags & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
1316                return setTextRange(
1317                    factory.createPropertyAccessExpression(
1318                        factory.createCallExpression(
1319                            factory.createIdentifier("_superIndex"),
1320                            /*typeArguments*/ undefined,
1321                            [argumentExpression]
1322                        ),
1323                        "value"
1324                    ),
1325                    location
1326                );
1327            }
1328            else {
1329                return setTextRange(
1330                    factory.createCallExpression(
1331                        factory.createIdentifier("_superIndex"),
1332                        /*typeArguments*/ undefined,
1333                        [argumentExpression]
1334                    ),
1335                    location
1336                );
1337            }
1338        }
1339    }
1340}
1341