// Copyright (c) 2018 Siegfried Pammer // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software // without restriction, including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons // to whom the Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all copies or // substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using ICSharpCode.Decompiler.TypeSystem.Implementation; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; namespace ICSharpCode.Decompiler.Metadata { public static class DotNetCorePathFinderExtensions { static readonly string RefPathPattern = @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" + @"|(NuGetFallbackFolder[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" + @"|(shared[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\])"; public static string DetectTargetFrameworkId(this PEFile assembly) { return DetectTargetFrameworkId(assembly.Reader, assembly.FileName); } public static string DetectTargetFrameworkId(this PEReader assembly, string assemblyPath = null) { if (assembly == null) throw new ArgumentNullException(nameof(assembly)); const string TargetFrameworkAttributeName = "System.Runtime.Versioning.TargetFrameworkAttribute"; var reader = assembly.GetMetadataReader(); foreach (var h in reader.GetCustomAttributes(Handle.AssemblyDefinition)) { var attribute = reader.GetCustomAttribute(h); if (attribute.GetAttributeType(reader).GetFullTypeName(reader).ToString() != TargetFrameworkAttributeName) continue; var blobReader = reader.GetBlobReader(attribute.Value); if (blobReader.ReadUInt16() == 0x0001) { return blobReader.ReadSerializedString(); } } // Optionally try to detect target version through assembly path as a fallback (use case: reference assemblies) if (assemblyPath != null) { /* * Detected path patterns (examples): * * - .NETFramework -> C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\mscorlib.dll * - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll * - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll */ var pathMatch = Regex.Match(assemblyPath, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture); if (pathMatch.Success) { var type = pathMatch.Groups[1].Value; var version = pathMatch.Groups[2].Value; if (type == ".NETFramework") { return $".NETFramework,Version=v{version}"; } else if (type.IndexOf("netcore", StringComparison.OrdinalIgnoreCase) >= 0) { return $".NETCoreApp,Version=v{version}"; } else if (type.IndexOf("netstandard", StringComparison.OrdinalIgnoreCase) >= 0) { return $".NETStandard,Version=v{version}"; } } } return string.Empty; } } public class ReferenceLoadInfo { readonly Dictionary loadedAssemblyReferences = new Dictionary(); public void AddMessage(string fullName, MessageKind kind, string message) { lock (loadedAssemblyReferences) { if (!loadedAssemblyReferences.TryGetValue(fullName, out var referenceInfo)) { referenceInfo = new UnresolvedAssemblyNameReference(fullName); loadedAssemblyReferences.Add(fullName, referenceInfo); } referenceInfo.Messages.Add((kind, message)); } } public void AddMessageOnce(string fullName, MessageKind kind, string message) { lock (loadedAssemblyReferences) { if (!loadedAssemblyReferences.TryGetValue(fullName, out var referenceInfo)) { referenceInfo = new UnresolvedAssemblyNameReference(fullName); loadedAssemblyReferences.Add(fullName, referenceInfo); referenceInfo.Messages.Add((kind, message)); } else { var lastMsg = referenceInfo.Messages.LastOrDefault(); if (kind != lastMsg.Item1 && message != lastMsg.Item2) referenceInfo.Messages.Add((kind, message)); } } } public bool TryGetInfo(string fullName, out UnresolvedAssemblyNameReference info) { lock (loadedAssemblyReferences) { return loadedAssemblyReferences.TryGetValue(fullName, out info); } } public IReadOnlyList Entries { get { lock (loadedAssemblyReferences) { return loadedAssemblyReferences.Values.ToList(); } } } public bool HasErrors { get { lock (loadedAssemblyReferences) { return loadedAssemblyReferences.Any(i => i.Value.HasErrors); } } } } }