Skip to content

Commit 5766465

Browse files
committed
Enable pooling for objects in new analyzer
1 parent a1bfe3d commit 5766465

File tree

1 file changed

+61
-2
lines changed

1 file changed

+61
-2
lines changed

src/CommunityToolkit.Mvvm.SourceGenerators/Diagnostics/Analyzers/UseObservablePropertyOnSemiAutoPropertyAnalyzer.cs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#if ROSLYN_4_12_0_OR_GREATER
66

7+
using System;
78
using System.Collections.Generic;
89
using System.Collections.Immutable;
910
using System.Diagnostics.CodeAnalysis;
@@ -14,6 +15,7 @@
1415
using Microsoft.CodeAnalysis.CSharp.Syntax;
1516
using Microsoft.CodeAnalysis.Diagnostics;
1617
using Microsoft.CodeAnalysis.Operations;
18+
using Microsoft.CodeAnalysis.PooledObjects;
1719
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;
1820

1921
namespace CommunityToolkit.Mvvm.SourceGenerators;
@@ -24,6 +26,22 @@ namespace CommunityToolkit.Mvvm.SourceGenerators;
2426
[DiagnosticAnalyzer(LanguageNames.CSharp)]
2527
public sealed class UseObservablePropertyOnSemiAutoPropertyAnalyzer : DiagnosticAnalyzer
2628
{
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+
2745
/// <inheritdoc/>
2846
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(UseObservablePropertyOnSemiAutoProperty);
2947

@@ -69,7 +87,8 @@ public override void Initialize(AnalysisContext context)
6987
return;
7088
}
7189

72-
Dictionary<IPropertySymbol, bool[]> propertyMap = new(SymbolEqualityComparer.Default);
90+
Dictionary<IPropertySymbol, bool[]> propertyMap = PropertyMapPool.Allocate();
91+
Stack<bool[]> propertyFlagsStack = PropertyFlagsStackPool.Allocate();
7392

7493
// Crawl all members to discover properties that might be of interest
7594
foreach (ISymbol memberSymbol in typeSymbol.GetMembers())
@@ -97,8 +116,13 @@ public override void Initialize(AnalysisContext context)
97116
continue;
98117
}
99118

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+
100124
// Track the property for later
101-
propertyMap.Add(propertySymbol, new bool[2]);
125+
propertyMap.Add(propertySymbol, flags);
102126
}
103127

104128
// We want to process both accessors, where we specifically need both the syntax
@@ -246,6 +270,24 @@ public override void Initialize(AnalysisContext context)
246270
pair.Key.Name));
247271
}
248272
}
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);
249291
});
250292
}, SymbolKind.NamedType);
251293
});
@@ -282,6 +324,23 @@ private static bool TryGetSetPropertyMethodSymbol(INamedTypeSymbol observableObj
282324

283325
return false;
284326
}
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+
}
285344
}
286345

287346
#endif

0 commit comments

Comments
 (0)