@@ -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 ;
@@ -56,7 +65,6 @@ namespace ts {
56
65
if ( ( node . transformFlags & TransformFlags . ContainsES2017 ) === 0 ) {
57
66
return node ;
58
67
}
59
-
60
68
switch ( node . kind ) {
61
69
case SyntaxKind . AsyncKeyword :
62
70
// ES2017 async modifier should be elided for targets < ES2017
@@ -77,6 +85,18 @@ namespace ts {
77
85
case SyntaxKind . ArrowFunction :
78
86
return visitArrowFunction ( < ArrowFunction > node ) ;
79
87
88
+ case SyntaxKind . PropertyAccessExpression :
89
+ if ( capturedSuperProperties && isPropertyAccessExpression ( node ) && node . expression . kind === SyntaxKind . SuperKeyword ) {
90
+ capturedSuperProperties . set ( node . name . escapedText , true ) ;
91
+ }
92
+ return visitEachChild ( node , visitor , context ) ;
93
+
94
+ case SyntaxKind . ElementAccessExpression :
95
+ if ( capturedSuperProperties && ( < ElementAccessExpression > node ) . expression . kind === SyntaxKind . SuperKeyword ) {
96
+ hasSuperElementAccess = true ;
97
+ }
98
+ return visitEachChild ( node , visitor , context ) ;
99
+
80
100
default :
81
101
return visitEachChild ( node , visitor , context ) ;
82
102
}
@@ -398,6 +418,11 @@ namespace ts {
398
418
recordDeclarationName ( parameter , enclosingFunctionParameterNames ) ;
399
419
}
400
420
421
+ const savedCapturedSuperProperties = capturedSuperProperties ;
422
+ const savedHasSuperElementAccess = hasSuperElementAccess ;
423
+ capturedSuperProperties = createUnderscoreEscapedMap < true > ( ) ;
424
+ hasSuperElementAccess = false ;
425
+
401
426
let result : ConciseBody ;
402
427
if ( ! isArrowFunction ) {
403
428
const statements : Statement [ ] = [ ] ;
@@ -415,18 +440,26 @@ namespace ts {
415
440
416
441
addStatementsAfterPrologue ( statements , endLexicalEnvironment ( ) ) ;
417
442
443
+ // Minor optimization, emit `_super` helper to capture `super` access in an arrow.
444
+ // This step isn't needed if we eventually transform this to ES5.
445
+ const emitSuperHelpers = languageVersion >= ScriptTarget . ES2015 && resolver . getNodeCheckFlags ( node ) & ( NodeCheckFlags . AsyncMethodWithSuperBinding | NodeCheckFlags . AsyncMethodWithSuper ) ;
446
+
447
+ if ( emitSuperHelpers ) {
448
+ enableSubstitutionForAsyncMethodsWithSuper ( ) ;
449
+ const variableStatement = createSuperAccessVariableStatement ( resolver , node , capturedSuperProperties ) ;
450
+ substitutedSuperAccessors [ getNodeId ( variableStatement ) ] = true ;
451
+ addStatementsAfterPrologue ( statements , [ variableStatement ] ) ;
452
+ }
453
+
418
454
const block = createBlock ( statements , /*multiLine*/ true ) ;
419
455
setTextRange ( block , node . body ) ;
420
456
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 ) {
457
+ if ( emitSuperHelpers && hasSuperElementAccess ) {
458
+ // Emit helpers for super element access expressions (`super[x]`).
424
459
if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . AsyncMethodWithSuperBinding ) {
425
- enableSubstitutionForAsyncMethodsWithSuper ( ) ;
426
460
addEmitHelper ( block , advancedAsyncSuperHelper ) ;
427
461
}
428
462
else if ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . AsyncMethodWithSuper ) {
429
- enableSubstitutionForAsyncMethodsWithSuper ( ) ;
430
463
addEmitHelper ( block , asyncSuperHelper ) ;
431
464
}
432
465
}
@@ -452,6 +485,8 @@ namespace ts {
452
485
}
453
486
454
487
enclosingFunctionParameterNames = savedEnclosingFunctionParameterNames ;
488
+ capturedSuperProperties = savedCapturedSuperProperties ;
489
+ hasSuperElementAccess = savedHasSuperElementAccess ;
455
490
return result ;
456
491
}
457
492
@@ -493,6 +528,8 @@ namespace ts {
493
528
context . enableEmitNotification ( SyntaxKind . GetAccessor ) ;
494
529
context . enableEmitNotification ( SyntaxKind . SetAccessor ) ;
495
530
context . enableEmitNotification ( SyntaxKind . Constructor ) ;
531
+ // We need to be notified when entering the generated accessor arrow functions.
532
+ context . enableEmitNotification ( SyntaxKind . VariableStatement ) ;
496
533
}
497
534
}
498
535
@@ -516,6 +553,14 @@ namespace ts {
516
553
return ;
517
554
}
518
555
}
556
+ // Disable substitution in the generated super accessor itself.
557
+ else if ( enabledSubstitutions && substitutedSuperAccessors [ getNodeId ( node ) ] ) {
558
+ const savedEnclosingSuperContainerFlags = enclosingSuperContainerFlags ;
559
+ enclosingSuperContainerFlags = 0 ;
560
+ previousOnEmitNode ( hint , node , emitCallback ) ;
561
+ enclosingSuperContainerFlags = savedEnclosingSuperContainerFlags ;
562
+ return ;
563
+ }
519
564
previousOnEmitNode ( hint , node , emitCallback ) ;
520
565
}
521
566
@@ -548,8 +593,10 @@ namespace ts {
548
593
549
594
function substitutePropertyAccessExpression ( node : PropertyAccessExpression ) {
550
595
if ( node . expression . kind === SyntaxKind . SuperKeyword ) {
551
- return createSuperAccessInAsyncMethod (
552
- createLiteral ( idText ( node . name ) ) ,
596
+ return setTextRange (
597
+ createPropertyAccess (
598
+ createFileLevelUniqueName ( "_super" ) ,
599
+ node . name ) ,
553
600
node
554
601
) ;
555
602
}
@@ -558,7 +605,7 @@ namespace ts {
558
605
559
606
function substituteElementAccessExpression ( node : ElementAccessExpression ) {
560
607
if ( node . expression . kind === SyntaxKind . SuperKeyword ) {
561
- return createSuperAccessInAsyncMethod (
608
+ return createSuperElementAccessInAsyncMethod (
562
609
node . argumentExpression ,
563
610
node
564
611
) ;
@@ -593,12 +640,12 @@ namespace ts {
593
640
|| kind === SyntaxKind . SetAccessor ;
594
641
}
595
642
596
- function createSuperAccessInAsyncMethod ( argumentExpression : Expression , location : TextRange ) : LeftHandSideExpression {
643
+ function createSuperElementAccessInAsyncMethod ( argumentExpression : Expression , location : TextRange ) : LeftHandSideExpression {
597
644
if ( enclosingSuperContainerFlags & NodeCheckFlags . AsyncMethodWithSuperBinding ) {
598
645
return setTextRange (
599
646
createPropertyAccess (
600
647
createCall (
601
- createFileLevelUniqueName ( "_super " ) ,
648
+ createFileLevelUniqueName ( "_superIndex " ) ,
602
649
/*typeArguments*/ undefined ,
603
650
[ argumentExpression ]
604
651
) ,
@@ -610,7 +657,7 @@ namespace ts {
610
657
else {
611
658
return setTextRange (
612
659
createCall (
613
- createFileLevelUniqueName ( "_super " ) ,
660
+ createFileLevelUniqueName ( "_superIndex " ) ,
614
661
/*typeArguments*/ undefined ,
615
662
[ argumentExpression ]
616
663
) ,
@@ -620,6 +667,89 @@ namespace ts {
620
667
}
621
668
}
622
669
670
+ /** Creates a variable named `_super` with accessor properties for the given property names. */
671
+ export function createSuperAccessVariableStatement ( resolver : EmitResolver , node : FunctionLikeDeclaration , names : UnderscoreEscapedMap < true > ) {
672
+ // Create a variable declaration with a getter/setter (if binding) definition for each name:
673
+ // const _super = Object.create(null, { x: { get: () => super.x, set: (v) => super.x = v }, ... });
674
+ const hasBinding = ( resolver . getNodeCheckFlags ( node ) & NodeCheckFlags . AsyncMethodWithSuperBinding ) !== 0 ;
675
+ const accessors : PropertyAssignment [ ] = [ ] ;
676
+ names . forEach ( ( _ , key ) => {
677
+ const name = unescapeLeadingUnderscores ( key ) ;
678
+ const getterAndSetter : PropertyAssignment [ ] = [ ] ;
679
+ getterAndSetter . push ( createPropertyAssignment (
680
+ "get" ,
681
+ createArrowFunction (
682
+ /* modifiers */ undefined ,
683
+ /* typeParameters */ undefined ,
684
+ /* parameters */ [ ] ,
685
+ /* type */ undefined ,
686
+ /* equalsGreaterThanToken */ undefined ,
687
+ createPropertyAccess (
688
+ createSuper ( ) ,
689
+ name
690
+ )
691
+ )
692
+ ) ) ;
693
+ if ( hasBinding ) {
694
+ getterAndSetter . push (
695
+ createPropertyAssignment (
696
+ "set" ,
697
+ createArrowFunction (
698
+ /* modifiers */ undefined ,
699
+ /* typeParameters */ undefined ,
700
+ /* parameters */ [
701
+ createParameter (
702
+ /* decorators */ undefined ,
703
+ /* modifiers */ undefined ,
704
+ /* dotDotDotToken */ undefined ,
705
+ "v" ,
706
+ /* questionToken */ undefined ,
707
+ /* type */ undefined ,
708
+ /* initializer */ undefined
709
+ )
710
+ ] ,
711
+ /* type */ undefined ,
712
+ /* equalsGreaterThanToken */ undefined ,
713
+ createAssignment (
714
+ createPropertyAccess (
715
+ createSuper ( ) ,
716
+ name ) ,
717
+ createIdentifier ( "v" )
718
+ )
719
+ )
720
+ )
721
+ ) ;
722
+ }
723
+ accessors . push (
724
+ createPropertyAssignment (
725
+ name ,
726
+ createObjectLiteral ( getterAndSetter ) ,
727
+ )
728
+ ) ;
729
+ } ) ;
730
+ return createVariableStatement (
731
+ /* modifiers */ undefined ,
732
+ createVariableDeclarationList (
733
+ [
734
+ createVariableDeclaration (
735
+ createFileLevelUniqueName ( "_super" ) ,
736
+ /* type */ undefined ,
737
+ createCall (
738
+ createPropertyAccess (
739
+ createIdentifier ( "Object" ) ,
740
+ "create"
741
+ ) ,
742
+ /* typeArguments */ undefined ,
743
+ [
744
+ createNull ( ) ,
745
+ createObjectLiteral ( accessors , /* multiline */ true )
746
+ ]
747
+ )
748
+ )
749
+ ] ,
750
+ NodeFlags . Const ) ) ;
751
+ }
752
+
623
753
const awaiterHelper : EmitHelper = {
624
754
name : "typescript:awaiter" ,
625
755
scoped : false ,
@@ -667,14 +797,14 @@ namespace ts {
667
797
name : "typescript:async-super" ,
668
798
scoped : true ,
669
799
text : helperString `
670
- const ${ "_super " } = name => super[name];`
800
+ const ${ "_superIndex " } = name => super[name];`
671
801
} ;
672
802
673
803
export const advancedAsyncSuperHelper : EmitHelper = {
674
804
name : "typescript:advanced-async-super" ,
675
805
scoped : true ,
676
806
text : helperString `
677
- const ${ "_super " } = (function (geti, seti) {
807
+ const ${ "_superIndex " } = (function (geti, seti) {
678
808
const cache = Object.create(null);
679
809
return name => cache[name] || (cache[name] = { get value() { return geti(name); }, set value(v) { seti(name, v); } });
680
810
})(name => super[name], (name, value) => super[name] = value);`
0 commit comments