// 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); } } }