Merge pull request #1176 from sharwell/reference-cache

Implement a cache for ReferenceAssemblies instances
This commit is contained in:
Sam Harwell 2024-08-14 15:06:13 -05:00 коммит произвёл GitHub
Родитель d0c5cbf09a af952dcb1b
Коммит bcd561eca5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
3 изменённых файлов: 246 добавлений и 8 удалений

Просмотреть файл

@ -5,13 +5,17 @@
using System;
using NuGet.Versioning;
#if !NETCOREAPP
using System.Collections.Generic;
#endif
namespace Microsoft.CodeAnalysis.Testing
{
/// <summary>
/// Represents the core identity of a NuGet package.
/// </summary>
/// <seealso cref="NuGet.Packaging.Core.PackageIdentity"/>
public sealed class PackageIdentity
public sealed class PackageIdentity : IEquatable<PackageIdentity?>
{
/// <summary>
/// Initializes a new instance of the <see cref="PackageIdentity"/> class with the specified name and version.
@ -41,6 +45,28 @@ namespace Microsoft.CodeAnalysis.Testing
/// <seealso cref="NuGet.Packaging.Core.PackageIdentity.Version"/>
public string Version { get; }
public override int GetHashCode()
{
#if NETCOREAPP
return HashCode.Combine(Id, Version);
#else
var hashCode = -612338121;
hashCode = (hashCode * -1521134295) + EqualityComparer<string>.Default.GetHashCode(Id);
hashCode = (hashCode * -1521134295) + EqualityComparer<string>.Default.GetHashCode(Version);
return hashCode;
#endif
}
public override bool Equals(object? obj)
=> Equals(obj as PackageIdentity);
public bool Equals(PackageIdentity? other)
{
return other is not null
&& Id == other.Id
&& Version == other.Version;
}
internal NuGet.Packaging.Core.PackageIdentity ToNuGetIdentity()
{
return new NuGet.Packaging.Core.PackageIdentity(Id, NuGetVersion.Parse(Version));

Просмотреть файл

@ -144,6 +144,7 @@ Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.Sources.get -> System
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.WithAdditionalDiagnostics(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic> additionalDiagnostics) -> Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState
Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState.WithSources(System.Collections.Immutable.ImmutableArray<(string filename, Microsoft.CodeAnalysis.Text.SourceText content)> sources) -> Microsoft.CodeAnalysis.Testing.Model.EvaluatedProjectState
Microsoft.CodeAnalysis.Testing.PackageIdentity
Microsoft.CodeAnalysis.Testing.PackageIdentity.Equals(Microsoft.CodeAnalysis.Testing.PackageIdentity other) -> bool
Microsoft.CodeAnalysis.Testing.PackageIdentity.Id.get -> string
Microsoft.CodeAnalysis.Testing.PackageIdentity.PackageIdentity(string id, string version) -> void
Microsoft.CodeAnalysis.Testing.PackageIdentity.Version.get -> string
@ -176,6 +177,7 @@ Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.AddLanguageSpecificAssemblies
Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.AddPackages(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Testing.PackageIdentity> packages) -> Microsoft.CodeAnalysis.Testing.ReferenceAssemblies
Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.Assemblies.get -> System.Collections.Immutable.ImmutableArray<string>
Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.AssemblyIdentityComparer.get -> Microsoft.CodeAnalysis.AssemblyIdentityComparer
Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.Equals(Microsoft.CodeAnalysis.Testing.ReferenceAssemblies other) -> bool
Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.FacadeAssemblies.get -> System.Collections.Immutable.ImmutableArray<string>
Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.LanguageSpecificAssemblies.get -> System.Collections.Immutable.ImmutableDictionary<string, System.Collections.Immutable.ImmutableArray<string>>
Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.Net
@ -246,6 +248,10 @@ abstract Microsoft.CodeAnalysis.Testing.CodeActionTest<TVerifier>.SyntaxKindType
override Microsoft.CodeAnalysis.Testing.DiagnosticResult.ToString() -> string
override Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext context) -> void
override Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.DiagnosticDescriptor>
override Microsoft.CodeAnalysis.Testing.PackageIdentity.Equals(object obj) -> bool
override Microsoft.CodeAnalysis.Testing.PackageIdentity.GetHashCode() -> int
override Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.Equals(object obj) -> bool
override Microsoft.CodeAnalysis.Testing.ReferenceAssemblies.GetHashCode() -> int
static Microsoft.CodeAnalysis.Testing.AnalyzerTest<TVerifier>.Verify.get -> TVerifier
static Microsoft.CodeAnalysis.Testing.AnalyzerVerifier<TAnalyzer, TTest, TVerifier>.Diagnostic() -> Microsoft.CodeAnalysis.Testing.DiagnosticResult
static Microsoft.CodeAnalysis.Testing.AnalyzerVerifier<TAnalyzer, TTest, TVerifier>.Diagnostic(Microsoft.CodeAnalysis.DiagnosticDescriptor descriptor) -> Microsoft.CodeAnalysis.Testing.DiagnosticResult

Просмотреть файл

@ -25,7 +25,7 @@ using NuGet.Packaging.Signing;
namespace Microsoft.CodeAnalysis.Testing
{
public sealed partial class ReferenceAssemblies
public sealed partial class ReferenceAssemblies : IEquatable<ReferenceAssemblies?>
{
private const string ReferenceAssembliesPackageVersion = "1.0.2";
@ -37,6 +37,8 @@ namespace Microsoft.CodeAnalysis.Testing
private static ImmutableHashSet<NuGet.Packaging.Core.PackageIdentity> s_emptyPackages
= ImmutableHashSet.Create<NuGet.Packaging.Core.PackageIdentity>(PackageIdentityComparer.Default);
private static ImmutableHashSet<ReferenceAssemblies> s_knownAssemblies = ImmutableHashSet<ReferenceAssemblies>.Empty;
private readonly Dictionary<string, ImmutableArray<MetadataReference>> _references
= new();
@ -123,14 +125,83 @@ namespace Microsoft.CodeAnalysis.Testing
public string? NuGetConfigFilePath { get; }
private static ReferenceAssemblies GetOrAddReferenceAssemblies(ReferenceAssemblies value)
{
if (s_knownAssemblies.TryGetValue(value, out var existingValue))
{
return existingValue;
}
if (ImmutableInterlocked.Update(
ref s_knownAssemblies,
static (knownAssemblies, value) => knownAssemblies.Add(value),
value))
{
return value;
}
if (!s_knownAssemblies.TryGetValue(value, out existingValue))
{
throw new InvalidOperationException();
}
return existingValue;
}
public override int GetHashCode()
{
#if NETCOREAPP
var hash = default(HashCode);
hash.Add(TargetFramework);
hash.Add(AssemblyIdentityComparer);
hash.Add(ReferenceAssemblyPackage);
hash.Add(ReferenceAssemblyPath);
hash.Add(Assemblies, ImmutableArrayEqualityComparer<string>.Instance);
hash.Add(FacadeAssemblies, ImmutableArrayEqualityComparer<string>.Instance);
hash.Add(LanguageSpecificAssemblies, ImmutableDictionaryWithImmutableArrayValuesEqualityComparer<string, string>.Instance);
hash.Add(Packages, ImmutableArrayEqualityComparer<PackageIdentity>.Instance);
hash.Add(NuGetConfigFilePath);
return hash.ToHashCode();
#else
var hashCode = -450793227;
hashCode = (hashCode * -1521134295) + EqualityComparer<string>.Default.GetHashCode(TargetFramework);
hashCode = (hashCode * -1521134295) + EqualityComparer<AssemblyIdentityComparer>.Default.GetHashCode(AssemblyIdentityComparer);
hashCode = (hashCode * -1521134295) + EqualityComparer<PackageIdentity?>.Default.GetHashCode(ReferenceAssemblyPackage);
hashCode = (hashCode * -1521134295) + EqualityComparer<string?>.Default.GetHashCode(ReferenceAssemblyPath);
hashCode = (hashCode * -1521134295) + ImmutableArrayEqualityComparer<string>.Instance.GetHashCode(Assemblies);
hashCode = (hashCode * -1521134295) + ImmutableArrayEqualityComparer<string>.Instance.GetHashCode(FacadeAssemblies);
hashCode = (hashCode * -1521134295) + ImmutableDictionaryWithImmutableArrayValuesEqualityComparer<string, string>.Instance.GetHashCode(LanguageSpecificAssemblies);
hashCode = (hashCode * -1521134295) + ImmutableArrayEqualityComparer<PackageIdentity>.Instance.GetHashCode(Packages);
hashCode = (hashCode * -1521134295) + EqualityComparer<string?>.Default.GetHashCode(NuGetConfigFilePath);
return hashCode;
#endif
}
public override bool Equals(object? obj)
=> Equals(obj as ReferenceAssemblies);
public bool Equals(ReferenceAssemblies? other)
{
return other is not null
&& TargetFramework == other.TargetFramework
&& EqualityComparer<AssemblyIdentityComparer>.Default.Equals(AssemblyIdentityComparer, other.AssemblyIdentityComparer)
&& EqualityComparer<PackageIdentity?>.Default.Equals(ReferenceAssemblyPackage, other.ReferenceAssemblyPackage)
&& ReferenceAssemblyPath == other.ReferenceAssemblyPath
&& ImmutableArrayEqualityComparer<string>.Instance.Equals(Assemblies, other.Assemblies)
&& ImmutableArrayEqualityComparer<string>.Instance.Equals(FacadeAssemblies, other.FacadeAssemblies)
&& ImmutableDictionaryWithImmutableArrayValuesEqualityComparer<string, string>.Instance.Equals(LanguageSpecificAssemblies, other.LanguageSpecificAssemblies)
&& ImmutableArrayEqualityComparer<PackageIdentity>.Instance.Equals(Packages, other.Packages)
&& NuGetConfigFilePath == other.NuGetConfigFilePath;
}
public ReferenceAssemblies WithAssemblyIdentityComparer(AssemblyIdentityComparer assemblyIdentityComparer)
=> new(TargetFramework, assemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, FacadeAssemblies, LanguageSpecificAssemblies, Packages, NuGetConfigFilePath);
=> GetOrAddReferenceAssemblies(new(TargetFramework, assemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, FacadeAssemblies, LanguageSpecificAssemblies, Packages, NuGetConfigFilePath));
public ReferenceAssemblies WithAssemblies(ImmutableArray<string> assemblies)
=> new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, assemblies, FacadeAssemblies, LanguageSpecificAssemblies, Packages, NuGetConfigFilePath);
=> GetOrAddReferenceAssemblies(new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, assemblies, FacadeAssemblies, LanguageSpecificAssemblies, Packages, NuGetConfigFilePath));
public ReferenceAssemblies WithFacadeAssemblies(ImmutableArray<string> facadeAssemblies)
=> new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, facadeAssemblies, LanguageSpecificAssemblies, Packages, NuGetConfigFilePath);
=> GetOrAddReferenceAssemblies(new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, facadeAssemblies, LanguageSpecificAssemblies, Packages, NuGetConfigFilePath));
public ReferenceAssemblies AddAssemblies(ImmutableArray<string> assemblies)
=> WithAssemblies(Assemblies.AddRange(assemblies));
@ -139,7 +210,7 @@ namespace Microsoft.CodeAnalysis.Testing
=> WithFacadeAssemblies(FacadeAssemblies.AddRange(facadeAssemblies));
public ReferenceAssemblies WithLanguageSpecificAssemblies(ImmutableDictionary<string, ImmutableArray<string>> languageSpecificAssemblies)
=> new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, FacadeAssemblies, languageSpecificAssemblies, Packages, NuGetConfigFilePath);
=> GetOrAddReferenceAssemblies(new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, FacadeAssemblies, languageSpecificAssemblies, Packages, NuGetConfigFilePath));
public ReferenceAssemblies WithLanguageSpecificAssemblies(string language, ImmutableArray<string> assemblies)
=> WithLanguageSpecificAssemblies(LanguageSpecificAssemblies.SetItem(language, assemblies));
@ -155,13 +226,13 @@ namespace Microsoft.CodeAnalysis.Testing
}
public ReferenceAssemblies WithPackages(ImmutableArray<PackageIdentity> packages)
=> new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, FacadeAssemblies, LanguageSpecificAssemblies, packages, NuGetConfigFilePath);
=> GetOrAddReferenceAssemblies(new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, FacadeAssemblies, LanguageSpecificAssemblies, packages, NuGetConfigFilePath));
public ReferenceAssemblies AddPackages(ImmutableArray<PackageIdentity> packages)
=> WithPackages(Packages.AddRange(packages));
public ReferenceAssemblies WithNuGetConfigFilePath(string nugetConfigFilePath)
=> new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, FacadeAssemblies, LanguageSpecificAssemblies, Packages, nugetConfigFilePath);
=> GetOrAddReferenceAssemblies(new(TargetFramework, AssemblyIdentityComparer, ReferenceAssemblyPackage, ReferenceAssemblyPath, Assemblies, FacadeAssemblies, LanguageSpecificAssemblies, Packages, nugetConfigFilePath));
public async Task<ImmutableArray<MetadataReference>> ResolveAsync(string? language, CancellationToken cancellationToken)
{
@ -1353,5 +1424,140 @@ namespace Microsoft.CodeAnalysis.Testing
return framework.IsPackageBased;
}
}
private sealed class ImmutableArrayEqualityComparer<T> : IEqualityComparer<ImmutableArray<T>>
{
public static readonly ImmutableArrayEqualityComparer<T> Instance = new();
private ImmutableArrayEqualityComparer()
{
}
public bool Equals(ImmutableArray<T> x, ImmutableArray<T> y)
{
if (x.IsDefault)
{
return y.IsDefault;
}
else if (y.IsDefault)
{
return false;
}
if (x.Length != y.Length)
{
return false;
}
for (var i = 0; i < x.Length; i++)
{
if (!EqualityComparer<T>.Default.Equals(x[i], y[i]))
{
return false;
}
}
return true;
}
public int GetHashCode(ImmutableArray<T> obj)
{
if (obj.IsDefault)
{
return 0;
}
#if NETCOREAPP
var hash = default(HashCode);
foreach (var item in obj)
{
hash.Add(item);
}
return hash.ToHashCode();
#else
var hashCode = -450793227;
foreach (var item in obj)
{
hashCode = (hashCode * -1521134295) + EqualityComparer<T>.Default.GetHashCode(item);
}
return hashCode;
#endif
}
}
private sealed class ImmutableDictionaryWithImmutableArrayValuesEqualityComparer<TKey, TValue> : IEqualityComparer<ImmutableDictionary<TKey, ImmutableArray<TValue>>?>
{
public static readonly ImmutableDictionaryWithImmutableArrayValuesEqualityComparer<TKey, TValue> Instance = new();
private ImmutableDictionaryWithImmutableArrayValuesEqualityComparer()
{
}
public bool Equals(ImmutableDictionary<TKey, ImmutableArray<TValue>>? x, ImmutableDictionary<TKey, ImmutableArray<TValue>>? y)
{
if (x is null)
{
return y is null;
}
else if (y is null)
{
return false;
}
if (x.Count != y.Count)
{
return false;
}
foreach (var (key, valueX) in x)
{
// Use a separate lookup in 'y' since ImmutableDictionary<,> can reorder pairs where the key has the
// same hash code.
if (!y.TryGetValue(key, out var valueY))
{
return false;
}
if (!ImmutableArrayEqualityComparer<TValue>.Instance.Equals(valueX, valueY))
{
return false;
}
}
return true;
}
public int GetHashCode(ImmutableDictionary<TKey, ImmutableArray<TValue>>? obj)
{
if (obj is null)
{
return 0;
}
#if NETCOREAPP
var hash = default(HashCode);
foreach (var (key, _) in obj)
{
// Intentionally ignore values since ImmutableDictionary<,> can reorder pairs where the key has the
// same hash code.
hash.Add(key);
}
return hash.ToHashCode();
#else
var hashCode = -450793227;
foreach (var (key, _) in obj)
{
// Intentionally ignore values since ImmutableDictionary<,> can reorder pairs where the key has the
// same hash code.
hashCode = (hashCode * -1521134295) + EqualityComparer<TKey>.Default.GetHashCode(key);
}
return hashCode;
#endif
}
}
}
}