diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs index 17d6309cf5f..56628404d9c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/SourceGenerators/RazorSourceGenerator.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; using System.IO; using System.Linq; using Microsoft.AspNetCore.Razor; @@ -21,6 +23,8 @@ public partial class RazorSourceGenerator : IIncrementalGenerator { private static RazorSourceGeneratorEventSource Log => RazorSourceGeneratorEventSource.Log; + private ReadOnlyDictionary> _descriptorCache = new(new Dictionary>()); + // Testing usage only. private readonly string? _testSuppressUniqueIds; @@ -195,7 +199,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return hasRazorFilesA == hasRazorFilesB; }) - .Select(static (pair, _) => + .Select((pair, _) => { var ((compilation, razorSourceGeneratorOptions), hasRazorFiles) = pair; if (!hasRazorFiles) @@ -211,14 +215,34 @@ public void Initialize(IncrementalGeneratorInitializationContext context) // So, we start with a larger capacity to avoid extra array copies. var results = new List(capacity: 128); + var oldCachedDescriptors = _descriptorCache; + var newCachedDescriptors = new Dictionary>(oldCachedDescriptors.Count); + var descriptorsBuilder = new List(); + foreach (var reference in compilation.References) { - if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) + if (!oldCachedDescriptors.TryGetValue(reference, out var cachedDescriptors)) { - tagHelperFeature.CollectDescriptors(assembly, results); + if (compilation.GetAssemblyOrModuleSymbol(reference) is IAssemblySymbol assembly) + { + tagHelperFeature.CollectDescriptors(assembly, descriptorsBuilder); + } + + cachedDescriptors = descriptorsBuilder.Count > 0 ? descriptorsBuilder.ToImmutableArray() : []; + descriptorsBuilder.Clear(); + } + + newCachedDescriptors[reference] = cachedDescriptors; + + // Avoid List.AddRange to avoid boxing of the IA and enumerator allocation + foreach (var descriptor in cachedDescriptors) + { + results.Add(descriptor); } } + _descriptorCache = new ReadOnlyDictionary>(newCachedDescriptors); + RazorSourceGeneratorEventSource.Log.DiscoverTagHelpersFromReferencesStop(); return results;