.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

141 lines
5.5 KiB

// 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<string, UnresolvedAssemblyNameReference> loadedAssemblyReferences = new Dictionary<string, UnresolvedAssemblyNameReference>();
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<UnresolvedAssemblyNameReference> Entries {
get {
lock (loadedAssemblyReferences) {
return loadedAssemblyReferences.Values.ToList();
}
}
}
public bool HasErrors {
get {
lock (loadedAssemblyReferences) {
return loadedAssemblyReferences.Any(i => i.Value.HasErrors);
}
}
}
}
}