Browse Source

Fix #2068: ILSpy can't find referenced library even though it's open

pull/2077/head
Siegfried Pammer 5 years ago
parent
commit
4f8c588c7b
  1. 45
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
  2. 10
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  3. 38
      ILSpy/LoadedAssembly.cs

45
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

@ -61,29 +61,30 @@ namespace ICSharpCode.Decompiler.Metadata
}; };
readonly DotNetCorePackageInfo[] packages; readonly DotNetCorePackageInfo[] packages;
ISet<string> packageBasePaths = new HashSet<string>(StringComparer.Ordinal); readonly List<string> searchPaths = new List<string>();
readonly Version version; readonly List<string> packageBasePaths = new List<string>();
readonly Version targetFrameworkVersion;
readonly string dotnetBasePath = FindDotNetExeDirectory(); readonly string dotnetBasePath = FindDotNetExeDirectory();
public DotNetCorePathFinder(Version version) public DotNetCorePathFinder(Version targetFrameworkVersion)
{ {
this.version = version; this.targetFrameworkVersion = targetFrameworkVersion;
} }
public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version version, ReferenceLoadInfo loadInfo = null) public DotNetCorePathFinder(string parentAssemblyFileName, string targetFrameworkIdString, TargetFrameworkIdentifier targetFramework, Version targetFrameworkVersion, ReferenceLoadInfo loadInfo = null)
{ {
string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName); string assemblyName = Path.GetFileNameWithoutExtension(parentAssemblyFileName);
string basePath = Path.GetDirectoryName(parentAssemblyFileName); string basePath = Path.GetDirectoryName(parentAssemblyFileName);
this.version = version; this.targetFrameworkVersion = targetFrameworkVersion;
if (targetFramework == TargetFrameworkIdentifier.NETStandard) { if (targetFramework == TargetFrameworkIdentifier.NETStandard) {
// .NET Standard 2.1 is implemented by .NET Core 3.0 or higher // .NET Standard 2.1 is implemented by .NET Core 3.0 or higher
if (version.Major == 2 && version.Minor == 1) { if (targetFrameworkVersion.Major == 2 && targetFrameworkVersion.Minor == 1) {
this.version = new Version(3, 0, 0); this.targetFrameworkVersion = new Version(3, 0, 0);
} }
} }
packageBasePaths.Add(basePath); searchPaths.Add(basePath);
var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json"); var depsJsonFileName = Path.Combine(basePath, $"{assemblyName}.deps.json");
if (File.Exists(depsJsonFileName)) { if (File.Exists(depsJsonFileName)) {
@ -106,17 +107,17 @@ namespace ICSharpCode.Decompiler.Metadata
public void AddSearchDirectory(string path) public void AddSearchDirectory(string path)
{ {
this.packageBasePaths.Add(path); this.searchPaths.Add(path);
} }
public void RemoveSearchDirectory(string path) public void RemoveSearchDirectory(string path)
{ {
this.packageBasePaths.Remove(path); this.searchPaths.Remove(path);
} }
public string TryResolveDotNetCore(IAssemblyReference name) public string TryResolveDotNetCore(IAssemblyReference name)
{ {
foreach (var basePath in packageBasePaths) { foreach (var basePath in searchPaths.Concat(packageBasePaths)) {
if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) { if (File.Exists(Path.Combine(basePath, name.Name + ".dll"))) {
return Path.Combine(basePath, name.Name + ".dll"); return Path.Combine(basePath, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) { } else if (File.Exists(Path.Combine(basePath, name.Name + ".exe"))) {
@ -124,7 +125,7 @@ namespace ICSharpCode.Decompiler.Metadata
} }
} }
return FallbackToDotNetSharedDirectory(name, version); return FallbackToDotNetSharedDirectory(name);
} }
internal string GetReferenceAssemblyPath(string targetFramework) internal string GetReferenceAssemblyPath(string targetFramework)
@ -169,7 +170,7 @@ namespace ICSharpCode.Decompiler.Metadata
} }
} }
string FallbackToDotNetSharedDirectory(IAssemblyReference name, Version version) string FallbackToDotNetSharedDirectory(IAssemblyReference name)
{ {
if (dotnetBasePath == null) if (dotnetBasePath == null)
return null; return null;
@ -177,7 +178,7 @@ namespace ICSharpCode.Decompiler.Metadata
foreach (var basePath in basePaths) { foreach (var basePath in basePaths) {
if (!Directory.Exists(basePath)) if (!Directory.Exists(basePath))
continue; continue;
var closestVersion = GetClosestVersionFolder(basePath, version); var closestVersion = GetClosestVersionFolder(basePath, targetFrameworkVersion);
if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) { if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".dll"))) {
return Path.Combine(basePath, closestVersion, name.Name + ".dll"); return Path.Combine(basePath, closestVersion, name.Name + ".dll");
} else if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".exe"))) { } else if (File.Exists(Path.Combine(basePath, closestVersion, name.Name + ".exe"))) {
@ -189,15 +190,17 @@ namespace ICSharpCode.Decompiler.Metadata
static string GetClosestVersionFolder(string basePath, Version version) static string GetClosestVersionFolder(string basePath, Version version)
{ {
string result = null; var foundVersions = new DirectoryInfo(basePath).GetDirectories()
foreach (var folder in new DirectoryInfo(basePath).GetDirectories().Select(d => ConvertToVersion(d.Name)).Where(v => v.Item1 != null).OrderByDescending(v => v.Item1)) { .Select(d => ConvertToVersion(d.Name))
if (folder.Item1 >= version) .Where(v => v.version != null);
result = folder.Item2; foreach (var folder in foundVersions.OrderBy(v => v.Item1)) {
if (folder.version >= version)
return folder.directoryName;
} }
return result ?? version.ToString(); return version.ToString();
} }
internal static (Version, string) ConvertToVersion(string name) internal static (Version version, string directoryName) ConvertToVersion(string name)
{ {
string RemoveTrailingVersionInfo() string RemoveTrailingVersionInfo()
{ {

10
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -327,9 +327,13 @@ namespace ICSharpCode.Decompiler.Metadata
if (assembly != null) if (assembly != null)
return assembly; return assembly;
assembly = SearchDirectory(name, framework_dirs); // when decompiling assemblies that target frameworks prior to 4.0, we can fall back to the 4.0 assemblies in case the target framework is not installed.
if (assembly != null) // but when looking for Microsoft.Build.Framework, Version=15.0.0.0 we should not use the version 4.0 assembly here so that the LoadedAssembly logic can instead fall back to version 15.1.0.0
return assembly; if (name.Version <= new Version(4, 0, 0, 0)) {
assembly = SearchDirectory(name, framework_dirs);
if (assembly != null)
return assembly;
}
if (throwOnError) if (throwOnError)
throw new AssemblyResolutionException(name); throw new AssemblyResolutionException(name);

38
ILSpy/LoadedAssembly.cs

@ -20,6 +20,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection.Metadata; using System.Reflection.Metadata;
using System.Reflection.PortableExecutable; using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -31,6 +32,7 @@ using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.PdbProvider; using ICSharpCode.Decompiler.PdbProvider;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy namespace ICSharpCode.ILSpy
@ -318,6 +320,17 @@ namespace ICSharpCode.ILSpy
static readonly Dictionary<string, LoadedAssembly> loadingAssemblies = new Dictionary<string, LoadedAssembly>(); static readonly Dictionary<string, LoadedAssembly> loadingAssemblies = new Dictionary<string, LoadedAssembly>();
MyUniversalResolver universalResolver; MyUniversalResolver universalResolver;
/// <summary>
/// 1) try to find exact match by tfm + full asm name in loaded assemblies
/// 2) try to find match in search paths
/// 3) if a.deps.json is found: search %USERPROFILE%/.nuget/packages/* as well
/// 4) look in /dotnet/shared/{runtime-pack}/{closest-version}
/// 5) if the version is retargetable or all zeros or ones, search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 6) For "mscorlib.dll" we use the exact same assembly with which ILSpy runs
/// 7) Search the GAC
/// 8) search C:\Windows\Microsoft.NET\Framework64\v4.0.30319
/// 9) try to find match by asm name (no tfm/version) in loaded assemblies
/// </summary>
LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, bool isWinRT, string tfm) LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, bool isWinRT, string tfm)
{ {
string key = tfm + ";" + (isWinRT ? fullName.Name : fullName.FullName); string key = tfm + ";" + (isWinRT ? fullName.Name : fullName.FullName);
@ -359,8 +372,29 @@ namespace ICSharpCode.ILSpy
LoadedAssemblyReferencesInfo.AddMessage(fullName.ToString(), MessageKind.Info, "Success - Loading from: " + file); LoadedAssemblyReferencesInfo.AddMessage(fullName.ToString(), MessageKind.Info, "Success - Loading from: " + file);
asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true }; asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true };
} else { } else {
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName); var candidates = new List<(LoadedAssembly assembly, Version version)>();
return null;
foreach (LoadedAssembly loaded in assemblyList.GetAssemblies()) {
var module = loaded.GetPEFileOrNull();
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly) continue;
var asmDef = reader.GetAssemblyDefinition();
var asmDefName = reader.GetString(asmDef.Name);
if (fullName.Name.Equals(asmDefName, StringComparison.OrdinalIgnoreCase)) {
candidates.Add((loaded, asmDef.Version));
}
}
if (candidates.Count == 0) {
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName);
return null;
}
candidates.SortBy(c => c.version);
var bestCandidate = candidates.FirstOrDefault(c => c.version >= fullName.Version).assembly ?? candidates.Last().assembly;
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Info, "Success - Found in Assembly List with different TFM or version: " + bestCandidate.fileName);
return bestCandidate;
} }
loadingAssemblies.Add(file, asm); loadingAssemblies.Add(file, asm);
} }

Loading…
Cancel
Save