1515using System . Runtime . CompilerServices ;
1616using System . Runtime . Versioning ;
1717using System . Threading ;
18+ using Microsoft . CodeAnalysis . PooledObjects ;
1819using Roslyn . Utilities ;
1920
2021namespace Microsoft . CodeAnalysis . Diagnostics
@@ -208,7 +209,7 @@ private static AnalyzerLoadFailureEventArgs CreateAnalyzerFailedArgs(Exception e
208209 return new AnalyzerLoadFailureEventArgs ( errorCode , message , e , typeName ) ;
209210 }
210211
211- internal ImmutableSortedDictionary < string , ImmutableSortedSet < string > > GetAnalyzerTypeNameMap ( )
212+ internal ImmutableSortedDictionary < string , ImmutableHashSet < string > > GetAnalyzerTypeNameMap ( )
212213 {
213214 return _diagnosticAnalyzers . GetExtensionTypeNameMap ( ) ;
214215 }
@@ -219,7 +220,7 @@ internal ImmutableSortedDictionary<string, ImmutableSortedSet<string>> GetAnalyz
219220 /// <exception cref="BadImageFormatException">The PE image format is invalid.</exception>
220221 /// <exception cref="IOException">IO error reading the metadata.</exception>
221222 [ PerformanceSensitive ( "https://github.com/dotnet/roslyn/issues/30449" ) ]
222- private static ImmutableSortedDictionary < string , ImmutableSortedSet < string > > GetAnalyzerTypeNameMap ( string fullPath , Type attributeType , AttributeLanguagesFunc languagesFunc )
223+ private static ImmutableSortedDictionary < string , ImmutableHashSet < string > > GetAnalyzerTypeNameMap ( string fullPath , Type attributeType , AttributeLanguagesFunc languagesFunc )
223224 {
224225 using var assembly = AssemblyMetadata . CreateFromFile ( fullPath ) ;
225226
@@ -235,7 +236,7 @@ where supportedLanguages.Any()
235236 from supportedLanguage in supportedLanguages
236237 group typeName by supportedLanguage ;
237238
238- return typeNameMap . ToImmutableSortedDictionary ( g => g . Key , g => g . ToImmutableSortedSet ( StringComparer . OrdinalIgnoreCase ) , StringComparer . OrdinalIgnoreCase ) ;
239+ return typeNameMap . ToImmutableSortedDictionary ( g => g . Key , g => g . ToImmutableHashSet ( ) , StringComparer . OrdinalIgnoreCase ) ;
239240 }
240241
241242 private static IEnumerable < string > GetSupportedLanguages ( TypeDefinition typeDef , PEModule peModule , Type attributeType , AttributeLanguagesFunc languagesFunc )
@@ -357,7 +358,7 @@ private sealed class Extensions<TExtension>
357358 private readonly Func < object ? , TExtension ? > ? _coerceFunction ;
358359 private ImmutableArray < TExtension > _lazyAllExtensions ;
359360 private ImmutableDictionary < string , ImmutableArray < TExtension > > _lazyExtensionsPerLanguage ;
360- private ImmutableSortedDictionary < string , ImmutableSortedSet < string > > ? _lazyExtensionTypeNameMap ;
361+ private ImmutableSortedDictionary < string , ImmutableHashSet < string > > ? _lazyExtensionTypeNameMap ;
361362
362363 internal Extensions ( AnalyzerFileReference reference , Type attributeType , AttributeLanguagesFunc languagesFunc , bool allowNetFramework , Func < object ? , TExtension ? > ? coerceFunction = null )
363364 {
@@ -432,7 +433,7 @@ private static ImmutableArray<TExtension> CreateLanguageSpecificExtensions(strin
432433 return builder . ToImmutable ( ) ;
433434 }
434435
435- internal ImmutableSortedDictionary < string , ImmutableSortedSet < string > > GetExtensionTypeNameMap ( )
436+ internal ImmutableSortedDictionary < string , ImmutableHashSet < string > > GetExtensionTypeNameMap ( )
436437 {
437438 if ( _lazyExtensionTypeNameMap == null )
438439 {
@@ -445,7 +446,7 @@ internal ImmutableSortedDictionary<string, ImmutableSortedSet<string>> GetExtens
445446
446447 internal void AddExtensions ( ImmutableSortedDictionary < string , ImmutableArray < TExtension > > . Builder builder )
447448 {
448- ImmutableSortedDictionary < string , ImmutableSortedSet < string > > analyzerTypeNameMap ;
449+ ImmutableSortedDictionary < string , ImmutableHashSet < string > > analyzerTypeNameMap ;
449450 Assembly analyzerAssembly ;
450451
451452 try
@@ -493,7 +494,7 @@ internal void AddExtensions(ImmutableSortedDictionary<string, ImmutableArray<TEx
493494
494495 internal void AddExtensions ( ImmutableArray < TExtension > . Builder builder , string language , Func < TExtension , bool > ? shouldInclude = null )
495496 {
496- ImmutableSortedDictionary < string , ImmutableSortedSet < string > > analyzerTypeNameMap ;
497+ ImmutableSortedDictionary < string , ImmutableHashSet < string > > analyzerTypeNameMap ;
497498 Assembly analyzerAssembly ;
498499
499500 try
@@ -557,22 +558,24 @@ bool CheckAssemblyReferencesNewerCompiler(Assembly analyzerAssembly)
557558 return false ;
558559 }
559560
560- private ImmutableArray < TExtension > GetLanguageSpecificAnalyzers ( Assembly analyzerAssembly , ImmutableSortedDictionary < string , ImmutableSortedSet < string > > analyzerTypeNameMap , string language , ref bool reportedError )
561+ private ImmutableArray < TExtension > GetLanguageSpecificAnalyzers ( Assembly analyzerAssembly , ImmutableSortedDictionary < string , ImmutableHashSet < string > > analyzerTypeNameMap , string language , ref bool reportedError )
561562 {
562- ImmutableSortedSet < string > ? languageSpecificAnalyzerTypeNames ;
563+ ImmutableHashSet < string > ? languageSpecificAnalyzerTypeNames ;
563564 if ( ! analyzerTypeNameMap . TryGetValue ( language , out languageSpecificAnalyzerTypeNames ) )
564565 {
565566 return ImmutableArray < TExtension > . Empty ;
566567 }
567568 return this . GetAnalyzersForTypeNames ( analyzerAssembly , languageSpecificAnalyzerTypeNames , ref reportedError ) ;
568569 }
569570
570- private ImmutableArray < TExtension > GetAnalyzersForTypeNames ( Assembly analyzerAssembly , IEnumerable < string > analyzerTypeNames , ref bool reportedError )
571+ private ImmutableArray < TExtension > GetAnalyzersForTypeNames ( Assembly analyzerAssembly , ImmutableHashSet < string > analyzerTypeNames , ref bool reportedError )
571572 {
572- var analyzers = ImmutableArray . CreateBuilder < TExtension > ( ) ;
573+ var builder = ArrayBuilder < ( string typeName , TExtension analyzer ) > . GetInstance ( ) ;
573574
574575 // Given the type names, get the actual System.Type and try to create an instance of the type through reflection.
575- foreach ( var typeName in analyzerTypeNames )
576+ // Randomize the order we instantiate analyzers to avoid static constructor/JIT contention, but still return
577+ // the list of analyzers in the order of the sorted type names for deterministic purpose.
578+ foreach ( var typeName in shuffle ( analyzerTypeNames ) )
576579 {
577580 Type ? type ;
578581 try
@@ -617,11 +620,36 @@ private ImmutableArray<TExtension> GetAnalyzersForTypeNames(Assembly analyzerAss
617620 TExtension ? analyzer = typeInstance as TExtension ?? _coerceFunction ? . Invoke ( typeInstance ) ;
618621 if ( analyzer != null )
619622 {
620- analyzers . Add ( analyzer ) ;
623+ builder . Add ( ( typeName , analyzer ) ) ;
621624 }
622625 }
623626
624- return analyzers . ToImmutable ( ) ;
627+ builder . Sort ( static ( x , y ) => string . Compare ( x . typeName , y . typeName , StringComparison . OrdinalIgnoreCase ) ) ;
628+ var analyzers = builder . SelectAsArray ( x => x . analyzer ) ;
629+ builder . Free ( ) ;
630+
631+ return analyzers ;
632+
633+ static IEnumerable < string > shuffle ( ImmutableHashSet < string > source )
634+ {
635+ var random =
636+ #if NET6_0_OR_GREATER
637+ Random . Shared ;
638+ #else
639+ new Random ( ) ;
640+ #endif
641+ var builder = ArrayBuilder < string > . GetInstance ( source . Count ) ;
642+ builder . AddRange ( source ) ;
643+
644+ for ( var i = builder . Count - 1 ; i >= 0 ; i -- )
645+ {
646+ var swapIndex = random . Next ( i + 1 ) ;
647+ yield return builder [ swapIndex ] ;
648+ builder [ swapIndex ] = builder [ i ] ;
649+ }
650+
651+ builder . Free ( ) ;
652+ }
625653 }
626654 }
627655
0 commit comments