Skip to content

Commit

Permalink
Fix #2076: VS AddIn opens reference assembly.
Browse files Browse the repository at this point in the history
  • Loading branch information
siegfriedpammer committed Jul 27, 2020
1 parent 8d72672 commit 4b865c2
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 41 deletions.
2 changes: 1 addition & 1 deletion ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ private static string ReplacePrivImplDetails(string il)
return Regex.Replace(il, @"'<PrivateImplementationDetails>\{[0-9A-F-]+\}'", "'<PrivateImplementationDetails>'");
}

static readonly string coreRefAsmPath = new DotNetCorePathFinder(new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1");
static readonly string coreRefAsmPath = new DotNetCorePathFinder(TargetFrameworkIdentifier.NETCoreApp, new Version(3, 1)).GetReferenceAssemblyPath(".NETCoreApp, Version = v3.1");

static readonly string refAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
@"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2");
Expand Down
16 changes: 8 additions & 8 deletions ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,23 @@ public DotNetCorePackageInfo(string fullName, string type, string path, string[]
readonly Version targetFrameworkVersion;
readonly string dotnetBasePath = FindDotNetExeDirectory();

public DotNetCorePathFinder(Version targetFrameworkVersion)
public DotNetCorePathFinder(TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion)
{
this.targetFrameworkVersion = targetFrameworkVersion;
}

public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null)
{
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
this.targetFrameworkVersion = targetFrameworkVersion;

if (targetFramework == TargetFrameworkIdentifier.NETStandard) {
// .NET Standard 2.1 is implemented by .NET Core 3.0 or higher
if (targetFrameworkVersion.Major == 2 && targetFrameworkVersion.Minor == 1) {
this.targetFrameworkVersion = new Version(3, 0, 0);
}
}
}

public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null)
: this(targetFramework, targetFrameworkVersion)
{
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName);

searchPaths.Add(basePath);

Expand Down
33 changes: 29 additions & 4 deletions ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,26 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.TypeSystem;

namespace ICSharpCode.Decompiler.Metadata
{
public static class DotNetCorePathFinderExtensions
{
static readonly string RefPathPattern =
static readonly string PathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" +
@"|((?<type>Microsoft\.NET)[/\\]Framework(64)?[/\\](?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])";
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])" +
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";

static readonly string RefPathPattern =
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";

public static string DetectTargetFrameworkId(this PEFile assembly)
{
Expand Down Expand Up @@ -99,7 +105,7 @@ public static string DetectTargetFrameworkId(this PEReader assembly, string asse
* - .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,
var pathMatch = Regex.Match(assemblyPath, PathPattern,
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
if (pathMatch.Success) {
var type = pathMatch.Groups["type"].Value;
Expand All @@ -121,6 +127,25 @@ public static string DetectTargetFrameworkId(this PEReader assembly, string asse

return string.Empty;
}

public static bool IsReferenceAssembly(this PEFile assembly)
{
return IsReferenceAssembly(assembly.Reader, assembly.FileName);
}

public static bool IsReferenceAssembly(this PEReader assembly, string assemblyPath)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));

var metadata = assembly.GetMetadataReader();
if (metadata.GetCustomAttributes(Handle.AssemblyDefinition).HasKnownAttribute(metadata, KnownAttribute.ReferenceAssembly))
return true;

// Try to detect reference assembly through specific path pattern
var refPathMatch = Regex.Match(assemblyPath, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
return refPathMatch.Success;
}
}

public class ReferenceLoadInfo
Expand Down
20 changes: 13 additions & 7 deletions ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,19 @@ public string[] GetSearchDirectories()
public UniversalAssemblyResolver(string mainAssemblyFileName, bool throwOnError, string targetFramework,
PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default)
{
this.mainAssemblyFileName = mainAssemblyFileName;
this.throwOnError = throwOnError;
this.streamOptions = streamOptions;
this.metadataOptions = metadataOptions;
this.targetFramework = targetFramework ?? string.Empty;
(targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework);
this.mainAssemblyFileName = mainAssemblyFileName;
this.baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
this.throwOnError = throwOnError;
if (string.IsNullOrWhiteSpace(this.baseDirectory))
this.baseDirectory = Environment.CurrentDirectory;
AddSearchDirectory(baseDirectory);

if (mainAssemblyFileName != null) {
string baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
if (string.IsNullOrWhiteSpace(this.baseDirectory))
this.baseDirectory = Environment.CurrentDirectory;
AddSearchDirectory(baseDirectory);
}
}

internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework)
Expand Down Expand Up @@ -191,7 +194,10 @@ public string FindAssemblyFile(IAssemblyReference name)
if (IsZeroOrAllOnes(targetFrameworkVersion))
goto default;
if (dotNetCorePathFinder == null) {
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
if (mainAssemblyFileName == null)
dotNetCorePathFinder = new DotNetCorePathFinder(targetFrameworkIdentifier, targetFrameworkVersion);
else
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
foreach (var directory in directories) {
dotNetCorePathFinder.AddSearchDirectory(directory);
}
Expand Down
23 changes: 10 additions & 13 deletions ILSpy.AddIn/AssemblyFileFinder.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
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.Metadata;
using ICSharpCode.Decompiler.Util;
using Mono.Cecil;

namespace ICSharpCode.ILSpy.AddIn
{
public class AssemblyFileFinder
{
public static string FindAssemblyFile(AssemblyDefinition assemblyDefinition, string assemblyFile)
public static string FindAssemblyFile(Mono.Cecil.AssemblyDefinition assemblyDefinition, string assemblyFile)
{
var assemblyResolver = new UniversalAssemblyResolver(assemblyFile, false,
DetectTargetFrameworkId(assemblyDefinition, assemblyFile));
string tfi = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
UniversalAssemblyResolver assemblyResolver;
if (IsReferenceAssembly(assemblyDefinition, assemblyFile)) {
assemblyResolver.RemoveSearchDirectory(Path.GetDirectoryName(assemblyFile));
assemblyResolver = new UniversalAssemblyResolver(null, throwOnError: false, tfi);
} else {
assemblyResolver = new UniversalAssemblyResolver(assemblyFile, throwOnError: false, tfi);
}
return assemblyResolver.FindAssemblyFile(
ICSharpCode.Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));

return assemblyResolver.FindAssemblyFile(AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
}

static readonly string RefPathPattern = @"NuGetFallbackFolder[/\\][^/\\]+[/\\][^/\\]+[/\\]ref[/\\]";

public static bool IsReferenceAssembly(AssemblyDefinition assemblyDef, string assemblyFile)
public static bool IsReferenceAssembly(Mono.Cecil.AssemblyDefinition assemblyDef, string assemblyFile)
{
if (assemblyDef.CustomAttributes.Any(ca => ca.AttributeType.FullName == "System.Runtime.CompilerServices.ReferenceAssemblyAttribute"))
return true;
Expand All @@ -40,7 +37,7 @@ public static bool IsReferenceAssembly(AssemblyDefinition assemblyDef, string as
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<1>.NETFramework)[/\\]v(?<2>[^/\\]+)[/\\])" +
@"|((NuGetFallbackFolder|packs)[/\\](?<1>[^/\\]+)\\(?<2>[^/\\]+)([/\\].*)?[/\\]ref[/\\])";

public static string DetectTargetFrameworkId(AssemblyDefinition assembly, string assemblyPath = null)
public static string DetectTargetFrameworkId(Mono.Cecil.AssemblyDefinition assembly, string assemblyPath = null)
{
if (assembly == null)
throw new ArgumentNullException(nameof(assembly));
Expand Down
3 changes: 1 addition & 2 deletions ILSpy.AddIn/Commands/OpenILSpyCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ protected Dictionary<string, DetectedReference> GetReferences(Microsoft.CodeAnal
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
string assemblyName = assemblyDef.Name.Name;
string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display);
dict.Add(assemblyName,
new DetectedReference(assemblyName, resolvedAssemblyFile, false));
dict.Add(assemblyName, new DetectedReference(assemblyName, resolvedAssemblyFile, false));
}
}
foreach (var projectReference in parentProject.ProjectReferences) {
Expand Down
4 changes: 2 additions & 2 deletions ILSpy.AddIn/ILSpyInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ class ILSpyParameters
{
public ILSpyParameters(IEnumerable<string> assemblyFileNames, params string[] arguments)
{
this.AssemblyFileNames = assemblyFileNames;
this.AssemblyFileNames = assemblyFileNames.ToArray();
this.Arguments = arguments;
}

public IEnumerable<string> AssemblyFileNames { get; private set; }
public string[] AssemblyFileNames { get; private set; }
public string[] Arguments { get; private set; }
}

Expand Down
4 changes: 1 addition & 3 deletions ILSpy/ILSpy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,7 @@
<Compile Include="Commands\ShowDebugSteps.cs" />
<Compile Include="Commands\SortAssemblyListCommand.cs" />
<Compile Include="Controls\BoolToVisibilityConverter.cs" />
<Compile Include="Controls\CustomDialog.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Controls\CustomDialog.cs" />
<Compile Include="Controls\GridViewColumnAutoSize.cs" />
<Compile Include="Controls\MarkupExtensions.cs" />
<Compile Include="Controls\ResourceObjectTable.xaml.cs">
Expand Down
4 changes: 3 additions & 1 deletion ILSpy/LoadedAssembly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;

using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.PdbProvider;
Expand Down Expand Up @@ -405,7 +407,7 @@ LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, boo
lock (loadingAssemblies) {
loadingAssemblies.Remove(file);
}
});
}, DispatcherPriority.Normal);
return asm;
}

Expand Down
9 changes: 9 additions & 0 deletions ILSpy/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,9 @@ async void NavigateOnLaunch(string navigateTo, string[] activeTreeViewPath, ILSp
found = true;
} else {
IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies));
// Make sure we wait for assemblies being loaded...
// BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal
await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal);
if (mr != null && mr.ParentModule.PEFile != null) {
found = true;
if (AssemblyTreeView.SelectedItem == initialSelection) {
Expand Down Expand Up @@ -483,6 +486,12 @@ static bool CanResolveTypeInPEFile(PEFile module, ITypeReference typeRef, out En
return false;
}

// We intentionally ignore reference assemblies, so that the loop continues looking for another assembly that might have a usable definition.
if (module.IsReferenceAssembly()) {
typeHandle = default;
return false;
}

switch (typeRef) {
case GetPotentiallyNestedClassTypeReference topLevelType:
typeHandle = topLevelType.ResolveInPEFile(module);
Expand Down

0 comments on commit 4b865c2

Please sign in to comment.