From 2fb6e143394839ae0f858968eb29ff3b5fb975bc Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Tue, 12 Jun 2018 21:31:17 +0200 Subject: [PATCH] Fixed #1164: AddIn opens reference assembly when trying to open symbol in ILSpy --- .../DotNetCore/DotNetCorePathFinder.cs | 3 +- .../DotNetCorePathFinderExtensions.cs | 32 +++++++++- ILSpy.AddIn/AssemblyFileFinder.cs | 61 +++++++++++++++++++ ILSpy.AddIn/Commands/OpenILSpyCommand.cs | 9 +-- ILSpy.AddIn/ILSpy.AddIn.csproj | 1 + 5 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 ILSpy.AddIn/AssemblyFileFinder.cs diff --git a/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinder.cs b/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinder.cs index 8ff093c1d..ebf22b145 100644 --- a/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinder.cs +++ b/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinder.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; +using ICSharpCode.Decompiler.Util; using LightJson.Serialization; using Mono.Cecil; @@ -27,7 +28,7 @@ namespace ICSharpCode.Decompiler this.Version = parts[1]; this.Type = type; this.Path = path; - this.RuntimeComponents = runtimeComponents ?? new string[0]; + this.RuntimeComponents = runtimeComponents ?? Empty.Array; } } diff --git a/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs b/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs index 7fb8f2561..7a1545792 100644 --- a/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs +++ b/ICSharpCode.Decompiler/DotNetCore/DotNetCorePathFinderExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using ICSharpCode.Decompiler.TypeSystem.Implementation; using Mono.Cecil; @@ -8,7 +9,11 @@ namespace ICSharpCode.Decompiler { public static class DotNetCorePathFinderExtensions { - public static string DetectTargetFrameworkId(this AssemblyDefinition assembly) + static readonly string RefPathPattern = + @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" + + @"|(NuGetFallbackFolder[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])"; + + public static string DetectTargetFrameworkId(this AssemblyDefinition assembly, string assemblyPath = null) { if (assembly == null) throw new ArgumentNullException(nameof(assembly)); @@ -24,6 +29,31 @@ namespace ICSharpCode.Decompiler } } + // 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.Contains("netcore")) { + return $".NETCoreApp,Version=v{version}"; + } else if (type.Contains("netstandard")) { + return $".NETStandard,Version=v{version}"; + } + } + } + return string.Empty; } } diff --git a/ILSpy.AddIn/AssemblyFileFinder.cs b/ILSpy.AddIn/AssemblyFileFinder.cs new file mode 100644 index 000000000..9a5f003a9 --- /dev/null +++ b/ILSpy.AddIn/AssemblyFileFinder.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Util; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.AddIn +{ + public class AssemblyFileFinder + { + public static string FindAssemblyFile(AssemblyDefinition assemblyDefinition, string assemblyFile) + { + var assemblyName = assemblyDefinition.Name; + + var detectedTargetFramework = assemblyDefinition.DetectTargetFrameworkId(assemblyFile); + if (string.IsNullOrEmpty(detectedTargetFramework)) { + // Without a target framework id it makes no sense to continue + return null; + } + + var targetFramework = detectedTargetFramework.Split(new[] { ",Version=v" }, StringSplitOptions.None); + string file = null; + switch (targetFramework[0]) { + case ".NETCoreApp": + case ".NETStandard": + if (targetFramework.Length != 2) + return FindAssemblyFromGAC(assemblyDefinition); + var version = targetFramework[1].Length == 3 ? targetFramework[1] + ".0" : targetFramework[1]; + var dotNetCorePathFinder = new DotNetCorePathFinder(assemblyFile, detectedTargetFramework, version); + file = dotNetCorePathFinder.TryResolveDotNetCore(assemblyName); + if (file != null) + return file; + return FindAssemblyFromGAC(assemblyDefinition); + default: + return FindAssemblyFromGAC(assemblyDefinition); + } + } + + static string FindAssemblyFromGAC(AssemblyDefinition assemblyDefinition) + { + return GacInterop.FindAssemblyInNetGac(assemblyDefinition.Name); + } + + static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]"; + + public static bool IsReferenceAssembly(AssemblyDefinition assemblyDef, string assemblyFile) + { + if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute")) + return true; + + // Try to detect reference assembly through specific path pattern + var refPathMatch = Regex.Match(assemblyFile, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + return refPathMatch.Success; + } + + } +} diff --git a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs index 9bbd1ae6f..8e8e48193 100644 --- a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs +++ b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs @@ -89,8 +89,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands foreach (var reference in parentProject.MetadataReferences) { using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) { string assemblyName = assemblyDef.Name.Name; - if (IsReferenceAssembly(assemblyDef)) { - string resolvedAssemblyFile = GacInterop.FindAssemblyInNetGac(assemblyDef.Name); + if (AssemblyFileFinder.IsReferenceAssembly(assemblyDef, reference.Display)) { + string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display); dict.Add(assemblyName, new DetectedReference(assemblyName, resolvedAssemblyFile, false)); } else { @@ -127,11 +127,6 @@ namespace ICSharpCode.ILSpy.AddIn.Commands return null; } - - protected bool IsReferenceAssembly(AssemblyDefinition assemblyDef) - { - return assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"); - } } class OpenILSpyCommand : ILSpyCommand diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index 26fdc6f75..320a1d5e1 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -85,6 +85,7 @@ +