// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using dnlib.Utils;
using dnlib.DotNet.MD;
using dnlib.DotNet.Pdb;
using dnlib.DotNet.Writer;
using dnlib.PE;
using dnlib.Threading;
using dnlib.W32Resources;
using System.Diagnostics;
namespace dnlib.DotNet {
///
/// A high-level representation of a row in the Module table
///
public abstract class ModuleDef : IHasCustomAttribute, IHasCustomDebugInformation, IResolutionScope, IDisposable, IListListener, IModule, ITypeDefFinder, IDnlibDef, ITokenResolver, ISignatureReaderHelper {
/// Default characteristics
protected const Characteristics DefaultCharacteristics = Characteristics.ExecutableImage | Characteristics.Bit32Machine;
/// Default DLL characteristics
protected const DllCharacteristics DefaultDllCharacteristics = DllCharacteristics.TerminalServerAware | DllCharacteristics.NoSeh | DllCharacteristics.NxCompat | DllCharacteristics.DynamicBase;
///
/// The row id in its table
///
protected uint rid;
#if THREAD_SAFE
readonly Lock theLock = Lock.Create();
#endif
///
/// Initialize this in the ctor
///
protected ICorLibTypes corLibTypes;
///
/// PDB state
///
protected PdbState pdbState;
TypeDefFinder typeDefFinder;
///
/// Array of last used rid in each table. I.e., next free rid is value + 1
///
protected readonly int[] lastUsedRids = new int[64];
/// Module context
protected ModuleContext context;
///
public MDToken MDToken => new MDToken(Table.Module, rid);
///
public uint Rid {
get => rid;
set => rid = value;
}
///
public int HasCustomAttributeTag => 7;
///
public int ResolutionScopeTag => 0;
///
/// Gets/sets a user value. This is never used by dnlib. This property isn't thread safe.
///
public object Tag {
get => tag;
set => tag = value;
}
object tag;
///
public ScopeType ScopeType => ScopeType.ModuleDef;
///
public string ScopeName => FullName;
///
/// Gets/sets Module.Generation column
///
public ushort Generation {
get => generation;
set => generation = value;
}
///
protected ushort generation;
///
/// Gets/sets Module.Name column
///
public UTF8String Name {
get => name;
set => name = value;
}
/// Name
protected UTF8String name;
///
/// Gets/sets Module.Mvid column
///
public Guid? Mvid {
get => mvid;
set => mvid = value;
}
///
protected Guid? mvid;
///
/// Gets/sets Module.EncId column
///
public Guid? EncId {
get => encId;
set => encId = value;
}
///
protected Guid? encId;
///
/// Gets/sets Module.EncBaseId column
///
public Guid? EncBaseId {
get => encBaseId;
set => encBaseId = value;
}
///
protected Guid? encBaseId;
///
/// Gets all custom attributes
///
public CustomAttributeCollection CustomAttributes {
get {
if (customAttributes is null)
InitializeCustomAttributes();
return customAttributes;
}
}
///
protected CustomAttributeCollection customAttributes;
/// Initializes
protected virtual void InitializeCustomAttributes() =>
Interlocked.CompareExchange(ref customAttributes, new CustomAttributeCollection(), null);
///
public int HasCustomDebugInformationTag => 7;
///
public bool HasCustomDebugInfos => CustomDebugInfos.Count > 0;
///
/// Gets all custom debug infos
///
public IList CustomDebugInfos {
get {
if (customDebugInfos is null)
InitializeCustomDebugInfos();
return customDebugInfos;
}
}
///
protected IList customDebugInfos;
/// Initializes
protected virtual void InitializeCustomDebugInfos() =>
Interlocked.CompareExchange(ref customDebugInfos, new List(), null);
///
/// Gets the module's assembly. To set this value, add this
/// to .
///
public AssemblyDef Assembly {
get => assembly;
internal set => assembly = value;
}
///
protected AssemblyDef assembly;
///
/// Gets a list of all non-nested s. See also
///
public IList Types {
get {
if (types is null)
InitializeTypes();
return types;
}
}
///
protected LazyList types;
/// Initializes
protected virtual void InitializeTypes() =>
Interlocked.CompareExchange(ref types, new LazyList(this), null);
///
/// Gets a list of all s
///
public IList ExportedTypes {
get {
if (exportedTypes is null)
InitializeExportedTypes();
return exportedTypes;
}
}
///
protected IList exportedTypes;
/// Initializes
protected virtual void InitializeExportedTypes() =>
Interlocked.CompareExchange(ref exportedTypes, new List(), null);
///
/// Gets/sets the native entry point. Only one of and
/// can be set. You write to one and the other one gets cleared.
///
public RVA NativeEntryPoint {
get {
if (!nativeAndManagedEntryPoint_initialized)
InitializeNativeAndManagedEntryPoint();
return nativeEntryPoint;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
nativeEntryPoint = value;
managedEntryPoint = null;
Cor20HeaderFlags |= ComImageFlags.NativeEntryPoint;
nativeAndManagedEntryPoint_initialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
/// Gets/sets the managed entry point. Only one of and
/// can be set. You write to one and the other one gets cleared.
///
public IManagedEntryPoint ManagedEntryPoint {
get {
if (!nativeAndManagedEntryPoint_initialized)
InitializeNativeAndManagedEntryPoint();
return managedEntryPoint;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
nativeEntryPoint = 0;
managedEntryPoint = value;
Cor20HeaderFlags &= ~ComImageFlags.NativeEntryPoint;
nativeAndManagedEntryPoint_initialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected RVA nativeEntryPoint;
///
protected IManagedEntryPoint managedEntryPoint;
///
protected bool nativeAndManagedEntryPoint_initialized;
void InitializeNativeAndManagedEntryPoint() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (nativeAndManagedEntryPoint_initialized)
return;
nativeEntryPoint = GetNativeEntryPoint_NoLock();
managedEntryPoint = GetManagedEntryPoint_NoLock();
nativeAndManagedEntryPoint_initialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual RVA GetNativeEntryPoint_NoLock() => 0;
/// Called to initialize
protected virtual IManagedEntryPoint GetManagedEntryPoint_NoLock() => null;
///
public bool HasCustomAttributes => CustomAttributes.Count > 0;
///
/// Gets/sets the entry point method
///
public MethodDef EntryPoint {
get => ManagedEntryPoint as MethodDef;
set => ManagedEntryPoint = value;
}
///
/// true if is non-zero
///
public bool IsNativeEntryPointValid => NativeEntryPoint != 0;
///
/// true if is non-null
///
public bool IsManagedEntryPointValid => ManagedEntryPoint is not null;
///
/// true if is non-null
///
public bool IsEntryPointValid => EntryPoint is not null;
///
/// Gets a list of all s
///
public ResourceCollection Resources {
get {
if (resources is null)
InitializeResources();
return resources;
}
}
///
protected ResourceCollection resources;
/// Initializes
protected virtual void InitializeResources() =>
Interlocked.CompareExchange(ref resources, new ResourceCollection(), null);
///
/// Gets/sets the . This is null if there are no
/// vtable fixups.
///
public VTableFixups VTableFixups {
get {
if (!vtableFixups_isInitialized)
InitializeVTableFixups();
return vtableFixups;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
vtableFixups = value;
vtableFixups_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected VTableFixups vtableFixups;
///
protected bool vtableFixups_isInitialized;
void InitializeVTableFixups() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (vtableFixups_isInitialized)
return;
vtableFixups = GetVTableFixups_NoLock();
vtableFixups_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual VTableFixups GetVTableFixups_NoLock() => null;
///
/// true if there's at least one in
///
public bool HasTypes => Types.Count > 0;
///
/// true if there's at least one in
///
public bool HasExportedTypes => ExportedTypes.Count > 0;
///
/// true if there's at least one in
///
public bool HasResources => Resources.Count > 0;
///
public string FullName => UTF8String.ToSystemStringOrEmpty(name);
///
/// Gets/sets the path of the module or an empty string if it wasn't loaded from disk
///
public string Location {
get => location;
set => location = value;
}
///
protected string location;
///
/// Gets the
///
public ICorLibTypes CorLibTypes => corLibTypes;
///
/// Gets the instance
///
TypeDefFinder TypeDefFinder {
get {
if (typeDefFinder is null)
Interlocked.CompareExchange(ref typeDefFinder, new TypeDefFinder(Types), null);
return typeDefFinder;
}
}
///
/// Gets/sets the module context. This is never null.
///
public ModuleContext Context {
get {
if (context is null)
Interlocked.CompareExchange(ref context, new ModuleContext(), null);
return context;
}
set => context = value ?? new ModuleContext();
}
///
/// If true, the cache is enabled. The cache is used by
/// and to find types.
///
/// IMPORTANT: Only enable the cache if this module's types keep their exact
/// name, namespace, and declaring type and if no type is either added or
/// removed from or from any type that is reachable from the
/// top-level types in (i.e., any type owned by this module).
/// This is disabled by default. When disabled, all calls to
/// and will result in a slow O(n) (linear) search.
///
///
public bool EnableTypeDefFindCache {
get => TypeDefFinder.IsCacheEnabled;
set => TypeDefFinder.IsCacheEnabled = value;
}
///
/// true if this is the manifest (main) module
///
public bool IsManifestModule {
get {
var asm = assembly;
return asm is not null && asm.ManifestModule == this;
}
}
///
/// Gets the global (aka. <Module>) type or null if there are no types
///
public TypeDef GlobalType => Types.Count == 0 ? null : Types[0];
///
/// true if it's the core library module, false if it's not the core library module,
/// and null if it's not known.
///
public bool? IsCoreLibraryModule { get; set; }
///
/// Gets/sets the Win32 resources
///
public Win32Resources Win32Resources {
get {
if (!win32Resources_isInitialized)
InitializeWin32Resources();
return win32Resources;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
win32Resources = value;
win32Resources_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected Win32Resources win32Resources;
///
protected bool win32Resources_isInitialized;
void InitializeWin32Resources() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (win32Resources_isInitialized)
return;
win32Resources = GetWin32Resources_NoLock();
win32Resources_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual Win32Resources GetWin32Resources_NoLock() => null;
///
/// Gets the . This is null if no PDB file
/// has been loaded or if no PDB file could be found.
///
public PdbState PdbState => pdbState;
///
/// Module kind
///
public ModuleKind Kind { get; set; }
///
/// Gets/sets the characteristics (from PE file header)
///
public Characteristics Characteristics { get; set; }
///
/// Gets/sets the DLL characteristics (from PE optional header)
///
public DllCharacteristics DllCharacteristics { get; set; }
///
/// Gets/sets the runtime version which is stored in the metadata header.
/// See .
///
/// Not thread safe
public string RuntimeVersion {
get => runtimeVersion;
set {
if (runtimeVersion != value) {
runtimeVersion = value;
cachedWinMDStatus = null;
runtimeVersionWinMD = null;
winMDVersion = null;
}
}
}
string runtimeVersion;
///
/// Gets the WinMD status
///
/// Not thread safe
public WinMDStatus WinMDStatus {
get {
var cval = cachedWinMDStatus;
if (cval is not null)
return cval.Value;
cachedWinMDStatus = cval = CalculateWinMDStatus(RuntimeVersion);
return cval.Value;
}
}
WinMDStatus? cachedWinMDStatus;
///
/// true if this is a WinMD file
///
public bool IsWinMD => WinMDStatus != WinMDStatus.None;
///
/// true if this is a managed WinMD file
///
public bool IsManagedWinMD => WinMDStatus == WinMDStatus.Managed;
///
/// true if this is a pure (non-managed) WinMD file
///
public bool IsPureWinMD => WinMDStatus == WinMDStatus.Pure;
///
/// Gets the CLR runtime version of the managed WinMD file or null if none. This is
/// similar to for normal non-WinMD files.
///
/// Not thread safe
public string RuntimeVersionWinMD {
get {
var rtver = runtimeVersionWinMD;
if (rtver is not null)
return rtver;
runtimeVersionWinMD = rtver = CalculateRuntimeVersionWinMD(RuntimeVersion);
return rtver;
}
}
string runtimeVersionWinMD;
///
/// Gets the WinMD version or null if none
///
/// Not thread safe
public string WinMDVersion {
get {
var ver = winMDVersion;
if (ver is not null)
return ver;
winMDVersion = ver = CalculateWinMDVersion(RuntimeVersion);
return ver;
}
}
string winMDVersion;
static WinMDStatus CalculateWinMDStatus(string version) {
if (version is null)
return WinMDStatus.None;
if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal))
return WinMDStatus.None;
return version.IndexOf(';') < 0 ? WinMDStatus.Pure : WinMDStatus.Managed;
}
static string CalculateRuntimeVersionWinMD(string version) {
// Original parser code:
// CoreCLR file: src/md/winmd/adapter.cpp
// Func: WinMDAdapter::Create(IMDCommon *pRawMDCommon, /*[out]*/ WinMDAdapter **ppAdapter)
if (version is null)
return null;
if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal))
return null;
int index = version.IndexOf(';');
if (index < 0)
return null;
var s = version.Substring(index + 1);
if (s.StartsWith("CLR", StringComparison.OrdinalIgnoreCase))
s = s.Substring(3);
s = s.TrimStart(' ');
return s;
}
static string CalculateWinMDVersion(string version) {
if (version is null)
return null;
if (!version.StartsWith("WindowsRuntime ", StringComparison.Ordinal))
return null;
int index = version.IndexOf(';');
if (index < 0)
return version;
return version.Substring(0, index);
}
///
/// true if is the CLR v1.0 string (only the major
/// and minor version numbers are checked)
///
public bool IsClr10 {
get {
var ver = RuntimeVersion ?? string.Empty;
return ver.StartsWith(MDHeaderRuntimeVersion.MS_CLR_10_PREFIX) ||
ver.StartsWith(MDHeaderRuntimeVersion.MS_CLR_10_PREFIX_X86RETAIL) ||
ver == MDHeaderRuntimeVersion.MS_CLR_10_RETAIL ||
ver == MDHeaderRuntimeVersion.MS_CLR_10_COMPLUS;
}
}
///
/// true if is the CLR v1.0 string
///
public bool IsClr10Exactly =>
RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10 ||
RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_X86RETAIL ||
RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_RETAIL ||
RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_10_COMPLUS;
///
/// true if is the CLR v1.1 string (only the major
/// and minor version numbers are checked)
///
public bool IsClr11 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_11_PREFIX);
///
/// true if is the CLR v1.1 string
///
public bool IsClr11Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_11;
///
/// true if is the CLR v1.0 or v1.1 string (only the
/// major and minor version numbers are checked)
///
public bool IsClr1x => IsClr10 || IsClr11;
///
/// true if is the CLR v1.0 or v1.1 string
///
public bool IsClr1xExactly => IsClr10Exactly || IsClr11Exactly;
///
/// true if is the CLR v2.0 string (only the major
/// and minor version numbers are checked)
///
public bool IsClr20 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_20_PREFIX);
///
/// true if is the CLR v2.0 string
///
public bool IsClr20Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_20;
///
/// true if is the CLR v4.0 string (only the major
/// and minor version numbers are checked)
///
public bool IsClr40 => (RuntimeVersion ?? string.Empty).StartsWith(MDHeaderRuntimeVersion.MS_CLR_40_PREFIX);
///
/// true if is the CLR v4.0 string
///
public bool IsClr40Exactly => RuntimeVersion == MDHeaderRuntimeVersion.MS_CLR_40;
///
/// true if is the ECMA 2002 string
///
public bool IsEcma2002 => RuntimeVersion == MDHeaderRuntimeVersion.ECMA_2002;
///
/// true if is the ECMA 2005 string
///
public bool IsEcma2005 => RuntimeVersion == MDHeaderRuntimeVersion.ECMA_2005;
///
/// Gets/sets the (from PE header)
///
public Machine Machine { get; set; }
///
/// true if is , , ...
///
public bool IsI386 => Machine.IsI386();
///
/// true if is
///
public bool IsIA64 => Machine == Machine.IA64;
///
/// true if is , , ...
///
public bool IsAMD64 => Machine.IsAMD64();
///
/// true if is , , ...
///
public bool IsARM => Machine.IsARMNT();
///
/// true if is , , ...
///
public bool IsARM64 => Machine.IsARM64();
///
/// true if is s390x, , ...
///
public bool IsS390x => Machine.IsS390x();
///
/// Gets/sets the (from .NET header)
///
public ComImageFlags Cor20HeaderFlags {
get => (ComImageFlags)cor20HeaderFlags;
set => cor20HeaderFlags = (int)value;
}
///
protected int cor20HeaderFlags;
///
/// Gets/sets the runtime version number in the COR20 header. The major version is
/// in the high 16 bits. The minor version is in the low 16 bits. This is normally 2.5
/// (0x00020005), but if it's .NET Framework 1.x, it should be 2.0 (0x00020000). If this is
/// null, the default value will be used when saving the module (2.0 if CLR 1.x,
/// and 2.5 if not CLR 1.x).
///
public uint? Cor20HeaderRuntimeVersion { get; set; }
///
/// Gets the tables header version. The major version is in the upper 8 bits and the
/// minor version is in the lower 8 bits. .NET Framework 1.0/1.1 use version 1.0 (0x0100) and
/// .NET Framework 2.x and later use version 2.0 (0x0200). 1.0 has no support for generics,
/// 1.1 has support for generics (GenericParam rows have an extra Kind column),
/// and 2.0 has support for generics (GenericParam rows have the standard 4 columns).
/// No other version is supported. If this is null, the default version is
/// used (1.0 if .NET Framework 1.x, else 2.0).
///
public ushort? TablesHeaderVersion { get; set; }
///
/// Set or clear flags in
///
/// true if flags should be set, false if flags should
/// be cleared
/// Flags to set or clear
void ModifyComImageFlags(bool set, ComImageFlags flags) {
#if THREAD_SAFE
int origVal, newVal;
do {
origVal = cor20HeaderFlags;
if (set)
newVal = origVal | (int)flags;
else
newVal = origVal & ~(int)flags;
} while (Interlocked.CompareExchange(ref cor20HeaderFlags, newVal, origVal) != origVal);
#else
if (set)
cor20HeaderFlags |= (int)flags;
else
cor20HeaderFlags &= ~(int)flags;
#endif
}
///
/// Gets/sets the bit
///
public bool IsILOnly {
get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.ILOnly) != 0;
set => ModifyComImageFlags(value, ComImageFlags.ILOnly);
}
///
/// Gets/sets the bit
///
public bool Is32BitRequired {
get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.Bit32Required) != 0;
set => ModifyComImageFlags(value, ComImageFlags.Bit32Required);
}
///
/// Gets/sets the bit
///
public bool IsStrongNameSigned {
get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.StrongNameSigned) != 0;
set => ModifyComImageFlags(value, ComImageFlags.StrongNameSigned);
}
///
/// Gets/sets the bit
///
public bool HasNativeEntryPoint {
get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.NativeEntryPoint) != 0;
set => ModifyComImageFlags(value, ComImageFlags.NativeEntryPoint);
}
///
/// Gets/sets the bit
///
public bool Is32BitPreferred {
get => ((ComImageFlags)cor20HeaderFlags & ComImageFlags.Bit32Preferred) != 0;
set => ModifyComImageFlags(value, ComImageFlags.Bit32Preferred);
}
///
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Dispose method
///
/// true if called by
protected virtual void Dispose(bool disposing) {
if (!disposing)
return;
var tdf = typeDefFinder;
if (tdf is not null) {
tdf.Dispose();
typeDefFinder = null;
}
pdbState?.Dispose();
pdbState = null;
}
///
/// Gets all the types (including nested types) present in this module
///
public IEnumerable GetTypes() => AllTypesHelper.Types(Types);
///
/// Adds as a non-nested type. If it's already nested, its
/// will be set to null.
///
/// The to insert
public void AddAsNonNestedType(TypeDef typeDef) {
if (typeDef is null)
return;
typeDef.DeclaringType = null;
Types.Add(typeDef);
}
///
/// Updates the rid to the next free rid available. It's only updated if
/// the original rid is 0.
///
/// IMDTokenProvider
/// The row that should be updated
/// Returns the input
public T UpdateRowId(T tableRow) where T : IMDTokenProvider {
if (tableRow != null && tableRow.Rid == 0)
tableRow.Rid = GetNextFreeRid(tableRow.MDToken.Table);
return tableRow;
}
///
/// Updates the rid to the next free rid available.
///
/// IMDTokenProvider
/// The row that should be updated
/// Returns the input
public T ForceUpdateRowId(T tableRow) where T : IMDTokenProvider {
if (tableRow != null)
tableRow.Rid = GetNextFreeRid(tableRow.MDToken.Table);
return tableRow;
}
uint GetNextFreeRid(Table table) {
var lastUsedRids = this.lastUsedRids;
if ((uint)table >= lastUsedRids.Length)
return 0;
return (uint)Interlocked.Increment(ref lastUsedRids[(int)table]) & 0x00FFFFFF;
}
///
/// Imports a as a
///
/// The type
/// The imported type or null if is invalid
public ITypeDefOrRef Import(Type type) => new Importer(this).Import(type);
///
/// Imports a as a
///
/// The type
/// The imported type or null if is invalid
public TypeSig ImportAsTypeSig(Type type) => new Importer(this).ImportAsTypeSig(type);
///
/// Imports a as a
///
/// The field
/// The imported field or null if is invalid
/// or if we failed to import the field
public MemberRef Import(FieldInfo fieldInfo) => (MemberRef)new Importer(this).Import(fieldInfo);
///
/// Imports a as a . This will be either
/// a or a .
///
/// The method
/// The imported method or null if is invalid
/// or if we failed to import the method
public IMethod Import(MethodBase methodBase) => new Importer(this).Import(methodBase);
///
/// Imports a
///
/// The type
/// The imported type or null
public IType Import(IType type) => new Importer(this).Import(type);
///
/// Imports a as a
///
/// The type
/// The imported type or null
public TypeRef Import(TypeDef type) => (TypeRef)new Importer(this).Import(type);
///
/// Imports a
///
/// The type
/// The imported type or null
public TypeRef Import(TypeRef type) => (TypeRef)new Importer(this).Import(type);
///
/// Imports a
///
/// The type
/// The imported type or null
public TypeSpec Import(TypeSpec type) => new Importer(this).Import(type);
///
/// Imports a
///
/// The type
/// The imported type or null
public TypeSig Import(TypeSig type) => new Importer(this).Import(type);
///
/// Imports a
///
/// The field
/// The imported type or null if is invalid
public MemberRef Import(IField field) => (MemberRef)new Importer(this).Import(field);
///
/// Imports a as a
///
/// The field
/// The imported type or null if is invalid
public MemberRef Import(FieldDef field) => (MemberRef)new Importer(this).Import(field);
///
/// Imports a
///
/// The method
/// The imported method or null if is invalid
public IMethod Import(IMethod method) => new Importer(this).Import(method);
///
/// Imports a as a
///
/// The method
/// The imported method or null if is invalid
public MemberRef Import(MethodDef method) => (MemberRef)new Importer(this).Import(method);
///
/// Imports a
///
/// The method
/// The imported method or null if is invalid
public MethodSpec Import(MethodSpec method) => new Importer(this).Import(method);
///
/// Imports a
///
/// The member ref
/// The imported member ref or null if is invalid
public MemberRef Import(MemberRef memberRef) => new Importer(this).Import(memberRef);
///
/// Writes the module to a file on disk. If the file exists, it will be overwritten.
///
/// Filename
public void Write(string filename) => Write(filename, null);
///
/// Writes the module to a file on disk. If the file exists, it will be overwritten.
///
/// Filename
/// Writer options
public void Write(string filename, ModuleWriterOptions options) {
var writer = new ModuleWriter(this, options ?? new ModuleWriterOptions(this));
writer.Write(filename);
}
///
/// Writes the module to a stream.
///
/// Destination stream
public void Write(Stream dest) => Write(dest, null);
///
/// Writes the module to a stream.
///
/// Destination stream
/// Writer options
public void Write(Stream dest, ModuleWriterOptions options) {
var writer = new ModuleWriter(this, options ?? new ModuleWriterOptions(this));
writer.Write(dest);
}
///
/// Resets the cache which can be enabled by setting
/// to true. Use this method if the cache is
/// enabled but some of the types have been modified (eg. removed, added, renamed).
///
public void ResetTypeDefFindCache() => TypeDefFinder.ResetCache();
///
/// Finds a
///
/// Type
/// Name
/// Language ID
/// The or null if none found
public ResourceData FindWin32ResourceData(ResourceName type, ResourceName name, ResourceName langId) => Win32Resources?.Find(type, name, langId);
///
/// Creates a new
///
/// PDB file kind
public void CreatePdbState(PdbFileKind pdbFileKind) => SetPdbState(new PdbState(this, pdbFileKind));
///
/// Sets a
///
/// New
public void SetPdbState(PdbState pdbState) {
if (pdbState is null)
throw new ArgumentNullException(nameof(pdbState));
var orig = Interlocked.CompareExchange(ref this.pdbState, pdbState, null);
if (orig is not null)
throw new InvalidOperationException("PDB file has already been initialized");
}
uint GetCor20RuntimeVersion() {
var rtVer = Cor20HeaderRuntimeVersion;
if (rtVer is not null)
return rtVer.Value;
return IsClr1x ? 0x00020000U : 0x00020005;
}
///
/// Returns the size of a pointer. Assumes it's 32-bit if pointer size is unknown or
/// if it can be 32-bit or 64-bit.
///
/// Size of a pointer (4 or 8)
public int GetPointerSize() => GetPointerSize(4);
///
/// Returns the size of a pointer
///
/// Default pointer size if it's not known or if it
/// can be 32-bit or 64-bit
/// Size of a pointer (4 or 8)
public int GetPointerSize(int defaultPointerSize) => GetPointerSize(defaultPointerSize, defaultPointerSize);
///
/// Returns the size of a pointer
///
/// Default pointer size
/// Pointer size if it's prefer-32-bit (should usually be 4)
///
public int GetPointerSize(int defaultPointerSize, int prefer32bitPointerSize) {
var machine = Machine;
if (machine.Is64Bit())
return 8;
if (!machine.IsI386())
return 4;
// Machine is I386 so it's either x86 or platform neutral
// If the runtime version is < 2.5, then it's always loaded as a 32-bit process.
if (GetCor20RuntimeVersion() < 0x00020005)
return 4;
// If it's a 32-bit PE header, and ILOnly is cleared, it's always loaded as a
// 32-bit process.
var flags = (ComImageFlags)cor20HeaderFlags;
if ((flags & ComImageFlags.ILOnly) == 0)
return 4;
// 32-bit Preferred flag is new in .NET Framework 4.5. See CorHdr.h in Windows SDK for more info
switch (flags & (ComImageFlags.Bit32Required | ComImageFlags.Bit32Preferred)) {
case 0:
// Machine and ILOnly flag should be checked
break;
case ComImageFlags.Bit32Preferred:
// Illegal
break;
case ComImageFlags.Bit32Required:
// x86 image (32-bit process)
return 4;
case ComImageFlags.Bit32Required | ComImageFlags.Bit32Preferred:
// Platform neutral but prefers to be 32-bit
return prefer32bitPointerSize;
}
return defaultPointerSize;
}
///
void IListListener.OnLazyAdd(int index, ref TypeDef value) {
#if DEBUG
if (value.DeclaringType is not null)
throw new InvalidOperationException("Added type's DeclaringType is not null");
#endif
value.Module2 = this;
}
///
void IListListener.OnAdd(int index, TypeDef value) {
if (value.DeclaringType is not null)
throw new InvalidOperationException("Nested type is already owned by another type. Set DeclaringType to null first.");
if (value.Module is not null)
throw new InvalidOperationException("Type is already owned by another module. Remove it from that module's type list.");
value.Module2 = this;
}
///
void IListListener.OnRemove(int index, TypeDef value) => value.Module2 = null;
///
void IListListener.OnResize(int index) {
}
///
void IListListener.OnClear() {
foreach (var type in types.GetEnumerable_NoLock())
type.Module2 = null;
}
///
/// Finds a . For speed, enable
/// if possible (read the documentation first).
///
/// Full name of the type (no assembly information)
/// true if it's a reflection name, and nested
/// type names are separated by a + character. If false, nested type names
/// are separated by a / character.
/// An existing or null if it wasn't found.
public TypeDef Find(string fullName, bool isReflectionName) => TypeDefFinder.Find(fullName, isReflectionName);
///
/// Finds a . Its scope (i.e., module or assembly) is ignored when
/// looking up the type. For speed, enable if possible
/// (read the documentation first).
///
/// The type ref
/// An existing or null if it wasn't found.
public TypeDef Find(TypeRef typeRef) => TypeDefFinder.Find(typeRef);
///
/// Finds a
///
/// The type
/// A or null if it wasn't found
public TypeDef Find(ITypeDefOrRef typeRef) {
if (typeRef is TypeDef td)
return td.Module == this ? td : null;
if (typeRef is TypeRef tr)
return Find(tr);
var ts = typeRef as TypeSpec;
if (ts is null)
return null;
var sig = ts.TypeSig as TypeDefOrRefSig;
if (sig is null)
return null;
td = sig.TypeDef;
if (td is not null)
return td.Module == this ? td : null;
tr = sig.TypeRef;
if (tr is not null)
return Find(tr);
return null;
}
///
/// Creates a new instance. There should normally only be one
/// instance shared by all s.
///
/// A new instance
public static ModuleContext CreateModuleContext() {
var ctx = new ModuleContext();
var asmRes = new AssemblyResolver(ctx);
var res = new Resolver(asmRes);
ctx.AssemblyResolver = asmRes;
ctx.Resolver = res;
asmRes.DefaultModuleContext = ctx;
return ctx;
}
///
/// Load everything in this module. All types, fields, asm refs, etc are loaded, all their
/// properties are read to make sure everything is cached.
///
/// Cancellation token or null
public virtual void LoadEverything(ICancellationToken cancellationToken = null) => ModuleLoader.LoadAll(this, cancellationToken);
///
public override string ToString() => FullName;
///
/// Resolves a token
///
/// The metadata token
/// A or null if is invalid
public IMDTokenProvider ResolveToken(MDToken mdToken) => ResolveToken(mdToken.Raw, new GenericParamContext());
///
/// Resolves a token
///
/// The metadata token
/// Generic parameter context
/// A or null if is invalid
public IMDTokenProvider ResolveToken(MDToken mdToken, GenericParamContext gpContext) => ResolveToken(mdToken.Raw, gpContext);
///
/// Resolves a token
///
/// The metadata token
/// A or null if is invalid
public IMDTokenProvider ResolveToken(int token) => ResolveToken((uint)token, new GenericParamContext());
///
/// Resolves a token
///
/// The metadata token
/// Generic parameter context
/// A or null if is invalid
public IMDTokenProvider ResolveToken(int token, GenericParamContext gpContext) => ResolveToken((uint)token, gpContext);
///
/// Resolves a token
///
/// The metadata token
/// A or null if is invalid
public IMDTokenProvider ResolveToken(uint token) => ResolveToken(token, new GenericParamContext());
///
/// Resolves a token
///
/// The metadata token
/// Generic parameter context
/// A or null if is invalid
public virtual IMDTokenProvider ResolveToken(uint token, GenericParamContext gpContext) => null;
///
/// Gets all s
///
public IEnumerable GetAssemblyRefs() {
for (uint rid = 1; ; rid++) {
var asmRef = ResolveToken(new MDToken(Table.AssemblyRef, rid).Raw) as AssemblyRef;
if (asmRef is null)
break;
yield return asmRef;
}
}
///
/// Gets all s
///
public IEnumerable GetModuleRefs() {
for (uint rid = 1; ; rid++) {
var modRef = ResolveToken(new MDToken(Table.ModuleRef, rid).Raw) as ModuleRef;
if (modRef is null)
break;
yield return modRef;
}
}
///
/// Gets all s. s with generic parameters
/// aren't cached and a new copy is always returned.
///
public IEnumerable GetMemberRefs() => GetMemberRefs(new GenericParamContext());
///
/// Gets all s. s with generic parameters
/// aren't cached and a new copy is always returned.
///
/// Generic parameter context
public IEnumerable GetMemberRefs(GenericParamContext gpContext) {
for (uint rid = 1; ; rid++) {
var mr = ResolveToken(new MDToken(Table.MemberRef, rid).Raw, gpContext) as MemberRef;
if (mr is null)
break;
yield return mr;
}
}
///
/// Gets all s
///
public IEnumerable GetTypeRefs() {
for (uint rid = 1; ; rid++) {
var mr = ResolveToken(new MDToken(Table.TypeRef, rid).Raw) as TypeRef;
if (mr is null)
break;
yield return mr;
}
}
///
/// Finds an assembly reference by name. If there's more than one, pick the one with
/// the greatest version number.
///
/// Simple name of assembly (eg. "mscorlib")
/// The found or null if there's no such
/// assembly reference.
public AssemblyRef GetAssemblyRef(UTF8String simpleName) {
AssemblyRef found = null;
foreach (var asmRef in GetAssemblyRefs()) {
if (asmRef.Name != simpleName)
continue;
if (IsGreaterAssemblyRefVersion(found, asmRef))
found = asmRef;
}
return found;
}
///
/// Compare asm refs' version
///
/// First asm ref
/// New asm ref
///
protected static bool IsGreaterAssemblyRefVersion(AssemblyRef found, AssemblyRef newOne) {
if (found is null)
return true;
var foundVer = found.Version;
var newVer = newOne.Version;
return foundVer is null || (newVer is not null && newVer >= foundVer);
}
ITypeDefOrRef ISignatureReaderHelper.ResolveTypeDefOrRef(uint codedToken, GenericParamContext gpContext) {
if (!CodedToken.TypeDefOrRef.Decode(codedToken, out uint token))
return null;
return ResolveToken(token) as ITypeDefOrRef;
}
TypeSig ISignatureReaderHelper.ConvertRTInternalAddress(IntPtr address) => null;
}
///
/// A Module row created by the user and not present in the original .NET file
///
public class ModuleDefUser : ModuleDef {
///
/// Default constructor
///
public ModuleDefUser()
: this(null, null) {
}
///
/// Constructor
///
/// is initialized to a random
/// Module nam
public ModuleDefUser(UTF8String name)
: this(name, Guid.NewGuid()) {
}
///
/// Constructor
///
/// Module name
/// Module version ID
public ModuleDefUser(UTF8String name, Guid? mvid)
: this(name, mvid, null) {
}
///
/// Constructor
///
/// Module name
/// Module version ID
/// Corlib assembly ref or null
public ModuleDefUser(UTF8String name, Guid? mvid, AssemblyRef corLibAssemblyRef) {
Kind = ModuleKind.Windows;
Characteristics = DefaultCharacteristics;
DllCharacteristics = DefaultDllCharacteristics;
RuntimeVersion = MDHeaderRuntimeVersion.MS_CLR_20;
Machine = Machine.I386;
cor20HeaderFlags = (int)ComImageFlags.ILOnly;
Cor20HeaderRuntimeVersion = 0x00020005; // .NET Framework 2.0 or later should use 2.5
TablesHeaderVersion = 0x0200; // .NET Framework 2.0 or later should use 2.0
types = new LazyList(this);
exportedTypes = new LazyList();
resources = new ResourceCollection();
corLibTypes = new CorLibTypes(this, corLibAssemblyRef);
types = new LazyList(this);
this.name = name;
this.mvid = mvid;
types.Add(CreateModuleType());
UpdateRowId(this);
}
TypeDef CreateModuleType() {
var type = UpdateRowId(new TypeDefUser(UTF8String.Empty, "", null));
type.Attributes = TypeAttributes.NotPublic | TypeAttributes.AutoLayout | TypeAttributes.Class | TypeAttributes.AnsiClass;
return type;
}
}
///
/// Created from a row in the Module table
///
public class ModuleDefMD2 : ModuleDef, IMDTokenProviderMD {
/// The module where this instance is located
readonly ModuleDefMD readerModule;
readonly uint origRid;
///
public uint OrigRid => origRid;
///
protected override void InitializeCustomAttributes() {
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Module, origRid);
var tmp = new CustomAttributeCollection(list.Count, list, (list2, index) => readerModule.ReadCustomAttribute(list[index]));
Interlocked.CompareExchange(ref customAttributes, tmp, null);
}
///
protected override void InitializeCustomDebugInfos() {
var list = new List();
readerModule.InitializeCustomDebugInfos(new MDToken(MDToken.Table, origRid), new GenericParamContext(), list);
Interlocked.CompareExchange(ref customDebugInfos, list, null);
}
///
protected override RVA GetNativeEntryPoint_NoLock() => readerModule.GetNativeEntryPoint();
///
protected override IManagedEntryPoint GetManagedEntryPoint_NoLock() => readerModule.GetManagedEntryPoint();
///
/// Constructor
///
/// The module which contains this Module row
/// Row ID
/// If is null
/// If is invalid
internal ModuleDefMD2(ModuleDefMD readerModule, uint rid) {
if (rid == 1 && readerModule is null)
readerModule = (ModuleDefMD)this;
#if DEBUG
if (readerModule is null)
throw new ArgumentNullException("readerModule");
if (rid != 1 && readerModule.TablesStream.ModuleTable.IsInvalidRID(rid))
throw new BadImageFormatException($"Module rid {rid} does not exist");
#endif
origRid = rid;
this.rid = rid;
this.readerModule = readerModule;
if (rid != 1) {
Kind = ModuleKind.Windows;
Characteristics = DefaultCharacteristics;
DllCharacteristics = DefaultDllCharacteristics;
RuntimeVersion = MDHeaderRuntimeVersion.MS_CLR_20;
Machine = Machine.I386;
cor20HeaderFlags = (int)ComImageFlags.ILOnly;
Cor20HeaderRuntimeVersion = 0x00020005; // .NET Framework 2.0 or later should use 2.5
TablesHeaderVersion = 0x0200; // .NET Framework 2.0 or later should use 2.0
corLibTypes = new CorLibTypes(this);
location = string.Empty;
InitializeFromRawRow();
}
}
///
/// Initialize fields from the raw Module row
///
protected void InitializeFromRawRow() {
bool b = readerModule.TablesStream.TryReadModuleRow(origRid, out var row);
Debug.Assert(b);
generation = row.Generation;
mvid = readerModule.GuidStream.Read(row.Mvid);
encId = readerModule.GuidStream.Read(row.EncId);
encBaseId = readerModule.GuidStream.Read(row.EncBaseId);
name = readerModule.StringsStream.ReadNoNull(row.Name);
if (origRid == 1)
assembly = readerModule.ResolveAssembly(origRid);
}
}
}