// 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.Threading; namespace dnlib.DotNet { /// /// A high-level representation of a row in the Property table /// public abstract class PropertyDef : IHasConstant, IHasCustomAttribute, IHasSemantic, IHasCustomDebugInformation, IFullName, 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.Property, rid); /// public uint Rid { get => rid; set => rid = value; } /// public int HasConstantTag => 2; /// public int HasCustomAttributeTag => 9; /// public int HasSemanticTag => 1; /// /// From column Property.PropFlags /// public PropertyAttributes Attributes { get => (PropertyAttributes)attributes; set => attributes = (int)value; } /// Attributes protected int attributes; /// /// From column Property.Name /// public UTF8String Name { get => name; set => name = value; } /// Name protected UTF8String name; /// /// From column Property.Type /// public CallingConventionSig Type { get => type; set => type = value; } /// protected CallingConventionSig type; /// 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; /// /// 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 => 9; /// 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/sets the first getter method. Writing null will clear all get methods. /// public MethodDef GetMethod { get { if (otherMethods is null) InitializePropertyMethods(); return getMethods.Count == 0 ? null : getMethods[0]; } set { if (otherMethods is null) InitializePropertyMethods(); if (value is null) getMethods.Clear(); else if (getMethods.Count == 0) getMethods.Add(value); else getMethods[0] = value; } } /// /// Gets/sets the first setter method. Writing null will clear all set methods. /// public MethodDef SetMethod { get { if (otherMethods is null) InitializePropertyMethods(); return setMethods.Count == 0 ? null : setMethods[0]; } set { if (otherMethods is null) InitializePropertyMethods(); if (value is null) setMethods.Clear(); else if (setMethods.Count == 0) setMethods.Add(value); else setMethods[0] = value; } } /// /// Gets all getter methods /// public IList GetMethods { get { if (otherMethods is null) InitializePropertyMethods(); return getMethods; } } /// /// Gets all setter methods /// public IList SetMethods { get { if (otherMethods is null) InitializePropertyMethods(); return setMethods; } } /// /// Gets the other methods /// public IList OtherMethods { get { if (otherMethods is null) InitializePropertyMethods(); return otherMethods; } } void InitializePropertyMethods() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif if (otherMethods is null) InitializePropertyMethods_NoLock(); #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } /// /// Initializes , , /// and . /// protected virtual void InitializePropertyMethods_NoLock() { getMethods = new List(); setMethods = new List(); otherMethods = new List(); } /// protected IList getMethods; /// protected IList setMethods; /// protected IList otherMethods; /// Reset , , protected void ResetMethods() => otherMethods = null; /// /// true if there are no methods attached to this property /// public bool IsEmpty => // The first property access initializes the other fields we access here GetMethods.Count == 0 && setMethods.Count == 0 && otherMethods.Count == 0; /// public bool HasCustomAttributes => CustomAttributes.Count > 0; /// /// true if is not empty /// public bool HasOtherMethods => OtherMethods.Count > 0; /// /// 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; } } /// /// Gets/sets the property sig /// public PropertySig PropertySig { get => type as PropertySig; set => type = value; } /// /// 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.Properties.Remove(this); // Will set DeclaringType2 = null if (value is not null) value.Properties.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; /// public ModuleDef Module => declaringType2?.Module; /// /// Gets the full name of the property /// public string FullName => FullNameFactory.PropertyFullName(declaringType2?.FullName, name, type, null, null); bool IIsTypeOrMethod.IsType => false; bool IIsTypeOrMethod.IsMethod => false; bool IMemberRef.IsField => false; 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 => false; bool IMemberRef.IsPropertyDef => true; bool IMemberRef.IsEventDef => false; bool IMemberRef.IsGenericParam => false; /// /// 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, PropertyAttributes flags) { if (set) attributes |= (int)flags; else attributes &= ~(int)flags; } /// /// Gets/sets the bit /// public bool IsSpecialName { get => ((PropertyAttributes)attributes & PropertyAttributes.SpecialName) != 0; set => ModifyAttributes(value, PropertyAttributes.SpecialName); } /// /// Gets/sets the bit /// public bool IsRuntimeSpecialName { get => ((PropertyAttributes)attributes & PropertyAttributes.RTSpecialName) != 0; set => ModifyAttributes(value, PropertyAttributes.RTSpecialName); } /// /// Gets/sets the bit /// public bool HasDefault { get => ((PropertyAttributes)attributes & PropertyAttributes.HasDefault) != 0; set => ModifyAttributes(value, PropertyAttributes.HasDefault); } /// public override string ToString() => FullName; } /// /// A Property row created by the user and not present in the original .NET file /// public class PropertyDefUser : PropertyDef { /// /// Default constructor /// public PropertyDefUser() { } /// /// Constructor /// /// Name public PropertyDefUser(UTF8String name) : this(name, null) { } /// /// Constructor /// /// Name /// Property signature public PropertyDefUser(UTF8String name, PropertySig sig) : this(name, sig, 0) { } /// /// Constructor /// /// Name /// Property signature /// Flags public PropertyDefUser(UTF8String name, PropertySig sig, PropertyAttributes flags) { this.name = name; type = sig; attributes = (int)flags; } } /// /// Created from a row in the Property table /// sealed class PropertyDefMD : PropertyDef, IMDTokenProviderMD { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; /// public uint OrigRid => origRid; /// protected override Constant GetConstant_NoLock() => readerModule.ResolveConstant(readerModule.Metadata.GetConstantRid(Table.Property, origRid)); /// protected override void InitializeCustomAttributes() { var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Property, 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); } /// /// Constructor /// /// The module which contains this Property row /// Row ID /// If is null /// If is invalid public PropertyDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.PropertyTable.IsInvalidRID(rid)) throw new BadImageFormatException($"Property rid {rid} does not exist"); #endif origRid = rid; this.rid = rid; this.readerModule = readerModule; bool b = readerModule.TablesStream.TryReadPropertyRow(origRid, out var row); Debug.Assert(b); attributes = row.PropFlags; name = readerModule.StringsStream.ReadNoNull(row.Name); declaringType2 = readerModule.GetOwnerType(this); type = readerModule.ReadSignature(row.Type, new GenericParamContext(declaringType2)); } internal PropertyDefMD InitializeAll() { MemberMDInitializer.Initialize(Attributes); MemberMDInitializer.Initialize(Name); MemberMDInitializer.Initialize(Type); MemberMDInitializer.Initialize(Constant); MemberMDInitializer.Initialize(CustomAttributes); MemberMDInitializer.Initialize(GetMethod); MemberMDInitializer.Initialize(SetMethod); MemberMDInitializer.Initialize(OtherMethods); MemberMDInitializer.Initialize(DeclaringType); return this; } /// protected override void InitializePropertyMethods_NoLock() { if (otherMethods is not null) return; IList newOtherMethods; IList newGetMethods, newSetMethods; var dt = declaringType2 as TypeDefMD; if (dt is null) { newGetMethods = new List(); newSetMethods = new List(); newOtherMethods = new List(); } else dt.InitializeProperty(this, out newGetMethods, out newSetMethods, out newOtherMethods); getMethods = newGetMethods; setMethods = newSetMethods; // Must be initialized last otherMethods = newOtherMethods; } } }