@@ -10,21 +10,29 @@ namespace ts {
10
10
ClassAliases = 1 << 1 ,
11
11
}
12
12
13
+
13
14
/**
14
15
* A mapping of private names to information needed for transformation.
15
16
*/
16
- type PrivateNameEnvironment = UnderscoreEscapedMap < PrivateNamedInstanceField > ;
17
+ type PrivateNameEnvironment = UnderscoreEscapedMap < PrivateNamedInstanceField | PrivateNamedInstanceMethod > ;
17
18
18
19
/**
19
20
* Identifies the type of private name.
20
21
*/
21
- const enum PrivateNameType {
22
- InstanceField
22
+ const enum PrivateNamePlacement {
23
+ InstanceField ,
24
+ InstanceMethod
23
25
}
24
26
25
27
interface PrivateNamedInstanceField {
26
- type : PrivateNameType . InstanceField ;
27
- weakMapName : Identifier ;
28
+ placement : PrivateNamePlacement . InstanceField ;
29
+ accumulator : Identifier ;
30
+ }
31
+
32
+ interface PrivateNamedInstanceMethod {
33
+ placement : PrivateNamePlacement . InstanceMethod ;
34
+ accumulator : Identifier ;
35
+ funcName : Identifier ;
28
36
}
29
37
30
38
export function transformESNext ( context : TransformationContext ) {
@@ -365,8 +373,9 @@ namespace ts {
365
373
366
374
function transformClassMembers ( node : ClassDeclaration | ClassExpression , isDerivedClass : boolean ) {
367
375
// Declare private names.
368
- const privateProperties = filter ( node . members , isPrivatePropertyDeclaration ) ;
369
- privateProperties . forEach ( property => addPrivateNameToEnvironment ( property . name ) ) ;
376
+ node . members
377
+ . filter ( element => isNamedDeclaration ( element ) && isPrivateName ( element . name ) )
378
+ . forEach ( addPrivateName ) ;
370
379
371
380
const members : ClassElement [ ] = [ ] ;
372
381
const constructor = transformConstructor ( node , isDerivedClass ) ;
@@ -528,10 +537,10 @@ namespace ts {
528
537
if ( isPrivateName ( propertyName ) ) {
529
538
const privateNameInfo = accessPrivateName ( propertyName ) ;
530
539
if ( privateNameInfo ) {
531
- switch ( privateNameInfo . type ) {
532
- case PrivateNameType . InstanceField : {
540
+ switch ( privateNameInfo . placement ) {
541
+ case PrivateNamePlacement . InstanceField : {
533
542
return createCall (
534
- createPropertyAccess ( privateNameInfo . weakMapName , "set" ) ,
543
+ createPropertyAccess ( privateNameInfo . accumulator , "set" ) ,
535
544
/*typeArguments*/ undefined ,
536
545
[ receiver , initializer || createVoidZero ( ) ]
537
546
) ;
@@ -557,17 +566,68 @@ namespace ts {
557
566
privateNameEnvironmentStack . pop ( ) ;
558
567
}
559
568
560
- function addPrivateNameToEnvironment ( name : PrivateName ) {
569
+ function privateNamedMethodToFunction ( declaration : MethodDeclaration , funcName : Identifier , accumulator : Identifier ) : FunctionDeclaration {
570
+ const params = declaration . parameters ;
571
+ let body = getMutableClone ( declaration . body || createBlock ( [ ] , true ) ) ;
572
+ body = visitEachChild ( body , visitor , context ) ;
573
+ const toPrepend = startOnNewLine (
574
+ createStatement (
575
+ createClassPrivateNamedCallCheckHelper ( context , accumulator )
576
+ )
577
+ ) ;
578
+ body . statements = setTextRange (
579
+ createNodeArray ( [
580
+ toPrepend ,
581
+ ...body . statements
582
+ ] ) ,
583
+ body . statements
584
+ ) ;
585
+ const func = createFunctionDeclaration (
586
+ /* decorators */ undefined ,
587
+ /* modifiers */ undefined ,
588
+ /* asteriskToken */ undefined ,
589
+ funcName ,
590
+ /* typeParameters */ undefined ,
591
+ params ,
592
+ /* type */ undefined ,
593
+ body ) as FunctionDeclaration ;
594
+ return func ;
595
+ }
596
+
597
+
598
+ function addPrivateName ( element : ClassElement & { name : PrivateName } ) {
561
599
const env = last ( privateNameEnvironmentStack ) ;
562
- const text = getTextOfPropertyName ( name ) as string ;
563
- const weakMapName = createFileLevelUniqueName ( "_" + text . substring ( 1 ) ) ;
564
- hoistVariableDeclaration ( weakMapName ) ;
565
- env . set ( name . escapedText , { type : PrivateNameType . InstanceField , weakMapName } ) ;
566
- ( pendingExpressions || ( pendingExpressions = [ ] ) ) . push (
600
+ const text = getTextOfPropertyName ( element . name ) as string ;
601
+ const accumulator = createFileLevelUniqueName ( "_" + text . substring ( 1 ) ) ;
602
+ const { escapedText } = element . name ;
603
+ hoistVariableDeclaration ( accumulator ) ;
604
+
605
+ let identifierName : string ;
606
+ if ( hasModifier ( element , ModifierFlags . Static ) ) {
607
+ // statics not supported yet
608
+ return ;
609
+ }
610
+ if ( isPropertyDeclaration ( element ) ) {
611
+ identifierName = "WeakMap" ;
612
+ env . set ( escapedText , { placement : PrivateNamePlacement . InstanceField , accumulator } ) ;
613
+ }
614
+ else if ( isMethodDeclaration ( element ) ) {
615
+ identifierName = "WeakSet" ;
616
+ const escapedText = element . name . escapedText ;
617
+ const escapedTextNoHash = `_${ `${ escapedText } ` . slice ( 1 ) } ` ;
618
+ const funcName : Identifier = createFileLevelUniqueName ( escapedTextNoHash ) ;
619
+ const func = privateNamedMethodToFunction ( element , funcName , accumulator ) ;
620
+ env . set ( escapedText , { placement : PrivateNamePlacement . InstanceMethod , accumulator, funcName } ) ;
621
+ ( pendingStatements = pendingStatements || [ ] ) . push ( func ) ;
622
+ }
623
+ else {
624
+ return ;
625
+ }
626
+ ( pendingExpressions = pendingExpressions || [ ] ) . push (
567
627
createAssignment (
568
- weakMapName ,
628
+ accumulator ,
569
629
createNew (
570
- createIdentifier ( "WeakMap" ) ,
630
+ createIdentifier ( identifierName ) ,
571
631
/*typeArguments*/ undefined ,
572
632
[ ]
573
633
)
@@ -589,14 +649,14 @@ namespace ts {
589
649
if ( isPrivateName ( node . name ) ) {
590
650
const privateNameInfo = accessPrivateName ( node . name ) ;
591
651
if ( privateNameInfo ) {
592
- switch ( privateNameInfo . type ) {
593
- case PrivateNameType . InstanceField :
652
+ switch ( privateNameInfo . placement ) {
653
+ case PrivateNamePlacement . InstanceField :
594
654
return setOriginalNode (
595
655
setTextRange (
596
656
createClassPrivateFieldGetHelper (
597
657
context ,
598
658
visitNode ( node . expression , visitor , isExpression ) ,
599
- privateNameInfo . weakMapName
659
+ privateNameInfo . accumulator
600
660
) ,
601
661
node
602
662
) ,
@@ -903,7 +963,7 @@ namespace ts {
903
963
}
904
964
else if ( isAssignmentExpression ( node ) && isPropertyAccessExpression ( node . left ) && isPrivateName ( node . left . name ) ) {
905
965
const privateNameInfo = accessPrivateName ( node . left . name ) ;
906
- if ( privateNameInfo && privateNameInfo . type === PrivateNameType . InstanceField ) {
966
+ if ( privateNameInfo && privateNameInfo . placement === PrivateNamePlacement . InstanceField ) {
907
967
if ( isCompoundAssignment ( node . operatorToken . kind ) ) {
908
968
const isReceiverInlineable = isSimpleInlineableExpression ( node . left . expression ) ;
909
969
const getReceiver = isReceiverInlineable ? node . left . expression : createTempVariable ( hoistVariableDeclaration ) ;
@@ -914,12 +974,12 @@ namespace ts {
914
974
createClassPrivateFieldSetHelper (
915
975
context ,
916
976
setReceiver ,
917
- privateNameInfo . weakMapName ,
977
+ privateNameInfo . accumulator ,
918
978
createBinary (
919
979
createClassPrivateFieldGetHelper (
920
980
context ,
921
981
getReceiver ,
922
- privateNameInfo . weakMapName
982
+ privateNameInfo . accumulator
923
983
) ,
924
984
getOperatorForCompoundAssignment ( node . operatorToken . kind ) ,
925
985
visitNode ( node . right , visitor )
@@ -933,7 +993,7 @@ namespace ts {
933
993
createClassPrivateFieldSetHelper (
934
994
context ,
935
995
node . left . expression ,
936
- privateNameInfo . weakMapName ,
996
+ privateNameInfo . accumulator ,
937
997
visitNode ( node . right , visitor )
938
998
) ,
939
999
node
@@ -1789,4 +1849,15 @@ namespace ts {
1789
1849
context . requestEmitHelper ( classPrivateFieldSetHelper ) ;
1790
1850
return createCall ( getHelperName ( "_classPrivateFieldSet" ) , /* typeArguments */ undefined , [ receiver , privateField , value ] ) ;
1791
1851
}
1852
+ const classPrivateNamedCallCheckHelper : EmitHelper = {
1853
+ name : "typescript:classPrivateNamedCallCheck" ,
1854
+ scoped : false ,
1855
+ text : `var _classPrivateNamedCallCheck = function (receiver, privateSet) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get weak field on non-instance"); }};`
1856
+ } ;
1857
+
1858
+ function createClassPrivateNamedCallCheckHelper ( context : TransformationContext , weakSet : Identifier ) {
1859
+ context . requestEmitHelper ( classPrivateNamedCallCheckHelper ) ;
1860
+ return createCall ( getHelperName ( "_classPrivateNamedCallCheck" ) , /* typeArguments */ undefined , [ createThis ( ) , weakSet ] ) ;
1861
+ }
1862
+
1792
1863
}
0 commit comments