From cc7533e1f0a61733ab880251016d68c6dc03fad5 Mon Sep 17 00:00:00 2001 From: Qian <26250460+QianMoXi@users.noreply.github.com> Date: Sat, 2 Mar 2024 03:35:35 +0800 Subject: [PATCH] Add the Dotnet runtimes to the list of assemblies (#290) Co-authored-by: ElektroKill --- .../Utilities/DotNetPathProvider.cs | 6 ++ .../Utilities/FrameworkPath.cs | 53 +++++++++++++++- .../Documents/Tabs/DefaultDocumentList.cs | 62 +++++++++++++++++++ 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/dnSpy/dnSpy.Contracts.Logic/Utilities/DotNetPathProvider.cs b/dnSpy/dnSpy.Contracts.Logic/Utilities/DotNetPathProvider.cs index 661d482829..ac04e53b04 100644 --- a/dnSpy/dnSpy.Contracts.Logic/Utilities/DotNetPathProvider.cs +++ b/dnSpy/dnSpy.Contracts.Logic/Utilities/DotNetPathProvider.cs @@ -98,6 +98,12 @@ static FrameworkPaths[] CreateAndSortFrameworkPaths(IEnumerable l return array; } + /// + /// Returns the .NET Core runtime assembly directories found on the system. + /// + /// .NET Core framework path info + public FrameworkPath[] GetSharedDotNetPaths() => netPathsShared.SelectMany(x => x.frameworkPaths).ToArray(); + /// /// Returns the .NET Core runtime assembly directories found on the system. /// diff --git a/dnSpy/dnSpy.Contracts.Logic/Utilities/FrameworkPath.cs b/dnSpy/dnSpy.Contracts.Logic/Utilities/FrameworkPath.cs index 4fc5e3e122..2579b24aa9 100644 --- a/dnSpy/dnSpy.Contracts.Logic/Utilities/FrameworkPath.cs +++ b/dnSpy/dnSpy.Contracts.Logic/Utilities/FrameworkPath.cs @@ -31,10 +31,12 @@ sealed class FrameworkPaths : IComparable { public readonly FrameworkVersion Version; public readonly Version SystemVersion; public readonly bool IsReferencePath; + internal readonly FrameworkPath[] frameworkPaths; string DebuggerPaths => string.Join(Path.PathSeparator.ToString(), Paths); public FrameworkPaths(FrameworkPath[] paths, bool isRef) { + frameworkPaths = paths; var firstPath = paths[0]; #if DEBUG for (int i = 1; i < paths.Length; i++) { @@ -122,13 +124,32 @@ public bool IsCompatibleWithNetStandard(Version netStandardVersion) { } } + /// + /// .NET Core path info + /// // It's a class since very few of these are created [DebuggerDisplay("{Bitness,d}-bit {Version,nq} {Path,nq}")] - sealed class FrameworkPath { + public sealed class FrameworkPath { + /// + /// .NET Core assembly directories + /// public readonly string Path; + /// + /// Assembly bitness + /// public readonly int Bitness; + /// + /// Assembly version + /// public readonly FrameworkVersion Version; + /// + /// Constructor + /// + /// .NET Core assembly directories + /// Assembly bitness + /// Assembly version + /// is null public FrameworkPath(string path, int bitness, FrameworkVersion version) { Path = path ?? throw new ArgumentNullException(nameof(path)); Bitness = bitness; @@ -136,12 +157,34 @@ public FrameworkPath(string path, int bitness, FrameworkVersion version) { } } - readonly struct FrameworkVersion : IComparable, IEquatable { + /// + /// .NET Core version info + /// + public readonly struct FrameworkVersion : IComparable, IEquatable { + /// + /// The major component + /// public readonly int Major; + /// + /// The minor component + /// public readonly int Minor; + /// + /// The patch component + /// public readonly int Patch; + /// + /// The extra component + /// public readonly string Extra; + /// + /// Constructor + /// + /// The major component + /// The minor component + /// The patch component + /// The extra component public FrameworkVersion(int major, int minor, int patch, string extra) { Major = major; Minor = minor; @@ -149,12 +192,14 @@ public FrameworkVersion(int major, int minor, int patch, string extra) { Extra = extra; } + /// public override string ToString() { if (Extra.Length == 0) return $"{Major}.{Minor}.{Patch}"; return $"{Major}.{Minor}.{Patch}-{Extra}"; } + /// public int CompareTo(FrameworkVersion other) { int c = Major.CompareTo(other.Major); if (c != 0) @@ -178,13 +223,17 @@ static int CompareExtra(string a, string b) { return StringComparer.Ordinal.Compare(a, b); } + /// public bool Equals(FrameworkVersion other) => Major == other.Major && Minor == other.Minor && Patch == other.Patch && StringComparer.Ordinal.Equals(Extra, other.Extra); + /// public override bool Equals(object? obj) => obj is FrameworkVersion other && Equals(other); + + /// public override int GetHashCode() => Major ^ Minor ^ Patch ^ StringComparer.Ordinal.GetHashCode(Extra ?? string.Empty); } } diff --git a/dnSpy/dnSpy/Documents/Tabs/DefaultDocumentList.cs b/dnSpy/dnSpy/Documents/Tabs/DefaultDocumentList.cs index 61b78905e3..6a6d2d3b75 100644 --- a/dnSpy/dnSpy/Documents/Tabs/DefaultDocumentList.cs +++ b/dnSpy/dnSpy/Documents/Tabs/DefaultDocumentList.cs @@ -26,6 +26,7 @@ You should have received a copy of the GNU General Public License using System.Xml.Linq; using dnSpy.Contracts.App; using dnSpy.Contracts.Documents; +using dnSpy.Contracts.Utilities; namespace dnSpy.Documents.Tabs { sealed class DefaultDocumentList { @@ -48,6 +49,61 @@ public DefaultDocumentList(string name, IEnumerable asmNames) { public void Add(DsDocumentInfo file) => files.Add(file); } + sealed class DotnetReferenceFileFinder { + readonly CancellationToken cancellationToken; + readonly List allLists; + + public DotnetReferenceFileFinder(CancellationToken cancellationToken) { + this.cancellationToken = cancellationToken; + allLists = new List(); + } + + public IEnumerable AllFiles => allLists.ToArray(); + + public void Find() { + cancellationToken.ThrowIfCancellationRequested(); + + var dotNetPathProvider = new DotNetPathProvider(); + + foreach (var frameworkPath in dotNetPathProvider.GetSharedDotNetPaths()) { + cancellationToken.ThrowIfCancellationRequested(); + var appName = $"{Path.GetFileName(Path.GetDirectoryName(frameworkPath.Path))} {frameworkPath.Version} (x{frameworkPath.Bitness:d})"; + allLists.Add(new DefaultDocumentList(appName, Directory.GetFiles(frameworkPath.Path, "*.dll").Where(FilterFiles).Select(DsDocumentInfo.CreateDocument))); + } + } + + static bool FilterFiles(string file) { + var fileName = Path.GetFileName(file); + + if (fileName.Length == 0) + return false; + + // The official managed assemblies in .NET Core almost always start with uppercase characters, which makes it easy for quick filtering. + if (fileName[0] >= 'A' && fileName[0] <= 'Z') { + // Exclude native assemblies here + // Microsoft.NETCore.App + if (fileName.StartsWith("Microsoft.DiaSymReader.Native.", StringComparison.Ordinal)) + return false; + + // Microsoft.WindowsDesktop.App + if (fileName.StartsWith("D3DCompiler_", StringComparison.Ordinal) || fileName.Equals("PenImc_cor3") || fileName.Equals("PresentationNative_cor3")) + return false; + + return true; + } + if (fileName[0] >= 'a' && fileName[0] <= 'z') { + // Include managed assemblies here + // Microsoft.NETCore.App + if (fileName.Equals("mscorlib") || fileName.Equals("netstandard")) + return true; + + return false; + } + + return true; + } + } + sealed class ReferenceFileFinder { readonly CancellationToken cancellationToken; readonly List allFiles; @@ -356,6 +412,9 @@ public DefaultDocumentList[] Find() { var finder = new ReferenceFileFinder(cancellationToken); finder.Find(); + var dotnetFinder = new DotnetReferenceFileFinder(cancellationToken); + dotnetFinder.Find(); + var xmlFiles = new List<(DefaultDocumentList list, bool isDefault)>(); foreach (var dir in FilesDirs) { cancellationToken.ThrowIfCancellationRequested(); @@ -381,6 +440,9 @@ public DefaultDocumentList[] Find() { foreach (var f in finder.AllFiles) dict[f.Name] = f; + foreach (var f in dotnetFinder.AllFiles) + dict[f.Name] = f; + foreach (var t in xmlFiles) { if (!t.isDefault) // if user file dict[t.list.Name] = t.list;