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