@@ -32,6 +32,15 @@ namespace ts {
32
32
33
33
let enclosingFunctionParameterNames : UnderscoreEscapedMap < true > ;
34
34
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
+
35
44
// Save the previous transformation hooks.
36
45
const previousOnEmitNode = context . onEmitNode ;
37
46
const previousOnSubstituteNode = context . onSubstituteNode ;
@@ -53,10 +62,6 @@ namespace ts {
53
62
}
54
63
55
64
function visitor ( node : Node ) : VisitResult < Node > {
56
- if ( ( node . transformFlags & TransformFlags . ContainsES2017 ) === 0 ) {
57
- return node ;
58
- }
59
-
60
65
switch ( node . kind ) {
61
66
case SyntaxKind . AsyncKeyword :
62
67
// ES2017 async modifier should be elided for targets < ES2017
@@ -77,6 +82,18 @@ namespace ts {
77
82
case SyntaxKind . ArrowFunction :
78
83
return visitArrowFunction ( < ArrowFunction > node ) ;
79
84
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
+
80
97
default :
81
98
return visitEachChild ( node , visitor , context ) ;
82
99
}
@@ -398,6 +415,11 @@ namespace ts {
398
415
recordDeclarationName ( parameter , enclosingFunctionParameterNames ) ;
399
416
}
400
417
418
+ const savedCapturedSuperProperties = capturedSuperProperties ;
419
+ const savedHasSuperElementAccess = hasSuperElementAccess ;
420
+ capturedSuperProperties = createUnderscoreEscapedMap < true > ( ) ;
421
+ hasSuperElementAccess = false ;
422
+
401
423
let result : ConciseBody ;
402
424
if ( ! isArrowFunction ) {
403
425
const statements : Statement [ ] = [ ] ;
@@ -415,18 +437,26 @@ namespace ts {
415
437
416
438
addStatementsAfterPrologue ( statements , endLexicalEnvironment ( ) ) ;
417
439
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
+
418
451
const block = createBlock ( statements , /*multiLine*/ true ) ;
419
452
setTextRange ( block , node . body ) ;
420
453
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]`).
424
456
if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . AsyncMethodWithSuperBinding ) {
425
- enableSubstitutionForAsyncMethodsWithSuper ( ) ;
426
457
addEmitHelper ( block , advancedAsyncSuperHelper ) ;
427
458
}
428
459
else if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . AsyncMethodWithSuper ) {
429
- enableSubstitutionForAsyncMethodsWithSuper ( ) ;
430
460
addEmitHelper ( block , asyncSuperHelper ) ;
431
461
}
432
462
}
@@ -452,6 +482,8 @@ namespace ts {
452
482
}
453
483
454
484
enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames ;
485
+ capturedSuperProperties = savedCapturedSuperProperties ;
486
+ hasSuperElementAccess = savedHasSuperElementAccess ;
455
487
return result ;
456
488
}
457
489
@@ -493,6 +525,8 @@ namespace ts {
493
525
context . enableEmitNotification ( SyntaxKind . GetAccessor ) ;
494
526
context . enableEmitNotification ( SyntaxKind . SetAccessor ) ;
495
527
context . enableEmitNotification ( SyntaxKind . Constructor ) ;
528
+ // We need to be notified when entering the generated accessor arrow functions.
529
+ context . enableEmitNotification ( SyntaxKind . VariableStatement ) ;
496
530
}
497
531
}
498
532
@@ -516,6 +550,14 @@ namespace ts {
516
550
return ;
517
551
}
518
552
}
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
+ }
519
561
previousOnEmitNode ( hint , node , emitCallback ) ;
520
562
}
521
563
@@ -548,8 +590,10 @@ namespace ts {
548
590
549
591
function substitutePropertyAccessExpression ( node : PropertyAccessExpression ) {
550
592
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 ) ,
553
597
node
554
598
) ;
555
599
}
@@ -558,7 +602,7 @@ namespace ts {
558
602
559
603
function substituteElementAccessExpression ( node : ElementAccessExpression ) {
560
604
if ( node . expression . kind === SyntaxKind . SuperKeyword ) {
561
- return createSuperAccessInAsyncMethod (
605
+ return createSuperElementAccessInAsyncMethod (
562
606
node . argumentExpression ,
563
607
node
564
608
) ;
@@ -593,7 +637,7 @@ namespace ts {
593
637
|| kind === SyntaxKind . SetAccessor ;
594
638
}
595
639
596
- function createSuperAccessInAsyncMethod ( argumentExpression : Expression , location : TextRange ) : LeftHandSideExpression {
640
+ function createSuperElementAccessInAsyncMethod ( argumentExpression : Expression , location : TextRange ) : LeftHandSideExpression {
597
641
if ( enclosingSuperContainerFlags & NodeCheckFlags . AsyncMethodWithSuperBinding ) {
598
642
return setTextRange (
599
643
createPropertyAccess (
@@ -620,6 +664,89 @@ namespace ts {
620
664
}
621
665
}
622
666
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
+
623
750
const awaiterHelper : EmitHelper = {
624
751
name : "typescript:awaiter" ,
625
752
scoped : false ,
0 commit comments