/* 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 . */ // 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 { /// /// Module ID /// public readonly struct ModuleId : IEquatable { [Flags] enum Flags : byte { IsDynamic = 0x01, IsInMemory = 0x02, NameOnly = 0x04, CompareMask = IsDynamic | IsInMemory, } /// implicit operator /// Module filename public static implicit operator ModuleId(string moduleFilename) => Create(moduleFilename); /// /// Gets the full name, identical to the dnlib assembly full name /// public string AssemblyFullName => asmFullName ?? string.Empty; /// /// Name of module. This is the filename if is false, else it's /// public string ModuleName => moduleName ?? string.Empty; /// /// true if it's a dynamic module /// public bool IsDynamic => (flags & Flags.IsDynamic) != 0; /// /// true if it's an in-memory module and the file doesn't exist on disk /// public bool IsInMemory => (flags & Flags.IsInMemory) != 0; /// /// true if isn't used when comparing this instance against /// other instances. /// 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; /// /// Constructor /// /// Assembly full name /// Module name /// true if it's a dynamic module /// true if it's an in-memory module /// true if is ignored 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; } /// /// Creates a that was loaded from a file /// /// Module filename /// 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; } /// /// Creates a that was loaded from a file /// /// Module /// public static ModuleId CreateFromFile(ModuleDef module) => new ModuleId(module.Assembly?.FullName ?? string.Empty, module.Location, false, false, false); /// /// Creates an in-memory /// /// Module /// public static ModuleId CreateInMemory(ModuleDef module) => new ModuleId(module.Assembly?.FullName ?? string.Empty, module.Name, false, true, false); /// /// Creates a /// /// Module /// true if it's a dynamic module /// true if it's an in-memory module /// 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); /// /// Creates a /// /// Full name of assembly. Must be identical to /// Name of module. This is the filename if /// is false, else it must be identical to /// true if it's a dynamic module /// true if it's an in-memory module /// true if is ignored /// public static ModuleId Create(string? asmFullName, string moduleName, bool isDynamic, bool isInMemory, bool moduleNameOnly) => new ModuleId(asmFullName, moduleName, isDynamic, isInMemory, moduleNameOnly); /// /// operator==() /// /// a /// b /// public static bool operator ==(ModuleId a, ModuleId b) => a.Equals(b); /// /// operator!=() /// /// a /// b /// public static bool operator !=(ModuleId a, ModuleId b) => !a.Equals(b); /// /// Equals() /// /// Other instance /// 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); /// /// Equals() /// /// Other instance /// public override bool Equals(object? obj) => obj is ModuleId other && Equals(other); /// /// GetHashCode() /// /// 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); /// /// ToString() /// /// 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}]"; } } }