Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Dotnet runtimes to the list of assemblies #290

Merged
merged 6 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions dnSpy/dnSpy.Contracts.Logic/Utilities/DotNetPathProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ static FrameworkPaths[] CreateAndSortFrameworkPaths(IEnumerable<FrameworkPath> l
return array;
}

/// <summary>
/// Returns the .NET Core runtime assembly directories found on the system.
/// </summary>
/// <returns>.NET Core framework path info</returns>
public FrameworkPath[] GetSharedDotNetPaths() => netPathsShared.SelectMany(x => x.frameworkPaths).ToArray();

/// <summary>
/// Returns the .NET Core runtime assembly directories found on the system.
/// </summary>
Expand Down
53 changes: 51 additions & 2 deletions dnSpy/dnSpy.Contracts.Logic/Utilities/FrameworkPath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ sealed class FrameworkPaths : IComparable<FrameworkPaths> {
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++) {
Expand Down Expand Up @@ -122,39 +124,82 @@ public bool IsCompatibleWithNetStandard(Version netStandardVersion) {
}
}

/// <summary>
/// .NET Core path info
/// </summary>
// 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 {
/// <summary>
/// .NET Core assembly directories
/// </summary>
public readonly string Path;
/// <summary>
/// Assembly bitness
/// </summary>
public readonly int Bitness;
/// <summary>
/// Assembly version
/// </summary>
public readonly FrameworkVersion Version;

/// <summary>
/// Constructor
/// </summary>
/// <param name="path">.NET Core assembly directories</param>
/// <param name="bitness">Assembly bitness</param>
/// <param name="version">Assembly version</param>
/// <exception cref="ArgumentNullException"><paramref name="path"/> is null</exception>
public FrameworkPath(string path, int bitness, FrameworkVersion version) {
Path = path ?? throw new ArgumentNullException(nameof(path));
Bitness = bitness;
Version = version;
}
}

readonly struct FrameworkVersion : IComparable<FrameworkVersion>, IEquatable<FrameworkVersion> {
/// <summary>
/// .NET Core version info
/// </summary>
public readonly struct FrameworkVersion : IComparable<FrameworkVersion>, IEquatable<FrameworkVersion> {
/// <summary>
/// The major component
/// </summary>
public readonly int Major;
/// <summary>
/// The minor component
/// </summary>
public readonly int Minor;
/// <summary>
/// The patch component
/// </summary>
public readonly int Patch;
/// <summary>
/// The extra component
/// </summary>
public readonly string Extra;

/// <summary>
/// Constructor
/// </summary>
/// <param name="major">The major component</param>
/// <param name="minor">The minor component</param>
/// <param name="patch">The patch component</param>
/// <param name="extra">The extra component</param>
public FrameworkVersion(int major, int minor, int patch, string extra) {
Major = major;
Minor = minor;
Patch = patch;
Extra = extra;
}

/// <inheritdoc/>
public override string ToString() {
if (Extra.Length == 0)
return $"{Major}.{Minor}.{Patch}";
return $"{Major}.{Minor}.{Patch}-{Extra}";
}

/// <inheritdoc/>
public int CompareTo(FrameworkVersion other) {
int c = Major.CompareTo(other.Major);
if (c != 0)
Expand All @@ -178,13 +223,17 @@ static int CompareExtra(string a, string b) {
return StringComparer.Ordinal.Compare(a, b);
}

/// <inheritdoc/>
public bool Equals(FrameworkVersion other) =>
Major == other.Major &&
Minor == other.Minor &&
Patch == other.Patch &&
StringComparer.Ordinal.Equals(Extra, other.Extra);

/// <inheritdoc/>
public override bool Equals(object? obj) => obj is FrameworkVersion other && Equals(other);

/// <inheritdoc/>
public override int GetHashCode() => Major ^ Minor ^ Patch ^ StringComparer.Ordinal.GetHashCode(Extra ?? string.Empty);
}
}
62 changes: 62 additions & 0 deletions dnSpy/dnSpy/Documents/Tabs/DefaultDocumentList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -48,6 +49,61 @@ public DefaultDocumentList(string name, IEnumerable<DsDocumentInfo> asmNames) {
public void Add(DsDocumentInfo file) => files.Add(file);
}

sealed class DotnetReferenceFileFinder {
readonly CancellationToken cancellationToken;
readonly List<DefaultDocumentList> allLists;

public DotnetReferenceFileFinder(CancellationToken cancellationToken) {
this.cancellationToken = cancellationToken;
allLists = new List<DefaultDocumentList>();
}

public IEnumerable<DefaultDocumentList> 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<RefFileList> allFiles;
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand Down