@@ -10,21 +10,29 @@ namespace ts {
1010 ClassAliases = 1 << 1 ,
1111 }
1212
13+
1314 /**
1415 * A mapping of private names to information needed for transformation.
1516 */
16- type PrivateNameEnvironment = UnderscoreEscapedMap < PrivateNamedInstanceField > ;
17+ type PrivateNameEnvironment = UnderscoreEscapedMap < PrivateNamedInstanceField | PrivateNamedInstanceMethod > ;
1718
1819 /**
1920 * Identifies the type of private name.
2021 */
21- const enum PrivateNameType {
22- InstanceField
22+ const enum PrivateNamePlacement {
23+ InstanceField ,
24+ InstanceMethod
2325 }
2426
2527 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 ;
2836 }
2937
3038 export function transformESNext ( context : TransformationContext ) {
@@ -365,8 +373,9 @@ namespace ts {
365373
366374 function transformClassMembers ( node : ClassDeclaration | ClassExpression , isDerivedClass : boolean ) {
367375 // 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 ) ;
370379
371380 const members : ClassElement [ ] = [ ] ;
372381 const constructor = transformConstructor ( node , isDerivedClass ) ;
@@ -528,10 +537,10 @@ namespace ts {
528537 if ( isPrivateName ( propertyName ) ) {
529538 const privateNameInfo = accessPrivateName ( propertyName ) ;
530539 if ( privateNameInfo ) {
531- switch ( privateNameInfo . type ) {
532- case PrivateNameType . InstanceField : {
540+ switch ( privateNameInfo . placement ) {
541+ case PrivateNamePlacement . InstanceField : {
533542 return createCall (
534- createPropertyAccess ( privateNameInfo . weakMapName , "set" ) ,
543+ createPropertyAccess ( privateNameInfo . accumulator , "set" ) ,
535544 /*typeArguments*/ undefined ,
536545 [ receiver , initializer || createVoidZero ( ) ]
537546 ) ;
@@ -557,17 +566,68 @@ namespace ts {
557566 privateNameEnvironmentStack . pop ( ) ;
558567 }
559568
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 } ) {
561599 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 (
567627 createAssignment (
568- weakMapName ,
628+ accumulator ,
569629 createNew (
570- createIdentifier ( "WeakMap" ) ,
630+ createIdentifier ( identifierName ) ,
571631 /*typeArguments*/ undefined ,
572632 [ ]
573633 )
@@ -589,14 +649,14 @@ namespace ts {
589649 if ( isPrivateName ( node . name ) ) {
590650 const privateNameInfo = accessPrivateName ( node . name ) ;
591651 if ( privateNameInfo ) {
592- switch ( privateNameInfo . type ) {
593- case PrivateNameType . InstanceField :
652+ switch ( privateNameInfo . placement ) {
653+ case PrivateNamePlacement . InstanceField :
594654 return setOriginalNode (
595655 setTextRange (
596656 createClassPrivateFieldGetHelper (
597657 context ,
598658 visitNode ( node . expression , visitor , isExpression ) ,
599- privateNameInfo . weakMapName
659+ privateNameInfo . accumulator
600660 ) ,
601661 node
602662 ) ,
@@ -903,7 +963,7 @@ namespace ts {
903963 }
904964 else if ( isAssignmentExpression ( node ) && isPropertyAccessExpression ( node . left ) && isPrivateName ( node . left . name ) ) {
905965 const privateNameInfo = accessPrivateName ( node . left . name ) ;
906- if ( privateNameInfo && privateNameInfo . type === PrivateNameType . InstanceField ) {
966+ if ( privateNameInfo && privateNameInfo . placement === PrivateNamePlacement . InstanceField ) {
907967 if ( isCompoundAssignment ( node . operatorToken . kind ) ) {
908968 const isReceiverInlineable = isSimpleInlineableExpression ( node . left . expression ) ;
909969 const getReceiver = isReceiverInlineable ? node . left . expression : createTempVariable ( hoistVariableDeclaration ) ;
@@ -914,12 +974,12 @@ namespace ts {
914974 createClassPrivateFieldSetHelper (
915975 context ,
916976 setReceiver ,
917- privateNameInfo . weakMapName ,
977+ privateNameInfo . accumulator ,
918978 createBinary (
919979 createClassPrivateFieldGetHelper (
920980 context ,
921981 getReceiver ,
922- privateNameInfo . weakMapName
982+ privateNameInfo . accumulator
923983 ) ,
924984 getOperatorForCompoundAssignment ( node . operatorToken . kind ) ,
925985 visitNode ( node . right , visitor )
@@ -933,7 +993,7 @@ namespace ts {
933993 createClassPrivateFieldSetHelper (
934994 context ,
935995 node . left . expression ,
936- privateNameInfo . weakMapName ,
996+ privateNameInfo . accumulator ,
937997 visitNode ( node . right , visitor )
938998 ) ,
939999 node
@@ -1789,4 +1849,15 @@ namespace ts {
17891849 context . requestEmitHelper ( classPrivateFieldSetHelper ) ;
17901850 return createCall ( getHelperName ( "_classPrivateFieldSet" ) , /* typeArguments */ undefined , [ receiver , privateField , value ] ) ;
17911851 }
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+
17921863}
0 commit comments