@@ -32,6 +32,15 @@ namespace ts {
3232
3333 let enclosingFunctionParameterNames : UnderscoreEscapedMap < true > ;
3434
35+ /**
36+ * Keeps track of property names accessed on super (`super.x`) within async functions.
37+ */
38+ let capturedSuperProperties : UnderscoreEscapedMap < true > ;
39+ /** Whether the async function contains an element access on super (`super[x]`). */
40+ let hasSuperElementAccess : boolean ;
41+ /** A set of node IDs for generated super accessors (variable statements). */
42+ const substitutedSuperAccessors : boolean [ ] = [ ] ;
43+
3544 // Save the previous transformation hooks.
3645 const previousOnEmitNode = context . onEmitNode ;
3746 const previousOnSubstituteNode = context . onSubstituteNode ;
@@ -53,10 +62,6 @@ namespace ts {
5362 }
5463
5564 function visitor ( node : Node ) : VisitResult < Node > {
56- if ( ( node . transformFlags & TransformFlags . ContainsES2017 ) === 0 ) {
57- return node ;
58- }
59-
6065 switch ( node . kind ) {
6166 case SyntaxKind . AsyncKeyword :
6267 // ES2017 async modifier should be elided for targets < ES2017
@@ -77,6 +82,18 @@ namespace ts {
7782 case SyntaxKind . ArrowFunction :
7883 return visitArrowFunction ( < ArrowFunction > node ) ;
7984
85+ case SyntaxKind . PropertyAccessExpression :
86+ if ( capturedSuperProperties && isPropertyAccessExpression ( node ) && node . expression . kind === SyntaxKind . SuperKeyword ) {
87+ capturedSuperProperties . set ( node . name . escapedText , true ) ;
88+ }
89+ return visitEachChild ( node , visitor , context ) ;
90+
91+ case SyntaxKind . ElementAccessExpression :
92+ if ( capturedSuperProperties && ( < ElementAccessExpression > node ) . expression . kind === SyntaxKind . SuperKeyword ) {
93+ hasSuperElementAccess = true ;
94+ }
95+ return visitEachChild ( node , visitor , context ) ;
96+
8097 default :
8198 return visitEachChild ( node , visitor , context ) ;
8299 }
@@ -398,6 +415,11 @@ namespace ts {
398415 recordDeclarationName ( parameter , enclosingFunctionParameterNames ) ;
399416 }
400417
418+ const savedCapturedSuperProperties = capturedSuperProperties ;
419+ const savedHasSuperElementAccess = hasSuperElementAccess ;
420+ capturedSuperProperties = createUnderscoreEscapedMap < true > ( ) ;
421+ hasSuperElementAccess = false ;
422+
401423 let result : ConciseBody ;
402424 if ( ! isArrowFunction ) {
403425 const statements : Statement [ ] = [ ] ;
@@ -415,18 +437,26 @@ namespace ts {
415437
416438 addStatementsAfterPrologue ( statements , endLexicalEnvironment ( ) ) ;
417439
440+ // Minor optimization, emit `_super` helper to capture `super` access in an arrow.
441+ // This step isn't needed if we eventually transform this to ES5.
442+ const emitSuperHelpers = languageVersion >= ScriptTarget . ES2015 && resolver . getNodeCheckFlags ( node ) & ( NodeCheckFlags . AsyncMethodWithSuperBinding | NodeCheckFlags . AsyncMethodWithSuper ) ;
443+
444+ if ( emitSuperHelpers ) {
445+ enableSubstitutionForAsyncMethodsWithSuper ( ) ;
446+ const variableStatement = createSuperAccessVariableStatement ( resolver , node , capturedSuperProperties ) ;
447+ substitutedSuperAccessors [ getNodeId ( variableStatement ) ] = true ;
448+ addStatementsAfterPrologue ( statements , [ variableStatement ] ) ;
449+ }
450+
418451 const block = createBlock ( statements , /*multiLine*/ true ) ;
419452 setTextRange ( block , node . body ) ;
420453
421- // Minor optimization, emit `_super` helper to capture `super` access in an arrow.
422- // This step isn't needed if we eventually transform this to ES5.
423- if ( languageVersion >= ScriptTarget . ES2015 ) {
454+ if ( emitSuperHelpers && hasSuperElementAccess ) {
455+ // Emit helpers for super element access expressions (`super[x]`).
424456 if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . AsyncMethodWithSuperBinding ) {
425- enableSubstitutionForAsyncMethodsWithSuper ( ) ;
426457 addEmitHelper ( block , advancedAsyncSuperHelper ) ;
427458 }
428459 else if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . AsyncMethodWithSuper ) {
429- enableSubstitutionForAsyncMethodsWithSuper ( ) ;
430460 addEmitHelper ( block , asyncSuperHelper ) ;
431461 }
432462 }
@@ -452,6 +482,8 @@ namespace ts {
452482 }
453483
454484 enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames ;
485+ capturedSuperProperties = savedCapturedSuperProperties ;
486+ hasSuperElementAccess = savedHasSuperElementAccess ;
455487 return result ;
456488 }
457489
@@ -493,6 +525,8 @@ namespace ts {
493525 context . enableEmitNotification ( SyntaxKind . GetAccessor ) ;
494526 context . enableEmitNotification ( SyntaxKind . SetAccessor ) ;
495527 context . enableEmitNotification ( SyntaxKind . Constructor ) ;
528+ // We need to be notified when entering the generated accessor arrow functions.
529+ context . enableEmitNotification ( SyntaxKind . VariableStatement ) ;
496530 }
497531 }
498532
@@ -516,6 +550,14 @@ namespace ts {
516550 return ;
517551 }
518552 }
553+ // Disable substitution in the generated super accessor itself.
554+ else if ( enabledSubstitutions && substitutedSuperAccessors [ getNodeId ( node ) ] ) {
555+ const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags ;
556+ enclosingSuperContainerFlags = 0 as NodeCheckFlags ;
557+ previousOnEmitNode ( hint , node , emitCallback ) ;
558+ enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags ;
559+ return ;
560+ }
519561 previousOnEmitNode ( hint , node , emitCallback ) ;
520562 }
521563
@@ -548,8 +590,10 @@ namespace ts {
548590
549591 function substitutePropertyAccessExpression ( node : PropertyAccessExpression ) {
550592 if ( node . expression . kind === SyntaxKind . SuperKeyword ) {
551- return createSuperAccessInAsyncMethod (
552- createLiteral ( idText ( node . name ) ) ,
593+ return setTextRange (
594+ createPropertyAccess (
595+ createFileLevelUniqueName ( "_superProps" ) ,
596+ node . name ) ,
553597 node
554598 ) ;
555599 }
@@ -558,7 +602,7 @@ namespace ts {
558602
559603 function substituteElementAccessExpression ( node : ElementAccessExpression ) {
560604 if ( node . expression . kind === SyntaxKind . SuperKeyword ) {
561- return createSuperAccessInAsyncMethod (
605+ return createSuperElementAccessInAsyncMethod (
562606 node . argumentExpression ,
563607 node
564608 ) ;
@@ -593,7 +637,7 @@ namespace ts {
593637 || kind === SyntaxKind . SetAccessor ;
594638 }
595639
596- function createSuperAccessInAsyncMethod ( argumentExpression : Expression , location : TextRange ) : LeftHandSideExpression {
640+ function createSuperElementAccessInAsyncMethod ( argumentExpression : Expression , location : TextRange ) : LeftHandSideExpression {
597641 if ( enclosingSuperContainerFlags & NodeCheckFlags . AsyncMethodWithSuperBinding ) {
598642 return setTextRange (
599643 createPropertyAccess (
@@ -620,6 +664,89 @@ namespace ts {
620664 }
621665 }
622666
667+ /** Creates a variable named `_superProps` with accessor properties for the given property names. */
668+ export function createSuperAccessVariableStatement ( resolver : EmitResolver , node : FunctionLikeDeclaration , names : UnderscoreEscapedMap < true > ) {
669+ // Create a variable declaration with a getter/setter (if binding) definition for each name:
670+ // const _superProps = Object.create(null, { x: { get: () => super.x, set: (v) => super.x = v }, ... });
671+ const hasBinding = ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . AsyncMethodWithSuperBinding ) !== 0 ;
672+ const accessors : PropertyAssignment [ ] = [ ] ;
673+ names . forEach ( ( _ , key ) => {
674+ const name = unescapeLeadingUnderscores ( key ) ;
675+ const getterAndSetter : PropertyAssignment [ ] = [ ] ;
676+ getterAndSetter . push ( createPropertyAssignment (
677+ "get" ,
678+ createArrowFunction (
679+ /* modifiers */ undefined ,
680+ /* typeParameters */ undefined ,
681+ /* parameters */ [ ] ,
682+ /* type */ undefined ,
683+ /* equalsGreaterThanToken */ undefined ,
684+ createPropertyAccess (
685+ createSuper ( ) ,
686+ name
687+ )
688+ )
689+ ) ) ;
690+ if ( hasBinding ) {
691+ getterAndSetter . push (
692+ createPropertyAssignment (
693+ "set" ,
694+ createArrowFunction (
695+ /* modifiers */ undefined ,
696+ /* typeParameters */ undefined ,
697+ /* parameters */ [
698+ createParameter (
699+ /* decorators */ undefined ,
700+ /* modifiers */ undefined ,
701+ /* dotDotDotToken */ undefined ,
702+ "v" ,
703+ /* questionToken */ undefined ,
704+ /* type */ undefined ,
705+ /* initializer */ undefined
706+ )
707+ ] ,
708+ /* type */ undefined ,
709+ /* equalsGreaterThanToken */ undefined ,
710+ createAssignment (
711+ createPropertyAccess (
712+ createSuper ( ) ,
713+ name ) ,
714+ createIdentifier ( "v" )
715+ )
716+ )
717+ )
718+ ) ;
719+ }
720+ accessors . push (
721+ createPropertyAssignment (
722+ name ,
723+ createObjectLiteral ( getterAndSetter ) ,
724+ )
725+ ) ;
726+ } ) ;
727+ return createVariableStatement (
728+ /* modifiers */ undefined ,
729+ createVariableDeclarationList (
730+ [
731+ createVariableDeclaration (
732+ createFileLevelUniqueName ( "_superProps" ) ,
733+ /* type */ undefined ,
734+ createCall (
735+ createPropertyAccess (
736+ createIdentifier ( "Object" ) ,
737+ "create"
738+ ) ,
739+ /* typeArguments */ undefined ,
740+ [
741+ createNull ( ) ,
742+ createObjectLiteral ( accessors , /* multiline */ true )
743+ ]
744+ )
745+ )
746+ ] ,
747+ NodeFlags . Const ) ) ;
748+ }
749+
623750 const awaiterHelper : EmitHelper = {
624751 name : "typescript:awaiter" ,
625752 scoped : false ,
0 commit comments