// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using dnlib.DotNet.MD;
using dnlib.DotNet.Pdb;
using dnlib.PE;
using dnlib.Threading;
namespace dnlib.DotNet {
///
/// A high-level representation of a row in the Field table
///
public abstract class FieldDef : IHasConstant, IHasCustomAttribute, IHasFieldMarshal, IMemberForwarded, IHasCustomDebugInformation, IField, ITokenOperand, IMemberDef {
///
/// The row id in its table
///
protected uint rid;
#if THREAD_SAFE
readonly Lock theLock = Lock.Create();
#endif
///
public MDToken MDToken => new MDToken(Table.Field, rid);
///
public uint Rid {
get => rid;
set => rid = value;
}
///
public int HasConstantTag => 0;
///
public int HasCustomAttributeTag => 1;
///
public int HasFieldMarshalTag => 0;
///
public int MemberForwardedTag => 0;
///
/// 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 => 1;
///
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);
///
/// From column Field.Flags
///
public FieldAttributes Attributes {
get => (FieldAttributes)attributes;
set => attributes = (int)value;
}
/// Attributes
protected int attributes;
///
/// From column Field.Name
///
public UTF8String Name {
get => name;
set => name = value;
}
/// Name
protected UTF8String name;
///
/// From column Field.Signature
///
public CallingConventionSig Signature {
get => signature;
set => signature = value;
}
///
protected CallingConventionSig signature;
///
/// Gets/sets the field layout offset
///
public uint? FieldOffset {
get {
if (!fieldOffset_isInitialized)
InitializeFieldOffset();
return fieldOffset;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
fieldOffset = value;
fieldOffset_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected uint? fieldOffset;
///
protected bool fieldOffset_isInitialized;
void InitializeFieldOffset() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (fieldOffset_isInitialized)
return;
fieldOffset = GetFieldOffset_NoLock();
fieldOffset_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual uint? GetFieldOffset_NoLock() => null;
///
public MarshalType MarshalType {
get {
if (!marshalType_isInitialized)
InitializeMarshalType();
return marshalType;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
marshalType = value;
marshalType_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected MarshalType marshalType;
///
protected bool marshalType_isInitialized;
void InitializeMarshalType() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (marshalType_isInitialized)
return;
marshalType = GetMarshalType_NoLock();
marshalType_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual MarshalType GetMarshalType_NoLock() => null;
/// Reset
protected void ResetMarshalType() =>
marshalType_isInitialized = false;
///
/// Gets/sets the field RVA
///
public RVA RVA {
get {
if (!rva_isInitialized)
InitializeRVA();
return rva;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
rva = value;
rva_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected RVA rva;
///
protected bool rva_isInitialized;
void InitializeRVA() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (rva_isInitialized)
return;
rva = GetRVA_NoLock();
rva_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual RVA GetRVA_NoLock() => 0;
/// Reset
protected void ResetRVA() => rva_isInitialized = false;
///
/// Gets/sets the initial value. Be sure to set to true if
/// you write to this field.
///
public byte[] InitialValue {
get {
if (!initialValue_isInitialized)
InitializeInitialValue();
return initialValue;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
initialValue = value;
initialValue_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected byte[] initialValue;
///
protected bool initialValue_isInitialized;
void InitializeInitialValue() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (initialValue_isInitialized)
return;
initialValue = GetInitialValue_NoLock();
initialValue_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual byte[] GetInitialValue_NoLock() => null;
/// Reset
protected void ResetInitialValue() => initialValue_isInitialized = false;
///
public ImplMap ImplMap {
get {
if (!implMap_isInitialized)
InitializeImplMap();
return implMap;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
implMap = value;
implMap_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected ImplMap implMap;
///
protected bool implMap_isInitialized;
void InitializeImplMap() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (implMap_isInitialized)
return;
implMap = GetImplMap_NoLock();
implMap_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual ImplMap GetImplMap_NoLock() => null;
///
public Constant Constant {
get {
if (!constant_isInitialized)
InitializeConstant();
return constant;
}
set {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
constant = value;
constant_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
}
///
protected Constant constant;
///
protected bool constant_isInitialized;
void InitializeConstant() {
#if THREAD_SAFE
theLock.EnterWriteLock(); try {
#endif
if (constant_isInitialized)
return;
constant = GetConstant_NoLock();
constant_isInitialized = true;
#if THREAD_SAFE
} finally { theLock.ExitWriteLock(); }
#endif
}
/// Called to initialize
protected virtual Constant GetConstant_NoLock() => null;
/// Reset
protected void ResetConstant() => constant_isInitialized = false;
///
public bool HasCustomAttributes => CustomAttributes.Count > 0;
///
public bool HasImplMap => ImplMap is not null;
///
/// Gets/sets the declaring type (owner type)
///
public TypeDef DeclaringType {
get => declaringType2;
set {
var currentDeclaringType = DeclaringType2;
if (currentDeclaringType == value)
return;
if (currentDeclaringType is not null)
currentDeclaringType.Fields.Remove(this); // Will set DeclaringType2 = null
if (value is not null)
value.Fields.Add(this); // Will set DeclaringType2 = value
}
}
///
ITypeDefOrRef IMemberRef.DeclaringType => declaringType2;
///
/// Called by and should normally not be called by any user
/// code. Use instead. Only call this if you must set the
/// declaring type without inserting it in the declaring type's method list.
///
public TypeDef DeclaringType2 {
get => declaringType2;
set => declaringType2 = value;
}
///
protected TypeDef declaringType2;
///
/// Gets/sets the
///
public FieldSig FieldSig {
get => signature as FieldSig;
set => signature = value;
}
///
public ModuleDef Module => declaringType2?.Module;
bool IIsTypeOrMethod.IsType => false;
bool IIsTypeOrMethod.IsMethod => false;
bool IMemberRef.IsField => true;
bool IMemberRef.IsTypeSpec => false;
bool IMemberRef.IsTypeRef => false;
bool IMemberRef.IsTypeDef => false;
bool IMemberRef.IsMethodSpec => false;
bool IMemberRef.IsMethodDef => false;
bool IMemberRef.IsMemberRef => false;
bool IMemberRef.IsFieldDef => true;
bool IMemberRef.IsPropertyDef => false;
bool IMemberRef.IsEventDef => false;
bool IMemberRef.IsGenericParam => false;
///
/// true if is not null
///
public bool HasLayoutInfo => FieldOffset is not null;
///
/// true if is not null
///
public bool HasConstant => Constant is not null;
///
/// Gets the constant element type or if there's no constant
///
public ElementType ElementType {
get {
var c = Constant;
return c is null ? ElementType.End : c.Type;
}
}
///
/// true if is not null
///
public bool HasMarshalType => MarshalType is not null;
///
/// Gets/sets the field type
///
public TypeSig FieldType {
get => FieldSig.GetFieldType();
set {
var sig = FieldSig;
if (sig is not null)
sig.Type = value;
}
}
///
/// Modify field: =
/// ( & ) | .
///
/// Value to AND
/// Value to OR
void ModifyAttributes(FieldAttributes andMask, FieldAttributes orMask) =>
attributes = (attributes & (int)andMask) | (int)orMask;
///
/// Set or clear flags in
///
/// true if flags should be set, false if flags should
/// be cleared
/// Flags to set or clear
void ModifyAttributes(bool set, FieldAttributes flags) {
if (set)
attributes |= (int)flags;
else
attributes &= ~(int)flags;
}
///
/// Gets/sets the field access
///
public FieldAttributes Access {
get => (FieldAttributes)attributes & FieldAttributes.FieldAccessMask;
set => ModifyAttributes(~FieldAttributes.FieldAccessMask, value & FieldAttributes.FieldAccessMask);
}
///
/// true if is set
///
public bool IsCompilerControlled => IsPrivateScope;
///
/// true if is set
///
public bool IsPrivateScope => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.PrivateScope;
///
/// true if is set
///
public bool IsPrivate => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Private;
///
/// true if is set
///
public bool IsFamilyAndAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamANDAssem;
///
/// true if is set
///
public bool IsAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Assembly;
///
/// true if is set
///
public bool IsFamily => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Family;
///
/// true if is set
///
public bool IsFamilyOrAssembly => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.FamORAssem;
///
/// true if is set
///
public bool IsPublic => ((FieldAttributes)attributes & FieldAttributes.FieldAccessMask) == FieldAttributes.Public;
///
/// Gets/sets the bit
///
public bool IsStatic {
get => ((FieldAttributes)attributes & FieldAttributes.Static) != 0;
set => ModifyAttributes(value, FieldAttributes.Static);
}
///
/// Gets/sets the bit
///
public bool IsInitOnly {
get => ((FieldAttributes)attributes & FieldAttributes.InitOnly) != 0;
set => ModifyAttributes(value, FieldAttributes.InitOnly);
}
///
/// Gets/sets the bit
///
public bool IsLiteral {
get => ((FieldAttributes)attributes & FieldAttributes.Literal) != 0;
set => ModifyAttributes(value, FieldAttributes.Literal);
}
///
/// Gets/sets the bit
///
public bool IsNotSerialized {
get => ((FieldAttributes)attributes & FieldAttributes.NotSerialized) != 0;
set => ModifyAttributes(value, FieldAttributes.NotSerialized);
}
///
/// Gets/sets the bit
///
public bool IsSpecialName {
get => ((FieldAttributes)attributes & FieldAttributes.SpecialName) != 0;
set => ModifyAttributes(value, FieldAttributes.SpecialName);
}
///
/// Gets/sets the bit
///
public bool IsPinvokeImpl {
get => ((FieldAttributes)attributes & FieldAttributes.PinvokeImpl) != 0;
set => ModifyAttributes(value, FieldAttributes.PinvokeImpl);
}
///
/// Gets/sets the bit
///
public bool IsRuntimeSpecialName {
get => ((FieldAttributes)attributes & FieldAttributes.RTSpecialName) != 0;
set => ModifyAttributes(value, FieldAttributes.RTSpecialName);
}
///
/// Gets/sets the bit
///
public bool HasFieldMarshal {
get => ((FieldAttributes)attributes & FieldAttributes.HasFieldMarshal) != 0;
set => ModifyAttributes(value, FieldAttributes.HasFieldMarshal);
}
///
/// Gets/sets the bit
///
public bool HasDefault {
get => ((FieldAttributes)attributes & FieldAttributes.HasDefault) != 0;
set => ModifyAttributes(value, FieldAttributes.HasDefault);
}
///
/// Gets/sets the bit
///
public bool HasFieldRVA {
get => ((FieldAttributes)attributes & FieldAttributes.HasFieldRVA) != 0;
set => ModifyAttributes(value, FieldAttributes.HasFieldRVA);
}
///
/// Returns the full name of this field
///
public string FullName => FullNameFactory.FieldFullName(declaringType2?.FullName, name, FieldSig, null, null);
///
/// Gets the size of this field in bytes or 0 if unknown.
///
public uint GetFieldSize() {
if (!GetFieldSize(out uint size))
return 0;
return size;
}
///
/// Gets the size of this field in bytes or 0 if unknown.
///
/// Updated with size
/// true if is valid, false otherwise
public bool GetFieldSize(out uint size) => GetFieldSize(declaringType2, FieldSig, out size);
///
/// Gets the size of this field in bytes or 0 if unknown.
///
/// The declaring type of this
/// The field signature of this
/// Updated with size
/// true if is valid, false otherwise
protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, out uint size) => GetFieldSize(declaringType, fieldSig, GetPointerSize(declaringType), out size);
///
/// Gets the size of this field in bytes or 0 if unknown.
///
/// The declaring type of this
/// The field signature of this
/// Size of a pointer
/// Updated with size
/// true if is valid, false otherwise
protected bool GetFieldSize(TypeDef declaringType, FieldSig fieldSig, int ptrSize, out uint size) {
size = 0;
if (fieldSig is null)
return false;
return GetClassSize(declaringType, fieldSig.Type, ptrSize, out size);
}
bool GetClassSize(TypeDef declaringType, TypeSig ts, int ptrSize, out uint size) {
size = 0;
ts = ts.RemovePinnedAndModifiers();
if (ts is null)
return false;
int size2 = ts.ElementType.GetPrimitiveSize(ptrSize);
if (size2 >= 0) {
size = (uint)size2;
return true;
}
var tdrs = ts as TypeDefOrRefSig;
if (tdrs is null)
return false;
var td = tdrs.TypeDef;
if (td is not null)
return TypeDef.GetClassSize(td, out size);
var tr = tdrs.TypeRef;
if (tr is not null)
return TypeDef.GetClassSize(tr.Resolve(), out size);
return false;
}
int GetPointerSize(TypeDef declaringType) {
if (declaringType is null)
return 4;
var module = declaringType.Module;
if (module is null)
return 4;
return module.GetPointerSize();
}
///
public override string ToString() => FullName;
}
///
/// A Field row created by the user and not present in the original .NET file
///
public class FieldDefUser : FieldDef {
///
/// Default constructor
///
public FieldDefUser() {
}
///
/// Constructor
///
/// Name
public FieldDefUser(UTF8String name)
: this(name, null) {
}
///
/// Constructor
///
/// Name
/// Signature
public FieldDefUser(UTF8String name, FieldSig signature)
: this(name, signature, 0) {
}
///
/// Constructor
///
/// Name
/// Signature
/// Flags
public FieldDefUser(UTF8String name, FieldSig signature, FieldAttributes attributes) {
this.name = name;
this.signature = signature;
this.attributes = (int)attributes;
}
}
///
/// Created from a row in the Field table
///
sealed class FieldDefMD : FieldDef, IMDTokenProviderMD {
/// The module where this instance is located
readonly ModuleDefMD readerModule;
readonly uint origRid;
readonly FieldAttributes origAttributes;
///
public uint OrigRid => origRid;
///
protected override void InitializeCustomAttributes() {
var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Field, 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(declaringType2), list);
Interlocked.CompareExchange(ref customDebugInfos, list, null);
}
///
protected override uint? GetFieldOffset_NoLock() {
if (readerModule.TablesStream.TryReadFieldLayoutRow(readerModule.Metadata.GetFieldLayoutRid(origRid), out var row))
return row.OffSet;
return null;
}
///
protected override MarshalType GetMarshalType_NoLock() =>
readerModule.ReadMarshalType(Table.Field, origRid, new GenericParamContext(declaringType2));
///
protected override RVA GetRVA_NoLock() {
GetFieldRVA_NoLock(out var rva2);
return rva2;
}
///
protected override byte[] GetInitialValue_NoLock() {
if (!GetFieldRVA_NoLock(out var rva2))
return null;
return ReadInitialValue_NoLock(rva2);
}
///
protected override ImplMap GetImplMap_NoLock() =>
readerModule.ResolveImplMap(readerModule.Metadata.GetImplMapRid(Table.Field, origRid));
///
protected override Constant GetConstant_NoLock() =>
readerModule.ResolveConstant(readerModule.Metadata.GetConstantRid(Table.Field, origRid));
///
/// Constructor
///
/// The module which contains this Field row
/// Row ID
/// If is null
/// If is invalid
public FieldDefMD(ModuleDefMD readerModule, uint rid) {
#if DEBUG
if (readerModule is null)
throw new ArgumentNullException("readerModule");
if (readerModule.TablesStream.FieldTable.IsInvalidRID(rid))
throw new BadImageFormatException($"Field rid {rid} does not exist");
#endif
origRid = rid;
this.rid = rid;
this.readerModule = readerModule;
bool b = readerModule.TablesStream.TryReadFieldRow(origRid, out var row);
Debug.Assert(b);
name = readerModule.StringsStream.ReadNoNull(row.Name);
attributes = row.Flags;
origAttributes = (FieldAttributes)attributes;
declaringType2 = readerModule.GetOwnerType(this);
signature = readerModule.ReadSignature(row.Signature, new GenericParamContext(declaringType2));
}
internal FieldDefMD InitializeAll() {
MemberMDInitializer.Initialize(CustomAttributes);
MemberMDInitializer.Initialize(Attributes);
MemberMDInitializer.Initialize(Name);
MemberMDInitializer.Initialize(Signature);
MemberMDInitializer.Initialize(FieldOffset);
MemberMDInitializer.Initialize(MarshalType);
MemberMDInitializer.Initialize(RVA);
MemberMDInitializer.Initialize(InitialValue);
MemberMDInitializer.Initialize(ImplMap);
MemberMDInitializer.Initialize(Constant);
MemberMDInitializer.Initialize(DeclaringType);
return this;
}
bool GetFieldRVA_NoLock(out RVA rva) {
if ((origAttributes & FieldAttributes.HasFieldRVA) == 0) {
rva = 0;
return false;
}
if (!readerModule.TablesStream.TryReadFieldRVARow(readerModule.Metadata.GetFieldRVARid(origRid), out var row)) {
rva = 0;
return false;
}
rva = (RVA)row.RVA;
return true;
}
byte[] ReadInitialValue_NoLock(RVA rva) {
if (!GetFieldSize(declaringType2, signature as FieldSig, out uint size))
return null;
if (size >= int.MaxValue)
return null;
return readerModule.ReadDataAt(rva, (int)size);
}
}
}