• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*@internal*/
2namespace ts {
3    type SuperContainer = ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration;
4
5    const enum ES2017SubstitutionFlags {
6        /** Enables substitutions for async methods with `super` calls. */
7        AsyncMethodsWithSuper = 1 << 0
8    }
9
10    const enum ContextFlags {
11        NonTopLevel = 1 << 0,
12        HasLexicalThis = 1 << 1
13    }
14
15    export function transformES2017(context: TransformationContext) {
16        const {
17            factory,
18            getEmitHelperFactory: emitHelpers,
19            resumeLexicalEnvironment,
20            endLexicalEnvironment,
21            hoistVariableDeclaration
22        } = context;
23
24        const resolver = context.getEmitResolver();
25        const compilerOptions = context.getCompilerOptions();
26        const languageVersion = getEmitScriptTarget(compilerOptions);
27
28        /**
29         * Keeps track of whether expression substitution has been enabled for specific edge cases.
30         * They are persisted between each SourceFile transformation and should not be reset.
31         */
32        let enabledSubstitutions: ES2017SubstitutionFlags;
33
34        /**
35         * This keeps track of containers where `super` is valid, for use with
36         * just-in-time substitution for `super` expressions inside of async methods.
37         */
38        let enclosingSuperContainerFlags: NodeCheckFlags = 0;
39
40        let enclosingFunctionParameterNames: Set<__String>;
41
42        /**
43         * Keeps track of property names accessed on super (`super.x`) within async functions.
44         */
45        let capturedSuperProperties: Set<__String>;
46        /** Whether the async function contains an element access on super (`super[x]`). */
47        let hasSuperElementAccess: boolean;
48        /** A set of node IDs for generated super accessors (variable statements). */
49        const substitutedSuperAccessors: boolean[] = [];
50
51        let contextFlags: ContextFlags = 0;
52
53        // Save the previous transformation hooks.
54        const previousOnEmitNode = context.onEmitNode;
55        const previousOnSubstituteNode = context.onSubstituteNode;
56
57        // Set new transformation hooks.
58        context.onEmitNode = onEmitNode;
59        context.onSubstituteNode = onSubstituteNode;
60
61        return chainBundle(context, transformSourceFile);
62
63        function transformSourceFile(node: SourceFile) {
64            if (node.isDeclarationFile) {
65                return node;
66            }
67
68            setContextFlag(ContextFlags.NonTopLevel, false);
69            setContextFlag(ContextFlags.HasLexicalThis, !isEffectiveStrictModeSourceFile(node, compilerOptions));
70            const visited = visitEachChild(node, visitor, context);
71            addEmitHelpers(visited, context.readEmitHelpers());
72            return visited;
73        }
74
75        function setContextFlag(flag: ContextFlags, val: boolean) {
76            contextFlags = val ? contextFlags | flag : contextFlags & ~flag;
77        }
78
79        function inContext(flags: ContextFlags) {
80            return (contextFlags & flags) !== 0;
81        }
82
83        function inTopLevelContext() {
84            return !inContext(ContextFlags.NonTopLevel);
85        }
86
87        function inHasLexicalThisContext() {
88            return inContext(ContextFlags.HasLexicalThis);
89        }
90
91        function doWithContext<T, U>(flags: ContextFlags, cb: (value: T) => U, value: T) {
92            const contextFlagsToSet = flags & ~contextFlags;
93            if (contextFlagsToSet) {
94                setContextFlag(contextFlagsToSet, /*val*/ true);
95                const result = cb(value);
96                setContextFlag(contextFlagsToSet, /*val*/ false);
97                return result;
98            }
99            return cb(value);
100        }
101
102        function visitDefault(node: Node): VisitResult<Node> {
103            return visitEachChild(node, visitor, context);
104        }
105
106        function visitor(node: Node): VisitResult<Node> {
107            if ((node.transformFlags & TransformFlags.ContainsES2017) === 0) {
108                return node;
109            }
110            switch (node.kind) {
111                case SyntaxKind.AsyncKeyword:
112                    // ES2017 async modifier should be elided for targets < ES2017
113                    return undefined;
114
115                case SyntaxKind.AwaitExpression:
116                    return visitAwaitExpression(node as AwaitExpression);
117
118                case SyntaxKind.MethodDeclaration:
119                    return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitMethodDeclaration, node as MethodDeclaration);
120
121                case SyntaxKind.FunctionDeclaration:
122                    return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionDeclaration, node as FunctionDeclaration);
123
124                case SyntaxKind.FunctionExpression:
125                    return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionExpression, node as FunctionExpression);
126
127                case SyntaxKind.ArrowFunction:
128                    return doWithContext(ContextFlags.NonTopLevel, visitArrowFunction, node as ArrowFunction);
129
130                case SyntaxKind.PropertyAccessExpression:
131                    if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) {
132                        capturedSuperProperties.add(node.name.escapedText);
133                    }
134                    return visitEachChild(node, visitor, context);
135
136                case SyntaxKind.ElementAccessExpression:
137                    if (capturedSuperProperties && (node as ElementAccessExpression).expression.kind === SyntaxKind.SuperKeyword) {
138                        hasSuperElementAccess = true;
139                    }
140                    return visitEachChild(node, visitor, context);
141
142                case SyntaxKind.GetAccessor:
143                    return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitGetAccessorDeclaration, node as GetAccessorDeclaration);
144                case SyntaxKind.SetAccessor:
145                    return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitSetAccessorDeclaration, node as SetAccessorDeclaration);
146                case SyntaxKind.Constructor:
147                    return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitConstructorDeclaration, node as ConstructorDeclaration);
148                case SyntaxKind.ClassDeclaration:
149                case SyntaxKind.ClassExpression:
150                    return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitDefault, node);
151
152                default:
153                    return visitEachChild(node, visitor, context);
154            }
155        }
156
157        function asyncBodyVisitor(node: Node): VisitResult<Node> {
158            if (isNodeWithPossibleHoistedDeclaration(node)) {
159                switch (node.kind) {
160                    case SyntaxKind.VariableStatement:
161                        return visitVariableStatementInAsyncBody(node);
162                    case SyntaxKind.ForStatement:
163                        return visitForStatementInAsyncBody(node);
164                    case SyntaxKind.ForInStatement:
165                        return visitForInStatementInAsyncBody(node);
166                    case SyntaxKind.ForOfStatement:
167                        return visitForOfStatementInAsyncBody(node);
168                    case SyntaxKind.CatchClause:
169                        return visitCatchClauseInAsyncBody(node);
170                    case SyntaxKind.Block:
171                    case SyntaxKind.SwitchStatement:
172                    case SyntaxKind.CaseBlock:
173                    case SyntaxKind.CaseClause:
174                    case SyntaxKind.DefaultClause:
175                    case SyntaxKind.TryStatement:
176                    case SyntaxKind.DoStatement:
177                    case SyntaxKind.WhileStatement:
178                    case SyntaxKind.IfStatement:
179                    case SyntaxKind.WithStatement:
180                    case SyntaxKind.LabeledStatement:
181                        return visitEachChild(node, asyncBodyVisitor, context);
182                    default:
183                        return Debug.assertNever(node, "Unhandled node.");
184                }
185            }
186            return visitor(node);
187        }
188
189        function visitCatchClauseInAsyncBody(node: CatchClause) {
190            const catchClauseNames = new Set<__String>();
191            recordDeclarationName(node.variableDeclaration!, catchClauseNames); // TODO: GH#18217
192
193            // names declared in a catch variable are block scoped
194            let catchClauseUnshadowedNames: Set<__String> | undefined;
195            catchClauseNames.forEach((_, escapedName) => {
196                if (enclosingFunctionParameterNames.has(escapedName)) {
197                    if (!catchClauseUnshadowedNames) {
198                        catchClauseUnshadowedNames = new Set(enclosingFunctionParameterNames);
199                    }
200                    catchClauseUnshadowedNames.delete(escapedName);
201                }
202            });
203
204            if (catchClauseUnshadowedNames) {
205                const savedEnclosingFunctionParameterNames = enclosingFunctionParameterNames;
206                enclosingFunctionParameterNames = catchClauseUnshadowedNames;
207                const result = visitEachChild(node, asyncBodyVisitor, context);
208                enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames;
209                return result;
210            }
211            else {
212                return visitEachChild(node, asyncBodyVisitor, context);
213            }
214        }
215
216        function visitVariableStatementInAsyncBody(node: VariableStatement) {
217            if (isVariableDeclarationListWithCollidingName(node.declarationList)) {
218                const expression = visitVariableDeclarationListWithCollidingNames(node.declarationList, /*hasReceiver*/ false);
219                return expression ? factory.createExpressionStatement(expression) : undefined;
220            }
221            return visitEachChild(node, visitor, context);
222        }
223
224        function visitForInStatementInAsyncBody(node: ForInStatement) {
225            return factory.updateForInStatement(
226                node,
227                isVariableDeclarationListWithCollidingName(node.initializer)
228                    ? visitVariableDeclarationListWithCollidingNames(node.initializer, /*hasReceiver*/ true)!
229                    : visitNode(node.initializer, visitor, isForInitializer),
230                visitNode(node.expression, visitor, isExpression),
231                visitIterationBody(node.statement, asyncBodyVisitor, context)
232            );
233        }
234
235        function visitForOfStatementInAsyncBody(node: ForOfStatement) {
236            return factory.updateForOfStatement(
237                node,
238                visitNode(node.awaitModifier, visitor, isToken),
239                isVariableDeclarationListWithCollidingName(node.initializer)
240                    ? visitVariableDeclarationListWithCollidingNames(node.initializer, /*hasReceiver*/ true)!
241                    : visitNode(node.initializer, visitor, isForInitializer),
242                visitNode(node.expression, visitor, isExpression),
243                visitIterationBody(node.statement, asyncBodyVisitor, context)
244            );
245        }
246
247        function visitForStatementInAsyncBody(node: ForStatement) {
248            const initializer = node.initializer!; // TODO: GH#18217
249            return factory.updateForStatement(
250                node,
251                isVariableDeclarationListWithCollidingName(initializer)
252                    ? visitVariableDeclarationListWithCollidingNames(initializer, /*hasReceiver*/ false)
253                    : visitNode(node.initializer, visitor, isForInitializer),
254                visitNode(node.condition, visitor, isExpression),
255                visitNode(node.incrementor, visitor, isExpression),
256                visitIterationBody(node.statement, asyncBodyVisitor, context)
257            );
258        }
259
260        /**
261         * Visits an AwaitExpression node.
262         *
263         * This function will be called any time a ES2017 await expression is encountered.
264         *
265         * @param node The node to visit.
266         */
267        function visitAwaitExpression(node: AwaitExpression): Expression {
268            // do not downlevel a top-level await as it is module syntax...
269            if (inTopLevelContext()) {
270                return visitEachChild(node, visitor, context);
271            }
272            return setOriginalNode(
273                setTextRange(
274                    factory.createYieldExpression(
275                        /*asteriskToken*/ undefined,
276                        visitNode(node.expression, visitor, isExpression)
277                    ),
278                    node
279                ),
280                node
281            );
282        }
283
284        function visitConstructorDeclaration(node: ConstructorDeclaration) {
285            return factory.updateConstructorDeclaration(
286                node,
287                visitNodes(node.modifiers, visitor, isModifierLike),
288                visitParameterList(node.parameters, visitor, context),
289                transformMethodBody(node)
290            );
291        }
292
293        /**
294         * Visits a MethodDeclaration node.
295         *
296         * This function will be called when one of the following conditions are met:
297         * - The node is marked as async
298         *
299         * @param node The node to visit.
300         */
301        function visitMethodDeclaration(node: MethodDeclaration) {
302            return factory.updateMethodDeclaration(
303                node,
304                visitNodes(node.modifiers, visitor, isModifierLike),
305                node.asteriskToken,
306                node.name,
307                /*questionToken*/ undefined,
308                /*typeParameters*/ undefined,
309                visitParameterList(node.parameters, visitor, context),
310                /*type*/ undefined,
311                getFunctionFlags(node) & FunctionFlags.Async
312                    ? transformAsyncFunctionBody(node)
313                    : transformMethodBody(node)
314            );
315        }
316
317        function visitGetAccessorDeclaration(node: GetAccessorDeclaration) {
318            return factory.updateGetAccessorDeclaration(
319                node,
320                visitNodes(node.modifiers, visitor, isModifierLike),
321                node.name,
322                visitParameterList(node.parameters, visitor, context),
323                /*type*/ undefined,
324                transformMethodBody(node)
325            );
326        }
327
328        function visitSetAccessorDeclaration(node: SetAccessorDeclaration) {
329            return factory.updateSetAccessorDeclaration(
330                node,
331                visitNodes(node.modifiers, visitor, isModifierLike),
332                node.name,
333                visitParameterList(node.parameters, visitor, context),
334                transformMethodBody(node)
335            );
336        }
337
338        /**
339         * Visits a FunctionDeclaration node.
340         *
341         * This function will be called when one of the following conditions are met:
342         * - The node is marked async
343         *
344         * @param node The node to visit.
345         */
346        function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> {
347            return factory.updateFunctionDeclaration(
348                node,
349                visitNodes(node.modifiers, visitor, isModifierLike),
350                node.asteriskToken,
351                node.name,
352                /*typeParameters*/ undefined,
353                visitParameterList(node.parameters, visitor, context),
354                /*type*/ undefined,
355                getFunctionFlags(node) & FunctionFlags.Async
356                    ? transformAsyncFunctionBody(node)
357                    : visitFunctionBody(node.body, visitor, context)
358            );
359        }
360
361        /**
362         * Visits a FunctionExpression node.
363         *
364         * This function will be called when one of the following conditions are met:
365         * - The node is marked async
366         *
367         * @param node The node to visit.
368         */
369        function visitFunctionExpression(node: FunctionExpression): Expression {
370            return factory.updateFunctionExpression(
371                node,
372                visitNodes(node.modifiers, visitor, isModifierLike),
373                node.asteriskToken,
374                node.name,
375                /*typeParameters*/ undefined,
376                visitParameterList(node.parameters, visitor, context),
377                /*type*/ undefined,
378                getFunctionFlags(node) & FunctionFlags.Async
379                    ? transformAsyncFunctionBody(node)
380                    : visitFunctionBody(node.body, visitor, context)
381            );
382        }
383
384        /**
385         * Visits an ArrowFunction.
386         *
387         * This function will be called when one of the following conditions are met:
388         * - The node is marked async
389         *
390         * @param node The node to visit.
391         */
392        function visitArrowFunction(node: ArrowFunction) {
393            return factory.updateArrowFunction(
394                node,
395                visitNodes(node.modifiers, visitor, isModifierLike),
396                /*typeParameters*/ undefined,
397                visitParameterList(node.parameters, visitor, context),
398                /*type*/ undefined,
399                node.equalsGreaterThanToken,
400                getFunctionFlags(node) & FunctionFlags.Async
401                    ? transformAsyncFunctionBody(node)
402                    : visitFunctionBody(node.body, visitor, context),
403            );
404        }
405
406        function recordDeclarationName({ name }: ParameterDeclaration | VariableDeclaration | BindingElement, names: Set<__String>) {
407            if (isIdentifier(name)) {
408                names.add(name.escapedText);
409            }
410            else {
411                for (const element of name.elements) {
412                    if (!isOmittedExpression(element)) {
413                        recordDeclarationName(element, names);
414                    }
415                }
416            }
417        }
418
419        function isVariableDeclarationListWithCollidingName(node: ForInitializer): node is VariableDeclarationList {
420            return !!node
421                && isVariableDeclarationList(node)
422                && !(node.flags & NodeFlags.BlockScoped)
423                && node.declarations.some(collidesWithParameterName);
424        }
425
426        function visitVariableDeclarationListWithCollidingNames(node: VariableDeclarationList, hasReceiver: boolean) {
427            hoistVariableDeclarationList(node);
428
429            const variables = getInitializedVariables(node);
430            if (variables.length === 0) {
431                if (hasReceiver) {
432                    return visitNode(factory.converters.convertToAssignmentElementTarget(node.declarations[0].name), visitor, isExpression);
433                }
434                return undefined;
435            }
436
437            return factory.inlineExpressions(map(variables, transformInitializedVariable));
438        }
439
440        function hoistVariableDeclarationList(node: VariableDeclarationList) {
441            forEach(node.declarations, hoistVariable);
442        }
443
444        function hoistVariable({ name }: VariableDeclaration | BindingElement) {
445            if (isIdentifier(name)) {
446                hoistVariableDeclaration(name);
447            }
448            else {
449                for (const element of name.elements) {
450                    if (!isOmittedExpression(element)) {
451                        hoistVariable(element);
452                    }
453                }
454            }
455        }
456
457        function transformInitializedVariable(node: VariableDeclaration) {
458            const converted = setSourceMapRange(
459                factory.createAssignment(
460                    factory.converters.convertToAssignmentElementTarget(node.name),
461                    node.initializer!
462                ),
463                node
464            );
465            return visitNode(converted, visitor, isExpression);
466        }
467
468        function collidesWithParameterName({ name }: VariableDeclaration | BindingElement): boolean {
469            if (isIdentifier(name)) {
470                return enclosingFunctionParameterNames.has(name.escapedText);
471            }
472            else {
473                for (const element of name.elements) {
474                    if (!isOmittedExpression(element) && collidesWithParameterName(element)) {
475                        return true;
476                    }
477                }
478            }
479            return false;
480        }
481
482        function transformMethodBody(node: MethodDeclaration | AccessorDeclaration | ConstructorDeclaration): FunctionBody | undefined {
483            Debug.assertIsDefined(node.body);
484
485            const savedCapturedSuperProperties = capturedSuperProperties;
486            const savedHasSuperElementAccess = hasSuperElementAccess;
487            capturedSuperProperties = new Set();
488            hasSuperElementAccess = false;
489
490            let updated = visitFunctionBody(node.body, visitor, context);
491
492            // Minor optimization, emit `_super` helper to capture `super` access in an arrow.
493            // This step isn't needed if we eventually transform this to ES5.
494            const originalMethod = getOriginalNode(node, isFunctionLikeDeclaration);
495            const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 &&
496                resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) &&
497                (getFunctionFlags(originalMethod) & FunctionFlags.AsyncGenerator) !== FunctionFlags.AsyncGenerator;
498
499            if (emitSuperHelpers) {
500                enableSubstitutionForAsyncMethodsWithSuper();
501                if (capturedSuperProperties.size) {
502                    const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties);
503                    substitutedSuperAccessors[getNodeId(variableStatement)] = true;
504
505                    const statements = updated.statements.slice();
506                    insertStatementsAfterStandardPrologue(statements, [variableStatement]);
507                    updated = factory.updateBlock(updated, statements);
508                }
509
510                if (hasSuperElementAccess) {
511                    // Emit helpers for super element access expressions (`super[x]`).
512                    if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
513                        addEmitHelper(updated, advancedAsyncSuperHelper);
514                    }
515                    else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) {
516                        addEmitHelper(updated, asyncSuperHelper);
517                    }
518                }
519            }
520
521            capturedSuperProperties = savedCapturedSuperProperties;
522            hasSuperElementAccess = savedHasSuperElementAccess;
523            return updated;
524        }
525
526        function transformAsyncFunctionBody(node: MethodDeclaration | AccessorDeclaration | FunctionDeclaration | FunctionExpression): FunctionBody;
527        function transformAsyncFunctionBody(node: ArrowFunction): ConciseBody;
528        function transformAsyncFunctionBody(node: FunctionLikeDeclaration): ConciseBody {
529            resumeLexicalEnvironment();
530
531            const original = getOriginalNode(node, isFunctionLike);
532            const nodeType = original.type;
533            const promiseConstructor = languageVersion < ScriptTarget.ES2015 ? getPromiseConstructor(nodeType) : undefined;
534            const isArrowFunction = node.kind === SyntaxKind.ArrowFunction;
535            const hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0;
536
537            // An async function is emit as an outer function that calls an inner
538            // generator function. To preserve lexical bindings, we pass the current
539            // `this` and `arguments` objects to `__awaiter`. The generator function
540            // passed to `__awaiter` is executed inside of the callback to the
541            // promise constructor.
542
543            const savedEnclosingFunctionParameterNames = enclosingFunctionParameterNames;
544            enclosingFunctionParameterNames = new Set();
545            for (const parameter of node.parameters) {
546                recordDeclarationName(parameter, enclosingFunctionParameterNames);
547            }
548
549            const savedCapturedSuperProperties = capturedSuperProperties;
550            const savedHasSuperElementAccess = hasSuperElementAccess;
551            if (!isArrowFunction) {
552                capturedSuperProperties = new Set();
553                hasSuperElementAccess = false;
554            }
555
556            let result: ConciseBody;
557            if (!isArrowFunction) {
558                const statements: Statement[] = [];
559                const statementOffset = factory.copyPrologue((node.body as Block).statements, statements, /*ensureUseStrict*/ false, visitor);
560                statements.push(
561                    factory.createReturnStatement(
562                        emitHelpers().createAwaiterHelper(
563                            inHasLexicalThisContext(),
564                            hasLexicalArguments,
565                            promiseConstructor,
566                            transformAsyncFunctionBodyWorker(node.body as Block, statementOffset)
567                        )
568                    )
569                );
570
571                insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
572
573                // Minor optimization, emit `_super` helper to capture `super` access in an arrow.
574                // This step isn't needed if we eventually transform this to ES5.
575                const emitSuperHelpers = languageVersion >= ScriptTarget.ES2015 && resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync | NodeCheckFlags.MethodWithSuperPropertyAccessInAsync);
576
577                if (emitSuperHelpers) {
578                    enableSubstitutionForAsyncMethodsWithSuper();
579                    if (capturedSuperProperties.size) {
580                        const variableStatement = createSuperAccessVariableStatement(factory, resolver, node, capturedSuperProperties);
581                        substitutedSuperAccessors[getNodeId(variableStatement)] = true;
582                        insertStatementsAfterStandardPrologue(statements, [variableStatement]);
583                    }
584                }
585
586                const block = factory.createBlock(statements, /*multiLine*/ true);
587                setTextRange(block, node.body);
588
589                if (emitSuperHelpers && hasSuperElementAccess) {
590                    // Emit helpers for super element access expressions (`super[x]`).
591                    if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
592                        addEmitHelper(block, advancedAsyncSuperHelper);
593                    }
594                    else if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAccessInAsync) {
595                        addEmitHelper(block, asyncSuperHelper);
596                    }
597                }
598
599                result = block;
600            }
601            else {
602                const expression = emitHelpers().createAwaiterHelper(
603                    inHasLexicalThisContext(),
604                    hasLexicalArguments,
605                    promiseConstructor,
606                    transformAsyncFunctionBodyWorker(node.body)
607                );
608
609                const declarations = endLexicalEnvironment();
610                if (some(declarations)) {
611                    const block = factory.converters.convertToFunctionBlock(expression);
612                    result = factory.updateBlock(block, setTextRange(factory.createNodeArray(concatenate(declarations, block.statements)), block.statements));
613                }
614                else {
615                    result = expression;
616                }
617            }
618
619            enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames;
620            if (!isArrowFunction) {
621                capturedSuperProperties = savedCapturedSuperProperties;
622                hasSuperElementAccess = savedHasSuperElementAccess;
623            }
624            return result;
625        }
626
627        function transformAsyncFunctionBodyWorker(body: ConciseBody, start?: number) {
628            if (isBlock(body)) {
629                return factory.updateBlock(body, visitNodes(body.statements, asyncBodyVisitor, isStatement, start));
630            }
631            else {
632                return factory.converters.convertToFunctionBlock(visitNode(body, asyncBodyVisitor, isConciseBody));
633            }
634        }
635
636        function getPromiseConstructor(type: TypeNode | undefined) {
637            const typeName = type && getEntityNameFromTypeNode(type);
638            if (typeName && isEntityName(typeName)) {
639                const serializationKind = resolver.getTypeReferenceSerializationKind(typeName);
640                if (serializationKind === TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue
641                    || serializationKind === TypeReferenceSerializationKind.Unknown) {
642                    return typeName;
643                }
644            }
645
646            return undefined;
647        }
648
649        function enableSubstitutionForAsyncMethodsWithSuper() {
650            if ((enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper) === 0) {
651                enabledSubstitutions |= ES2017SubstitutionFlags.AsyncMethodsWithSuper;
652
653                // We need to enable substitutions for call, property access, and element access
654                // if we need to rewrite super calls.
655                context.enableSubstitution(SyntaxKind.CallExpression);
656                context.enableSubstitution(SyntaxKind.PropertyAccessExpression);
657                context.enableSubstitution(SyntaxKind.ElementAccessExpression);
658
659                // We need to be notified when entering and exiting declarations that bind super.
660                context.enableEmitNotification(SyntaxKind.ClassDeclaration);
661                context.enableEmitNotification(SyntaxKind.MethodDeclaration);
662                context.enableEmitNotification(SyntaxKind.GetAccessor);
663                context.enableEmitNotification(SyntaxKind.SetAccessor);
664                context.enableEmitNotification(SyntaxKind.Constructor);
665                // We need to be notified when entering the generated accessor arrow functions.
666                context.enableEmitNotification(SyntaxKind.VariableStatement);
667            }
668        }
669
670        /**
671         * Hook for node emit.
672         *
673         * @param hint A hint as to the intended usage of the node.
674         * @param node The node to emit.
675         * @param emit A callback used to emit the node in the printer.
676         */
677        function onEmitNode(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void {
678            // If we need to support substitutions for `super` in an async method,
679            // we should track it here.
680            if (enabledSubstitutions & ES2017SubstitutionFlags.AsyncMethodsWithSuper && isSuperContainer(node)) {
681                const superContainerFlags = resolver.getNodeCheckFlags(node) & (NodeCheckFlags.MethodWithSuperPropertyAccessInAsync | NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync);
682                if (superContainerFlags !== enclosingSuperContainerFlags) {
683                    const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
684                    enclosingSuperContainerFlags = superContainerFlags;
685                    previousOnEmitNode(hint, node, emitCallback);
686                    enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
687                    return;
688                }
689            }
690            // Disable substitution in the generated super accessor itself.
691            else if (enabledSubstitutions && substitutedSuperAccessors[getNodeId(node)]) {
692                const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags;
693                enclosingSuperContainerFlags = 0;
694                previousOnEmitNode(hint, node, emitCallback);
695                enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags;
696                return;
697            }
698            previousOnEmitNode(hint, node, emitCallback);
699        }
700
701        /**
702         * Hooks node substitutions.
703         *
704         * @param hint A hint as to the intended usage of the node.
705         * @param node The node to substitute.
706         */
707        function onSubstituteNode(hint: EmitHint, node: Node) {
708            node = previousOnSubstituteNode(hint, node);
709            if (hint === EmitHint.Expression && enclosingSuperContainerFlags) {
710                return substituteExpression(node as Expression);
711            }
712
713            return node;
714        }
715
716        function substituteExpression(node: Expression) {
717            switch (node.kind) {
718                case SyntaxKind.PropertyAccessExpression:
719                    return substitutePropertyAccessExpression(node as PropertyAccessExpression);
720                case SyntaxKind.ElementAccessExpression:
721                    return substituteElementAccessExpression(node as ElementAccessExpression);
722                case SyntaxKind.CallExpression:
723                    return substituteCallExpression(node as CallExpression);
724            }
725            return node;
726        }
727
728        function substitutePropertyAccessExpression(node: PropertyAccessExpression) {
729            if (node.expression.kind === SyntaxKind.SuperKeyword) {
730                return setTextRange(
731                    factory.createPropertyAccessExpression(
732                        factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel),
733                        node.name),
734                    node
735                );
736            }
737            return node;
738        }
739
740        function substituteElementAccessExpression(node: ElementAccessExpression) {
741            if (node.expression.kind === SyntaxKind.SuperKeyword) {
742                return createSuperElementAccessInAsyncMethod(
743                    node.argumentExpression,
744                    node
745                );
746            }
747            return node;
748        }
749
750        function substituteCallExpression(node: CallExpression): Expression {
751            const expression = node.expression;
752            if (isSuperProperty(expression)) {
753                const argumentExpression = isPropertyAccessExpression(expression)
754                    ? substitutePropertyAccessExpression(expression)
755                    : substituteElementAccessExpression(expression);
756                return factory.createCallExpression(
757                    factory.createPropertyAccessExpression(argumentExpression, "call"),
758                    /*typeArguments*/ undefined,
759                    [
760                        factory.createThis(),
761                        ...node.arguments
762                    ]
763                );
764            }
765            return node;
766        }
767
768        function isSuperContainer(node: Node): node is SuperContainer {
769            const kind = node.kind;
770            return kind === SyntaxKind.ClassDeclaration
771                || kind === SyntaxKind.Constructor
772                || kind === SyntaxKind.MethodDeclaration
773                || kind === SyntaxKind.GetAccessor
774                || kind === SyntaxKind.SetAccessor;
775        }
776
777        function createSuperElementAccessInAsyncMethod(argumentExpression: Expression, location: TextRange): LeftHandSideExpression {
778            if (enclosingSuperContainerFlags & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) {
779                return setTextRange(
780                    factory.createPropertyAccessExpression(
781                        factory.createCallExpression(
782                            factory.createUniqueName("_superIndex", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel),
783                            /*typeArguments*/ undefined,
784                            [argumentExpression]
785                        ),
786                        "value"
787                    ),
788                    location
789                );
790            }
791            else {
792                return setTextRange(
793                    factory.createCallExpression(
794                        factory.createUniqueName("_superIndex", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel),
795                        /*typeArguments*/ undefined,
796                        [argumentExpression]
797                    ),
798                    location
799                );
800            }
801        }
802    }
803
804    /** Creates a variable named `_super` with accessor properties for the given property names. */
805    export function createSuperAccessVariableStatement(factory: NodeFactory, resolver: EmitResolver, node: FunctionLikeDeclaration, names: Set<__String>) {
806        // Create a variable declaration with a getter/setter (if binding) definition for each name:
807        //   const _super = Object.create(null, { x: { get: () => super.x, set: (v) => super.x = v }, ... });
808        const hasBinding = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.MethodWithSuperPropertyAssignmentInAsync) !== 0;
809        const accessors: PropertyAssignment[] = [];
810        names.forEach((_, key) => {
811            const name = unescapeLeadingUnderscores(key);
812            const getterAndSetter: PropertyAssignment[] = [];
813            getterAndSetter.push(factory.createPropertyAssignment(
814                "get",
815                factory.createArrowFunction(
816                    /* modifiers */ undefined,
817                    /* typeParameters */ undefined,
818                    /* parameters */ [],
819                    /* type */ undefined,
820                    /* equalsGreaterThanToken */ undefined,
821                    setEmitFlags(
822                        factory.createPropertyAccessExpression(
823                            setEmitFlags(
824                                factory.createSuper(),
825                                EmitFlags.NoSubstitution
826                            ),
827                            name
828                        ),
829                        EmitFlags.NoSubstitution
830                    )
831                )
832            ));
833            if (hasBinding) {
834                getterAndSetter.push(
835                    factory.createPropertyAssignment(
836                        "set",
837                        factory.createArrowFunction(
838                            /* modifiers */ undefined,
839                            /* typeParameters */ undefined,
840                            /* parameters */ [
841                                factory.createParameterDeclaration(
842                                    /* modifiers */ undefined,
843                                    /* dotDotDotToken */ undefined,
844                                    "v",
845                                    /* questionToken */ undefined,
846                                    /* type */ undefined,
847                                    /* initializer */ undefined
848                                )
849                            ],
850                            /* type */ undefined,
851                            /* equalsGreaterThanToken */ undefined,
852                            factory.createAssignment(
853                                setEmitFlags(
854                                    factory.createPropertyAccessExpression(
855                                        setEmitFlags(
856                                            factory.createSuper(),
857                                            EmitFlags.NoSubstitution
858                                        ),
859                                        name
860                                    ),
861                                    EmitFlags.NoSubstitution
862                                ),
863                                factory.createIdentifier("v")
864                            )
865                        )
866                    )
867                );
868            }
869            accessors.push(
870                factory.createPropertyAssignment(
871                    name,
872                    factory.createObjectLiteralExpression(getterAndSetter),
873                )
874            );
875        });
876        return factory.createVariableStatement(
877            /* modifiers */ undefined,
878            factory.createVariableDeclarationList(
879                [
880                    factory.createVariableDeclaration(
881                        factory.createUniqueName("_super", GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel),
882                        /*exclamationToken*/ undefined,
883                        /* type */ undefined,
884                        factory.createCallExpression(
885                            factory.createPropertyAccessExpression(
886                                factory.createIdentifier("Object"),
887                                "create"
888                            ),
889                            /* typeArguments */ undefined,
890                            [
891                                factory.createNull(),
892                                factory.createObjectLiteralExpression(accessors, /* multiline */ true)
893                            ]
894                        )
895                    )
896                ],
897                NodeFlags.Const));
898    }
899}
900