@@ -301,19 +301,19 @@ namespace ts {
301301 return members . length > 0 && members [ 0 ] [ 0 ] === 0 ? members [ 0 ] [ 1 ] : "0" ;
302302 }
303303 if ( isFlags ) {
304- let result = "" ;
304+ const result : string [ ] = [ ] ;
305305 let remainingFlags = value ;
306306 for ( const [ enumValue , enumName ] of members ) {
307307 if ( enumValue > value ) {
308308 break ;
309309 }
310310 if ( enumValue !== 0 && enumValue & value ) {
311- result = ` ${ result } ${ result ? "|" : "" } ${ enumName } ` ;
311+ result . push ( enumName ) ;
312312 remainingFlags &= ~ enumValue ;
313313 }
314314 }
315315 if ( remainingFlags === 0 ) {
316- return result ;
316+ return result . join ( "|" ) ;
317317 }
318318 }
319319 else {
@@ -326,7 +326,17 @@ namespace ts {
326326 return value . toString ( ) ;
327327 }
328328
329+ const enumMemberCache = new Map < any , SortedReadonlyArray < [ number , string ] > > ( ) ;
330+
329331 function getEnumMembers ( enumObject : any ) {
332+ // Assuming enum objects do not change at runtime, we can cache the enum members list
333+ // to reuse later. This saves us from reconstructing this each and every time we call
334+ // a formatting function (which can be expensive for large enums like SyntaxKind).
335+ const existing = enumMemberCache . get ( enumObject ) ;
336+ if ( existing ) {
337+ return existing ;
338+ }
339+
330340 const result : [ number , string ] [ ] = [ ] ;
331341 for ( const name in enumObject ) {
332342 const value = enumObject [ name ] ;
@@ -335,7 +345,9 @@ namespace ts {
335345 }
336346 }
337347
338- return stableSort < [ number , string ] > ( result , ( x , y ) => compareValues ( x [ 0 ] , y [ 0 ] ) ) ;
348+ const sorted = stableSort < [ number , string ] > ( result , ( x , y ) => compareValues ( x [ 0 ] , y [ 0 ] ) ) ;
349+ enumMemberCache . set ( enumObject , sorted ) ;
350+ return sorted ;
339351 }
340352
341353 export function formatSyntaxKind ( kind : SyntaxKind | undefined ) : string {
0 commit comments