diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index f3c9536d09..b177b7ef91 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -185,7 +185,7 @@ private static string ReplacePrivImplDetails(string il) return Regex.Replace(il, @"'\{[0-9A-F-]+\}'", "''"); } - 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"); diff --git a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs index e118bd05ba..c5c8722cf9 100644 --- a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs +++ b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs @@ -66,16 +66,9 @@ 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 @@ -83,6 +76,13 @@ public DotNetCorePathFinder(string parentAssemblyFileName, string targetFramewor 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); diff --git a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs index 2be6f8072b..b29fae6e51 100644 --- a/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/DotNetCorePathFinderExtensions.cs @@ -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[/\\](?.NETFramework)[/\\]v(?[^/\\]+)[/\\])" + @"|((?Microsoft\.NET)[/\\]assembly[/\\]GAC_(MSIL|32|64)[/\\])" + @"|((?Microsoft\.NET)[/\\]Framework(64)?[/\\](?[^/\\]+)[/\\])" + @"|(NuGetFallbackFolder[/\\](?[^/\\]+)\\(?[^/\\]+)([/\\].*)?[/\\]ref[/\\])" + - @"|(shared[/\\](?[^/\\]+)\\(?[^/\\]+)([/\\].*)?[/\\])"; + @"|(shared[/\\](?[^/\\]+)\\(?[^/\\]+)([/\\].*)?[/\\])" + + @"|(packs[/\\](?[^/\\]+)\\(?[^/\\]+)\\ref([/\\].*)?[/\\])"; + + static readonly string RefPathPattern = + @"(Reference Assemblies[/\\]Microsoft[/\\]Framework[/\\](?.NETFramework)[/\\]v(?[^/\\]+)[/\\])" + + @"|(NuGetFallbackFolder[/\\](?[^/\\]+)\\(?[^/\\]+)([/\\].*)?[/\\]ref[/\\])" + + @"|(packs[/\\](?[^/\\]+)\\(?[^/\\]+)\\ref([/\\].*)?[/\\])"; public static string DetectTargetFrameworkId(this PEFile assembly) { @@ -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; @@ -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 diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index 1f41083026..0b30cbfc42 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -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) @@ -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); } diff --git a/ILSpy.AddIn/AssemblyFileFinder.cs b/ILSpy.AddIn/AssemblyFileFinder.cs index 66846a6cc9..cc6676782c 100644 --- a/ILSpy.AddIn/AssemblyFileFinder.cs +++ b/ILSpy.AddIn/AssemblyFileFinder.cs @@ -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; @@ -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)); diff --git a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs index cf2d65768e..13ef992b0e 100644 --- a/ILSpy.AddIn/Commands/OpenILSpyCommand.cs +++ b/ILSpy.AddIn/Commands/OpenILSpyCommand.cs @@ -80,8 +80,7 @@ protected Dictionary 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) { diff --git a/ILSpy.AddIn/ILSpyInstance.cs b/ILSpy.AddIn/ILSpyInstance.cs index b8c04bd140..bc0d21c374 100644 --- a/ILSpy.AddIn/ILSpyInstance.cs +++ b/ILSpy.AddIn/ILSpyInstance.cs @@ -12,11 +12,11 @@ class ILSpyParameters { public ILSpyParameters(IEnumerable assemblyFileNames, params string[] arguments) { - this.AssemblyFileNames = assemblyFileNames; + this.AssemblyFileNames = assemblyFileNames.ToArray(); this.Arguments = arguments; } - public IEnumerable AssemblyFileNames { get; private set; } + public string[] AssemblyFileNames { get; private set; } public string[] Arguments { get; private set; } } diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 71414747bc..4d4ee4e3a8 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -128,9 +128,7 @@ - - Form - + diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index 8f48a19e26..60c37eefe7 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -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; @@ -405,7 +407,7 @@ LoadedAssembly LookupReferencedAssemblyInternal(IAssemblyReference fullName, boo lock (loadingAssemblies) { loadingAssemblies.Remove(file); } - }); + }, DispatcherPriority.Normal); return asm; } diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 9e03b263c7..10bd97d475 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -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) { @@ -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);