2021-09-20 18:20:01 +02:00

208 lines
7.4 KiB
C#

/*
Copyright (C) 2014-2019 de4dot@gmail.com
This file is part of dnSpy
dnSpy is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
dnSpy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/
// NOTE: This is a copy of dndbg.Engine.DnModuleId. Keep them in sync.
using System;
using System.Diagnostics;
using System.IO;
using dnlib.DotNet;
namespace dnSpy.Contracts.Metadata {
/// <summary>
/// Module ID
/// </summary>
public readonly struct ModuleId : IEquatable<ModuleId> {
[Flags]
enum Flags : byte {
IsDynamic = 0x01,
IsInMemory = 0x02,
NameOnly = 0x04,
CompareMask = IsDynamic | IsInMemory,
}
/// <summary>implicit operator</summary>
/// <param name="moduleFilename">Module filename</param>
public static implicit operator ModuleId(string moduleFilename) => Create(moduleFilename);
/// <summary>
/// Gets the full name, identical to the dnlib assembly full name
/// </summary>
public string AssemblyFullName => asmFullName ?? string.Empty;
/// <summary>
/// Name of module. This is the filename if <see cref="IsInMemory"/> is false, else it's <see cref="ModuleDef.Name"/>
/// </summary>
public string ModuleName => moduleName ?? string.Empty;
/// <summary>
/// true if it's a dynamic module
/// </summary>
public bool IsDynamic => (flags & Flags.IsDynamic) != 0;
/// <summary>
/// true if it's an in-memory module and the file doesn't exist on disk
/// </summary>
public bool IsInMemory => (flags & Flags.IsInMemory) != 0;
/// <summary>
/// true if <see cref="AssemblyFullName"/> isn't used when comparing this instance against
/// other instances.
/// </summary>
public bool ModuleNameOnly => (flags & Flags.NameOnly) != 0;
static readonly StringComparer AssemblyNameComparer = StringComparer.OrdinalIgnoreCase;
// The module name can contain filenames so case must be ignored
static readonly StringComparer ModuleNameComparer = StringComparer.OrdinalIgnoreCase;
readonly string asmFullName;
readonly string moduleName;
readonly Flags flags;
/// <summary>
/// Constructor
/// </summary>
/// <param name="asmFullName">Assembly full name</param>
/// <param name="moduleName">Module name</param>
/// <param name="isDynamic">true if it's a dynamic module</param>
/// <param name="isInMemory">true if it's an in-memory module</param>
/// <param name="nameOnly">true if <paramref name="asmFullName"/> is ignored</param>
public ModuleId(string? asmFullName, string moduleName, bool isDynamic, bool isInMemory, bool nameOnly) {
Debug2.Assert(asmFullName is null || !asmFullName.Contains("\\:"));
this.asmFullName = asmFullName ?? string.Empty;
this.moduleName = moduleName ?? string.Empty;
flags = 0;
if (isDynamic)
flags |= Flags.IsDynamic;
if (isInMemory)
flags |= Flags.IsInMemory;
if (nameOnly)
flags |= Flags.NameOnly;
}
/// <summary>
/// Creates a <see cref="ModuleId"/> that was loaded from a file
/// </summary>
/// <param name="moduleFilename">Module filename</param>
/// <returns></returns>
public static ModuleId Create(string moduleFilename) =>
new ModuleId(string.Empty, GetFullName(moduleFilename), false, false, true);
static string GetFullName(string filename) {
try {
if (!string.IsNullOrEmpty(filename))
return Path.GetFullPath(filename);
}
catch {
}
return filename;
}
/// <summary>
/// Creates a <see cref="ModuleId"/> that was loaded from a file
/// </summary>
/// <param name="module">Module</param>
/// <returns></returns>
public static ModuleId CreateFromFile(ModuleDef module) =>
new ModuleId(module.Assembly?.FullName ?? string.Empty, module.Location, false, false, false);
/// <summary>
/// Creates an in-memory <see cref="ModuleId"/>
/// </summary>
/// <param name="module">Module</param>
/// <returns></returns>
public static ModuleId CreateInMemory(ModuleDef module) =>
new ModuleId(module.Assembly?.FullName ?? string.Empty, module.Name, false, true, false);
/// <summary>
/// Creates a <see cref="ModuleId"/>
/// </summary>
/// <param name="module">Module</param>
/// <param name="isDynamic">true if it's a dynamic module</param>
/// <param name="isInMemory">true if it's an in-memory module</param>
/// <returns></returns>
public static ModuleId Create(ModuleDef module, bool isDynamic, bool isInMemory) =>
new ModuleId(module.Assembly?.FullName ?? string.Empty, !isInMemory ? module.Location : module.Name.String, isDynamic, isInMemory, false);
/// <summary>
/// Creates a <see cref="ModuleId"/>
/// </summary>
/// <param name="asmFullName">Full name of assembly. Must be identical to <see cref="AssemblyDef.FullName"/></param>
/// <param name="moduleName">Name of module. This is the filename if <paramref name="isInMemory"/>
/// is false, else it must be identical to <see cref="ModuleDef.Name"/></param>
/// <param name="isDynamic">true if it's a dynamic module</param>
/// <param name="isInMemory">true if it's an in-memory module</param>
/// <param name="moduleNameOnly">true if <paramref name="asmFullName"/> is ignored</param>
/// <returns></returns>
public static ModuleId Create(string? asmFullName, string moduleName, bool isDynamic, bool isInMemory, bool moduleNameOnly) =>
new ModuleId(asmFullName, moduleName, isDynamic, isInMemory, moduleNameOnly);
/// <summary>
/// operator==()
/// </summary>
/// <param name="a">a</param>
/// <param name="b">b</param>
/// <returns></returns>
public static bool operator ==(ModuleId a, ModuleId b) => a.Equals(b);
/// <summary>
/// operator!=()
/// </summary>
/// <param name="a">a</param>
/// <param name="b">b</param>
/// <returns></returns>
public static bool operator !=(ModuleId a, ModuleId b) => !a.Equals(b);
/// <summary>
/// Equals()
/// </summary>
/// <param name="other">Other instance</param>
/// <returns></returns>
public bool Equals(ModuleId other) =>
(ModuleNameOnly || other.ModuleNameOnly || AssemblyNameComparer.Equals(AssemblyFullName, other.AssemblyFullName)) &&
ModuleNameComparer.Equals(ModuleName, other.ModuleName) &&
(flags & Flags.CompareMask) == (other.flags & Flags.CompareMask);
/// <summary>
/// Equals()
/// </summary>
/// <param name="obj">Other instance</param>
/// <returns></returns>
public override bool Equals(object? obj) => obj is ModuleId other && Equals(other);
/// <summary>
/// GetHashCode()
/// </summary>
/// <returns></returns>
public override int GetHashCode() =>
// We can't use AssemblyFullName since it's not used if ModuleNameOnly is true
ModuleNameComparer.GetHashCode(ModuleName) ^ ((int)(flags & Flags.CompareMask) << 16);
/// <summary>
/// ToString()
/// </summary>
/// <returns></returns>
public override string ToString() {
if (ModuleNameOnly)
return $"DYN={(IsDynamic ? 1 : 0)} MEM={(IsInMemory ? 1 : 0)} [{ModuleName}]";
return $"DYN={(IsDynamic ? 1 : 0)} MEM={(IsInMemory ? 1 : 0)} {AssemblyFullName} [{ModuleName}]";
}
}
}