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

10
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -327,9 +327,13 @@ namespace ICSharpCode.Decompiler.Metadata @@ -327,9 +327,13 @@ namespace ICSharpCode.Decompiler.Metadata
if (assembly != null)
return assembly;
assembly = SearchDirectory(name, framework_dirs);
if (assembly != null)
return assembly;
// 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.
// 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
if (name.Version <= new Version(4, 0, 0, 0)) {
assembly = SearchDirectory(name, framework_dirs);
if (assembly != null)
return assembly;
}
if (throwOnError)
throw new AssemblyResolutionException(name);

38
ILSpy/LoadedAssembly.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.CompilerServices;
@ -31,6 +32,7 @@ using ICSharpCode.Decompiler.Metadata; @@ -31,6 +32,7 @@ using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.PdbProvider;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy
@ -318,6 +320,17 @@ namespace ICSharpCode.ILSpy @@ -318,6 +320,17 @@ namespace ICSharpCode.ILSpy
static readonly Dictionary<string, LoadedAssembly> loadingAssemblies = new Dictionary<string, LoadedAssembly>();
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)
{
string key = tfm + ";" + (isWinRT ? fullName.Name : fullName.FullName);
@ -359,8 +372,29 @@ namespace ICSharpCode.ILSpy @@ -359,8 +372,29 @@ namespace ICSharpCode.ILSpy
LoadedAssemblyReferencesInfo.AddMessage(fullName.ToString(), MessageKind.Info, "Success - Loading from: " + file);
asm = new LoadedAssembly(assemblyList, file) { IsAutoLoaded = true };
} else {
LoadedAssemblyReferencesInfo.AddMessageOnce(fullName.ToString(), MessageKind.Error, "Could not find reference: " + fullName);
return null;
var candidates = new List<(LoadedAssembly assembly, Version version)>();
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);
}

Loading…
Cancel
Save