diff --git a/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs index 5937e1dec48d6..7bb2618b19b0a 100644 --- a/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs +++ b/src/Workspaces/Core/Portable/Serialization/SerializerService_Reference.cs @@ -70,7 +70,7 @@ protected virtual Checksum CreateChecksum(AnalyzerReference reference) { case AnalyzerFileReference fileReference: writer.WriteString(fileReference.FullPath); - writer.WriteGuid(IsolatedAnalyzerReferenceSet.TryGetAnalyzerFileReferenceMvid(fileReference)); + writer.WriteGuid(IsolatedAnalyzerReferenceSet.TryGetFileReferenceMvid(fileReference.FullPath)); break; case AnalyzerImageReference analyzerImageReference: diff --git a/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.Core.cs b/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.Core.cs index cf92f4720e2ad..b013c9736ed04 100644 --- a/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.Core.cs +++ b/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.Core.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Runtime.CompilerServices; using System.Runtime.Loader; using System.Threading; using System.Threading.Tasks; @@ -82,7 +83,11 @@ internal sealed partial class IsolatedAnalyzerReferenceSet /// /// Guarded by . Note that while the gate is static, this is instance data on the . And we only want to mutate that instance data from one thread at a - /// time. + /// time. + /// + /// Stored as an so this can safely be used as a key in caches that map from a list + /// of items to some value (for example in an . + /// private readonly Dictionary> _analyzerReferences = []; private IsolatedAnalyzerReferenceSet( @@ -304,7 +309,7 @@ static void PopulateFilePathToMvidMap( // Can ignore all other analyzer reference types. This is only about analyzer references changing on disk. var analyzerReference = GetUnderlyingAnalyzerReference(initialReference); if (analyzerReference is AnalyzerFileReference analyzerFileReference) - pathToMvidMap[analyzerFileReference.FullPath] = TryGetAnalyzerFileReferenceMvid(analyzerFileReference); + pathToMvidMap[analyzerFileReference.FullPath] = TryGetFileReferenceMvid(analyzerFileReference.FullPath); } } } diff --git a/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.Desktop.cs b/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.Desktop.cs index 48f925592c3b8..06505901c4213 100644 --- a/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.Desktop.cs +++ b/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.Desktop.cs @@ -5,6 +5,7 @@ #if !NET using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; diff --git a/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.cs b/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.cs index 73b337dbacd3f..dc661417bc6c9 100644 --- a/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.cs +++ b/src/Workspaces/Core/Portable/Workspace/IsolatedAnalyzerReferenceSet.cs @@ -51,11 +51,11 @@ private static async ValueTask> DefaultCreateI return await getReferencesAsync().ConfigureAwait(false); } - public static Guid TryGetAnalyzerFileReferenceMvid(AnalyzerFileReference file) + public static Guid TryGetFileReferenceMvid(string filePath) { try { - return AssemblyUtilities.ReadMvid(file.FullPath); + return AssemblyUtilities.ReadMvid(filePath); } catch { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs index 8c96d86647948..40abdf29896e9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs @@ -8,6 +8,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -568,16 +569,38 @@ public static Checksum GetOrCreate(TValue value, Func.GetOrCreate(value, checksumCreator, arg); } + public static ChecksumCollection GetOrCreateChecksumCollection( + ImmutableArray references, ISerializerService serializer, CancellationToken cancellationToken) where TReference : class + { + // Grab the internal array from the immutable array. This is safe as the callers can't modify it, and we just + // want the internal reference object to use as the key in the cache. + return GetOrCreateChecksumCollection( + ImmutableCollectionsMarshal.AsArray(references)!, serializer, cancellationToken); + } + public static ChecksumCollection GetOrCreateChecksumCollection( IReadOnlyList references, ISerializerService serializer, CancellationToken cancellationToken) where TReference : class { + // Cache both at the list-of-references level... return StronglyTypedChecksumCache, ChecksumCollection>.GetOrCreate( references, static (references, tuple) => { + var (serializer, cancellationToken) = tuple; var checksums = new FixedSizeArrayBuilder(references.Count); foreach (var reference in references) - checksums.Add(tuple.serializer.CreateChecksum(reference, tuple.cancellationToken)); + { + // ... and cache at the individual reference level. + var checksum = GetOrCreate( + reference, + static (reference, arg) => + { + var (serializer, cancellationToken) = arg; + return serializer.CreateChecksum(reference, cancellationToken); + }, + arg: (serializer, cancellationToken)); + checksums.Add(checksum); + } return new ChecksumCollection(checksums.MoveToImmutable()); },