/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Shared/GlobalAssemblyCacheHelpers/MonoGlobalAssemblyCache.cs
230 строк
9 KB
Tomáš Matoušek
Move SpecializedCollections to Microsoft.CodeAnalysis.Collections namespace (#78466)
15 май 2025, 00:23
Не верифицирован
15 май 2025, 00:23
4358d12
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using Microsoft.CodeAnalysis.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis { /// <summary> /// Provides APIs to enumerate and look up assemblies stored in the Global Assembly Cache. /// </summary> internal sealed class MonoGlobalAssemblyCache : GlobalAssemblyCache { private static readonly string s_corlibDirectory; private static readonly string s_gacDirectory; static MonoGlobalAssemblyCache() { var corlibAssemblyFile = typeof(object).Assembly.Location; s_corlibDirectory = Path.GetDirectoryName(corlibAssemblyFile); var systemAssemblyFile = typeof(Uri).Assembly.Location; s_gacDirectory = Directory.GetParent(Path.GetDirectoryName(systemAssemblyFile)).Parent.FullName; } private static AssemblyName CreateAssemblyNameFromFile(string path) => AssemblyName.GetAssemblyName(path); private static IEnumerable<string> GetGacAssemblyPaths(string gacPath, string name, Version version, byte[] publicKeyTokenBytes) { var fileName = name + ".dll"; // First check to see if the assembly lives alongside mscorlib.dll. var corlibFriendPath = Path.Combine(s_corlibDirectory, fileName); if (!File.Exists(corlibFriendPath)) { // If not, check the Facades directory (e.g. this is where netstandard.dll will live) corlibFriendPath = Path.Combine(s_corlibDirectory, "Facades", fileName); } // Yield and bail early if we find anything - it'll either be a Facade assembly or a // symlink into the GAC so we can avoid the more exhaustive work below. if (File.Exists(corlibFriendPath)) { yield return corlibFriendPath; yield break; } var publicKeyToken = ToHexString(publicKeyTokenBytes); // Another bail fast attempt to peek directly into the GAC if we have version and public key if (version != null && publicKeyToken != null) { yield return Path.Combine(gacPath, name, version + "__" + publicKeyToken, fileName); yield break; } // Otherwise we need to iterate the GAC in the file system to find a match var gacAssemblyRootDir = new DirectoryInfo(Path.Combine(gacPath, name)); if (!gacAssemblyRootDir.Exists) { yield break; } foreach (var assemblyDir in gacAssemblyRootDir.GetDirectories()) { if (version != null && !assemblyDir.Name.StartsWith(version.ToString(), StringComparison.Ordinal)) { continue; } if (publicKeyToken != null && !assemblyDir.Name.EndsWith(publicKeyToken, StringComparison.Ordinal)) { continue; } var assemblyPath = Path.Combine(assemblyDir.ToString(), fileName); if (File.Exists(assemblyPath)) { yield return assemblyPath; } } } private static IEnumerable<(AssemblyIdentity Identity, string Path)> GetAssemblyIdentitiesAndPaths(AssemblyName name, ImmutableArray<ProcessorArchitecture> architectureFilter) { if (name == null) { return GetAssemblyIdentitiesAndPaths(null, null, null, architectureFilter); } return GetAssemblyIdentitiesAndPaths(name.Name, name.Version, name.GetPublicKeyToken(), architectureFilter); } private static IEnumerable<(AssemblyIdentity Identity, string Path)> GetAssemblyIdentitiesAndPaths(string name, Version version, byte[] publicKeyToken, ImmutableArray<ProcessorArchitecture> architectureFilter) { var assemblyPaths = GetGacAssemblyPaths(s_gacDirectory, name, version, publicKeyToken); foreach (var assemblyPath in assemblyPaths) { if (!File.Exists(assemblyPath)) { continue; } var gacAssemblyName = CreateAssemblyNameFromFile(assemblyPath); #pragma warning disable SYSLIB0037 // warning SYSLIB0037: 'AssemblyName.ProcessorArchitecture' is obsolete: 'AssemblyName members HashAlgorithm, ProcessorArchitecture, and VersionCompatibility are obsolete and not supported.' if (gacAssemblyName.ProcessorArchitecture != ProcessorArchitecture.None && architectureFilter != default(ImmutableArray<ProcessorArchitecture>) && architectureFilter.Length > 0 && !architectureFilter.Contains(gacAssemblyName.ProcessorArchitecture)) { continue; } #pragma warning restore SYSLIB0037 var assemblyIdentity = new AssemblyIdentity( gacAssemblyName.Name, gacAssemblyName.Version, gacAssemblyName.CultureName, ImmutableArray.Create(gacAssemblyName.GetPublicKeyToken())); yield return (assemblyIdentity, assemblyPath); } } public override IEnumerable<AssemblyIdentity> GetAssemblyIdentities(AssemblyName partialName, ImmutableArray<ProcessorArchitecture> architectureFilter = default(ImmutableArray<ProcessorArchitecture>)) { return GetAssemblyIdentitiesAndPaths(partialName, architectureFilter).Select(identityAndPath => identityAndPath.Item1); } public override IEnumerable<AssemblyIdentity> GetAssemblyIdentities(string partialName = null, ImmutableArray<ProcessorArchitecture> architectureFilter = default(ImmutableArray<ProcessorArchitecture>)) { AssemblyName name; try { name = (partialName == null) ? null : new AssemblyName(partialName); } catch { return SpecializedCollections.EmptyEnumerable<AssemblyIdentity>(); } return GetAssemblyIdentities(name, architectureFilter); } public override IEnumerable<string> GetAssemblySimpleNames(ImmutableArray<ProcessorArchitecture> architectureFilter = default(ImmutableArray<ProcessorArchitecture>)) { return GetAssemblyIdentitiesAndPaths(name: null, version: null, publicKeyToken: null, architectureFilter: architectureFilter). Select(identityAndPath => identityAndPath.Identity.Name).Distinct(); } public override AssemblyIdentity ResolvePartialName( string displayName, out string location, ImmutableArray<ProcessorArchitecture> architectureFilter, CultureInfo preferredCulture) { if (displayName == null) { throw new ArgumentNullException(nameof(displayName)); } string cultureName = (preferredCulture != null && !preferredCulture.IsNeutralCulture) ? preferredCulture.Name : null; var assemblyName = new AssemblyName(displayName); AssemblyIdentity assemblyIdentity = null; location = null; bool isBestMatch = false; foreach (var identityAndPath in GetAssemblyIdentitiesAndPaths(assemblyName, architectureFilter)) { var assemblyPath = identityAndPath.Path; if (!File.Exists(assemblyPath)) { continue; } var gacAssemblyName = CreateAssemblyNameFromFile(assemblyPath); isBestMatch = cultureName == null || gacAssemblyName.CultureName == cultureName; bool isBetterMatch = location == null || isBestMatch; if (isBetterMatch) { location = assemblyPath; assemblyIdentity = identityAndPath.Identity; } if (isBestMatch) { break; } } return assemblyIdentity; } private static string ToHexString(byte[] bytes) { if (bytes == null) { return null; } var sb = PooledObjects.PooledStringBuilder.GetInstance(); foreach (var b in bytes) { sb.Builder.Append(b.ToString("x2")); } return sb.ToStringAndFree(); } } }