/*
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}]";
}
}
}