Skip to content

Commit 4b865c2

Browse files
Fix #2076: VS AddIn opens reference assembly.
1 parent 8d72672 commit 4b865c2

10 files changed

Lines changed: 77 additions & 41 deletions

File tree

ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ private static string ReplacePrivImplDetails(string il)
185185
return Regex.Replace(il, @"'<PrivateImplementationDetails>\{[0-9A-F-]+\}'", "'<PrivateImplementationDetails>'");
186186
}
187187

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

190190
static readonly string refAsmPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
191191
@"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2");

ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,23 @@ public DotNetCorePackageInfo(string fullName, string type, string path, string[]
6666
readonly Version targetFrameworkVersion;
6767
readonly string dotnetBasePath = FindDotNetExeDirectory();
6868

69-
public DotNetCorePathFinder(Version targetFrameworkVersion)
69+
public DotNetCorePathFinder(TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion)
7070
{
7171
this.targetFrameworkVersion = targetFrameworkVersion;
72-
}
73-
74-
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null)
75-
{
76-
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
77-
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
78-
this.targetFrameworkVersion = targetFrameworkVersion;
7972

8073
if (targetFramework == TargetFrameworkIdentifier.NETStandard) {
8174
// .NET Standard 2.1 is implemented by .NET Core 3.0 or higher
8275
if (targetFrameworkVersion.Major == 2 && targetFrameworkVersion.Minor == 1) {
8376
this.targetFrameworkVersion = new Version(3, 0, 0);
8477
}
8578
}
79+
}
80+
81+
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null)
82+
: this(targetFramework, targetFrameworkVersion)
83+
{
84+
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
85+
string basePath = Path.GetDirectoryName(parentAssemblyFileName);
8686

8787
searchPaths.Add(basePath);
8888

ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,26 @@
2020
using System.Collections.Generic;
2121
using System.Linq;
2222
using System.Text.RegularExpressions;
23-
using ICSharpCode.Decompiler.TypeSystem.Implementation;
2423
using System.Reflection.Metadata;
2524
using System.Reflection.PortableExecutable;
25+
using ICSharpCode.Decompiler.TypeSystem;
2626

2727
namespace ICSharpCode.Decompiler.Metadata
2828
{
2929
public static class DotNetCorePathFinderExtensions
3030
{
31-
static readonly string RefPathPattern =
31+
static readonly string PathPattern =
3232
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
3333
@"|((?<type>Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" +
3434
@"|((?<type>Microsoft\.NET)[/\\]Framework(64)?[/\\](?<version>[^/\\]+)[/\\])" +
3535
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
36-
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])";
36+
@"|(shared[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\])" +
37+
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";
38+
39+
static readonly string RefPathPattern =
40+
@"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?<type>.NETFramework)[/\\]v(?<version>[^/\\]+)[/\\])" +
41+
@"|(NuGetFallbackFolder[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)([/\\].*)?[/\\]ref[/\\])" +
42+
@"|(packs[/\\](?<type>[^/\\]+)\\(?<version>[^/\\]+)\\ref([/\\].*)?[/\\])";
3743

3844
public static string DetectTargetFrameworkId(this PEFile assembly)
3945
{
@@ -99,7 +105,7 @@ public static string DetectTargetFrameworkId(this PEReader assembly, string asse
99105
* - .NETCore -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.netcore.app\2.1.0\ref\netcoreapp2.1\System.Console.dll
100106
* - .NETStandard -> C:\Program Files\dotnet\sdk\NuGetFallbackFolder\netstandard.library\2.0.3\build\netstandard2.0\ref\netstandard.dll
101107
*/
102-
var pathMatch = Regex.Match(assemblyPath, RefPathPattern,
108+
var pathMatch = Regex.Match(assemblyPath, PathPattern,
103109
RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.ExplicitCapture);
104110
if (pathMatch.Success) {
105111
var type = pathMatch.Groups["type"].Value;
@@ -121,6 +127,25 @@ public static string DetectTargetFrameworkId(this PEReader assembly, string asse
121127

122128
return string.Empty;
123129
}
130+
131+
public static bool IsReferenceAssembly(this PEFile assembly)
132+
{
133+
return IsReferenceAssembly(assembly.Reader, assembly.FileName);
134+
}
135+
136+
public static bool IsReferenceAssembly(this PEReader assembly, string assemblyPath)
137+
{
138+
if (assembly == null)
139+
throw new ArgumentNullException(nameof(assembly));
140+
141+
var metadata = assembly.GetMetadataReader();
142+
if (metadata.GetCustomAttributes(Handle.AssemblyDefinition).HasKnownAttribute(metadata, KnownAttribute.ReferenceAssembly))
143+
return true;
144+
145+
// Try to detect reference assembly through specific path pattern
146+
var refPathMatch = Regex.Match(assemblyPath, RefPathPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled);
147+
return refPathMatch.Success;
148+
}
124149
}
125150

126151
public class ReferenceLoadInfo

ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,19 @@ public string[] GetSearchDirectories()
9292
public UniversalAssemblyResolver(string mainAssemblyFileName, bool throwOnError, string targetFramework,
9393
PEStreamOptions streamOptions = PEStreamOptions.Default, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default)
9494
{
95+
this.mainAssemblyFileName = mainAssemblyFileName;
96+
this.throwOnError = throwOnError;
9597
this.streamOptions = streamOptions;
9698
this.metadataOptions = metadataOptions;
9799
this.targetFramework = targetFramework ?? string.Empty;
98100
(targetFrameworkIdentifier, targetFrameworkVersion) = ParseTargetFramework(this.targetFramework);
99-
this.mainAssemblyFileName = mainAssemblyFileName;
100-
this.baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
101-
this.throwOnError = throwOnError;
102-
if (string.IsNullOrWhiteSpace(this.baseDirectory))
103-
this.baseDirectory = Environment.CurrentDirectory;
104-
AddSearchDirectory(baseDirectory);
101+
102+
if (mainAssemblyFileName != null) {
103+
string baseDirectory = Path.GetDirectoryName(mainAssemblyFileName);
104+
if (string.IsNullOrWhiteSpace(this.baseDirectory))
105+
this.baseDirectory = Environment.CurrentDirectory;
106+
AddSearchDirectory(baseDirectory);
107+
}
105108
}
106109

107110
internal static (TargetFrameworkIdentifier, Version) ParseTargetFramework(string targetFramework)
@@ -191,7 +194,10 @@ public string FindAssemblyFile(IAssemblyReference name)
191194
if (IsZeroOrAllOnes(targetFrameworkVersion))
192195
goto default;
193196
if (dotNetCorePathFinder == null) {
194-
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
197+
if (mainAssemblyFileName == null)
198+
dotNetCorePathFinder = new DotNetCorePathFinder(targetFrameworkIdentifier, targetFrameworkVersion);
199+
else
200+
dotNetCorePathFinder = new DotNetCorePathFinder(mainAssemblyFileName, targetFramework, targetFrameworkIdentifier, targetFrameworkVersion);
195201
foreach (var directory in directories) {
196202
dotNetCorePathFinder.AddSearchDirectory(directory);
197203
}

ILSpy.AddIn/AssemblyFileFinder.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,29 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.IO;
42
using System.Linq;
5-
using System.Text;
63
using System.Text.RegularExpressions;
7-
using ICSharpCode.Decompiler;
84
using ICSharpCode.Decompiler.Metadata;
95
using ICSharpCode.Decompiler.Util;
10-
using Mono.Cecil;
116

127
namespace ICSharpCode.ILSpy.AddIn
138
{
149
public class AssemblyFileFinder
1510
{
16-
public static string FindAssemblyFile(AssemblyDefinition assemblyDefinition, string assemblyFile)
11+
public static string FindAssemblyFile(Mono.Cecil.AssemblyDefinition assemblyDefinition, string assemblyFile)
1712
{
18-
var assemblyResolver = new UniversalAssemblyResolver(assemblyFile, false,
19-
DetectTargetFrameworkId(assemblyDefinition, assemblyFile));
13+
string tfi = DetectTargetFrameworkId(assemblyDefinition, assemblyFile);
14+
UniversalAssemblyResolver assemblyResolver;
2015
if (IsReferenceAssembly(assemblyDefinition, assemblyFile)) {
21-
assemblyResolver.RemoveSearchDirectory(Path.GetDirectoryName(assemblyFile));
16+
assemblyResolver = new UniversalAssemblyResolver(null, throwOnError: false, tfi);
17+
} else {
18+
assemblyResolver = new UniversalAssemblyResolver(assemblyFile, throwOnError: false, tfi);
2219
}
23-
return assemblyResolver.FindAssemblyFile(
24-
ICSharpCode.Decompiler.Metadata.AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
20+
21+
return assemblyResolver.FindAssemblyFile(AssemblyNameReference.Parse(assemblyDefinition.Name.FullName));
2522
}
2623

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

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

43-
public static string DetectTargetFrameworkId(AssemblyDefinition assembly, string assemblyPath = null)
40+
public static string DetectTargetFrameworkId(Mono.Cecil.AssemblyDefinition assembly, string assemblyPath = null)
4441
{
4542
if (assembly == null)
4643
throw new ArgumentNullException(nameof(assembly));

ILSpy.AddIn/Commands/OpenILSpyCommand.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,7 @@ protected Dictionary<string, DetectedReference> GetReferences(Microsoft.CodeAnal
8080
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
8181
string assemblyName = assemblyDef.Name.Name;
8282
string resolvedAssemblyFile = AssemblyFileFinder.FindAssemblyFile(assemblyDef, reference.Display);
83-
dict.Add(assemblyName,
84-
new DetectedReference(assemblyName, resolvedAssemblyFile, false));
83+
dict.Add(assemblyName, new DetectedReference(assemblyName, resolvedAssemblyFile, false));
8584
}
8685
}
8786
foreach (var projectReference in parentProject.ProjectReferences) {

ILSpy.AddIn/ILSpyInstance.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ class ILSpyParameters
1212
{
1313
public ILSpyParameters(IEnumerable<string> assemblyFileNames, params string[] arguments)
1414
{
15-
this.AssemblyFileNames = assemblyFileNames;
15+
this.AssemblyFileNames = assemblyFileNames.ToArray();
1616
this.Arguments = arguments;
1717
}
1818

19-
public IEnumerable<string> AssemblyFileNames { get; private set; }
19+
public string[] AssemblyFileNames { get; private set; }
2020
public string[] Arguments { get; private set; }
2121
}
2222

ILSpy/ILSpy.csproj

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,7 @@
128128
<Compile Include="Commands\ShowDebugSteps.cs" />
129129
<Compile Include="Commands\SortAssemblyListCommand.cs" />
130130
<Compile Include="Controls\BoolToVisibilityConverter.cs" />
131-
<Compile Include="Controls\CustomDialog.cs">
132-
<SubType>Form</SubType>
133-
</Compile>
131+
<Compile Include="Controls\CustomDialog.cs" />
134132
<Compile Include="Controls\GridViewColumnAutoSize.cs" />
135133
<Compile Include="Controls\MarkupExtensions.cs" />
136134
<Compile Include="Controls\ResourceObjectTable.xaml.cs">

ILSpy/LoadedAssembly.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
using System.Runtime.InteropServices;
2828
using System.Threading;
2929
using System.Threading.Tasks;
30+
using System.Windows.Threading;
31+
3032
using ICSharpCode.Decompiler.DebugInfo;
3133
using ICSharpCode.Decompiler.Metadata;
3234
using ICSharpCode.Decompiler.PdbProvider;
@@ -405,7 +407,7 @@ LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, boo
405407
lock (loadingAssemblies) {
406408
loadingAssemblies.Remove(file);
407409
}
408-
});
410+
}, DispatcherPriority.Normal);
409411
return asm;
410412
}
411413

ILSpy/MainWindow.xaml.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,9 @@ async void NavigateOnLaunch(string navigateTo, string[] activeTreeViewPath, ILSp
408408
found = true;
409409
} else {
410410
IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies));
411+
// Make sure we wait for assemblies being loaded...
412+
// BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal
413+
await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal);
411414
if (mr != null && mr.ParentModule.PEFile != null) {
412415
found = true;
413416
if (AssemblyTreeView.SelectedItem == initialSelection) {
@@ -483,6 +486,12 @@ static bool CanResolveTypeInPEFile(PEFile module, ITypeReference typeRef, out En
483486
return false;
484487
}
485488

489+
// We intentionally ignore reference assemblies, so that the loop continues looking for another assembly that might have a usable definition.
490+
if (module.IsReferenceAssembly()) {
491+
typeHandle = default;
492+
return false;
493+
}
494+
486495
switch (typeRef) {
487496
case GetPotentiallyNestedClassTypeReference topLevelType:
488497
typeHandle = topLevelType.ResolveInPEFile(module);

0 commit comments

Comments
 (0)