4
4
5
5
#if ROSLYN_4_12_0_OR_GREATER
6
6
7
+ using System ;
7
8
using System . Collections . Generic ;
8
9
using System . Collections . Immutable ;
9
10
using System . Diagnostics . CodeAnalysis ;
14
15
using Microsoft . CodeAnalysis . CSharp . Syntax ;
15
16
using Microsoft . CodeAnalysis . Diagnostics ;
16
17
using Microsoft . CodeAnalysis . Operations ;
18
+ using Microsoft . CodeAnalysis . PooledObjects ;
17
19
using static CommunityToolkit . Mvvm . SourceGenerators . Diagnostics . DiagnosticDescriptors ;
18
20
19
21
namespace CommunityToolkit . Mvvm . SourceGenerators ;
@@ -24,6 +26,22 @@ namespace CommunityToolkit.Mvvm.SourceGenerators;
24
26
[ DiagnosticAnalyzer ( LanguageNames . CSharp ) ]
25
27
public sealed class UseObservablePropertyOnSemiAutoPropertyAnalyzer : DiagnosticAnalyzer
26
28
{
29
+ /// <summary>
30
+ /// The number of pooled flags per stack (ie. how many properties we expect on average per type).
31
+ /// </summary>
32
+ private const int NumberOfPooledFlagsPerStack = 20 ;
33
+
34
+ /// <summary>
35
+ /// Shared pool for <see cref="Dictionary{TKey, TValue}"/> instances.
36
+ /// </summary>
37
+ [ SuppressMessage ( "MicrosoftCodeAnalysisPerformance" , "RS1008" , Justification = "This is a pool of (empty) dictionaries, it is not actually storing compilation data." ) ]
38
+ private static readonly ObjectPool < Dictionary < IPropertySymbol , bool [ ] > > PropertyMapPool = new ( static ( ) => new Dictionary < IPropertySymbol , bool [ ] > ( SymbolEqualityComparer . Default ) ) ;
39
+
40
+ /// <summary>
41
+ /// Shared pool for <see cref="Stack{T}"/>-s of flags, one per type being processed.
42
+ /// </summary>
43
+ private static readonly ObjectPool < Stack < bool [ ] > > PropertyFlagsStackPool = new ( CreatePropertyFlagsStack ) ;
44
+
27
45
/// <inheritdoc/>
28
46
public override ImmutableArray < DiagnosticDescriptor > SupportedDiagnostics { get ; } = ImmutableArray . Create ( UseObservablePropertyOnSemiAutoProperty ) ;
29
47
@@ -69,7 +87,8 @@ public override void Initialize(AnalysisContext context)
69
87
return ;
70
88
}
71
89
72
- Dictionary < IPropertySymbol , bool [ ] > propertyMap = new ( SymbolEqualityComparer . Default ) ;
90
+ Dictionary < IPropertySymbol , bool [ ] > propertyMap = PropertyMapPool . Allocate ( ) ;
91
+ Stack < bool [ ] > propertyFlagsStack = PropertyFlagsStackPool . Allocate ( ) ;
73
92
74
93
// Crawl all members to discover properties that might be of interest
75
94
foreach ( ISymbol memberSymbol in typeSymbol . GetMembers ( ) )
@@ -97,8 +116,13 @@ public override void Initialize(AnalysisContext context)
97
116
continue ;
98
117
}
99
118
119
+ // Take an array from the stack or create a new one otherwise
120
+ bool [ ] flags = propertyFlagsStack . Count > 0
121
+ ? propertyFlagsStack . Pop ( )
122
+ : new bool [ 2 ] ;
123
+
100
124
// Track the property for later
101
- propertyMap . Add ( propertySymbol , new bool [ 2 ] ) ;
125
+ propertyMap . Add ( propertySymbol , flags ) ;
102
126
}
103
127
104
128
// We want to process both accessors, where we specifically need both the syntax
@@ -246,6 +270,24 @@ public override void Initialize(AnalysisContext context)
246
270
pair . Key . Name ) ) ;
247
271
}
248
272
}
273
+
274
+ // Before clearing the dictionary, move back all values to the stack
275
+ foreach ( bool [ ] propertyFlags in propertyMap . Values )
276
+ {
277
+ // Make sure the array is cleared before returning it
278
+ propertyFlags . AsSpan ( ) . Clear ( ) ;
279
+
280
+ propertyFlagsStack . Push ( propertyFlags ) ;
281
+ }
282
+
283
+ // We are now done processing the symbol, we can return the dictionary.
284
+ // Note that we must clear it before doing so to avoid leaks and issues.
285
+ propertyMap . Clear ( ) ;
286
+
287
+ PropertyMapPool . Free ( propertyMap ) ;
288
+
289
+ // Also do the same for the stack, except we don't need to clean it (since it roots no compilation objects)
290
+ PropertyFlagsStackPool . Free ( propertyFlagsStack ) ;
249
291
} ) ;
250
292
} , SymbolKind . NamedType ) ;
251
293
} ) ;
@@ -282,6 +324,23 @@ private static bool TryGetSetPropertyMethodSymbol(INamedTypeSymbol observableObj
282
324
283
325
return false ;
284
326
}
327
+
328
+ /// <summary>
329
+ /// Produces a new <see cref="Stack{T}"/> instance to pool.
330
+ /// </summary>
331
+ /// <returns>The resulting <see cref="Stack{T}"/> instance to use.</returns>
332
+ private static Stack < bool [ ] > CreatePropertyFlagsStack ( )
333
+ {
334
+ static IEnumerable < bool [ ] > EnumerateFlags ( )
335
+ {
336
+ for ( int i = 0 ; i < NumberOfPooledFlagsPerStack ; i ++ )
337
+ {
338
+ yield return new bool [ 2 ] ;
339
+ }
340
+ }
341
+
342
+ return new ( EnumerateFlags ( ) ) ;
343
+ }
285
344
}
286
345
287
346
#endif
0 commit comments