// dnlib: See LICENSE.txt for more info using System; using System.Diagnostics; using System.Threading; using dnlib.Utils; using dnlib.DotNet.MD; using dnlib.DotNet.Pdb; using System.Collections.Generic; namespace dnlib.DotNet { /// /// A high-level representation of a row in the GenericParam table /// [DebuggerDisplay("{Name.String}")] public abstract class GenericParam : IHasCustomAttribute, IHasCustomDebugInformation, IMemberDef, IListListener { /// /// The row id in its table /// protected uint rid; /// public MDToken MDToken => new MDToken(Table.GenericParam, rid); /// public uint Rid { get => rid; set => rid = value; } /// public int HasCustomAttributeTag => 19; /// /// Gets the owner type/method /// public ITypeOrMethodDef Owner { get => owner; internal set => owner = value; } /// protected ITypeOrMethodDef owner; /// /// Gets the declaring type or null if none or if is /// not a /// public TypeDef DeclaringType => owner as TypeDef; /// ITypeDefOrRef IMemberRef.DeclaringType => owner as TypeDef; /// /// Gets the declaring method or null if none or if is /// not a /// public MethodDef DeclaringMethod => owner as MethodDef; /// /// From column GenericParam.Number /// public ushort Number { get => number; set => number = value; } /// protected ushort number; /// /// From column GenericParam.Flags /// public GenericParamAttributes Flags { get => (GenericParamAttributes)attributes; set => attributes = (int)value; } /// Attributes protected int attributes; /// /// From column GenericParam.Name /// public UTF8String Name { get => name; set => name = value; } /// Name protected UTF8String name; /// /// From column GenericParam.Kind (v1.1 only) /// public ITypeDefOrRef Kind { get => kind; set => kind = value; } /// protected ITypeDefOrRef kind; /// /// Gets the generic param constraints /// public IList GenericParamConstraints { get { if (genericParamConstraints is null) InitializeGenericParamConstraints(); return genericParamConstraints; } } /// protected LazyList genericParamConstraints; /// Initializes protected virtual void InitializeGenericParamConstraints() => Interlocked.CompareExchange(ref genericParamConstraints, new LazyList(this), null); /// /// 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 bool HasCustomAttributes => CustomAttributes.Count > 0; /// public int HasCustomDebugInformationTag => 19; /// 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); /// /// true if is not empty /// public bool HasGenericParamConstraints => GenericParamConstraints.Count > 0; /// public ModuleDef Module => owner?.Module; /// public string FullName => UTF8String.ToSystemStringOrEmpty(name); 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 => false; bool IMemberRef.IsEventDef => false; bool IMemberRef.IsGenericParam => true; /// /// Modify property: = /// ( & ) | . /// /// Value to AND /// Value to OR void ModifyAttributes(GenericParamAttributes andMask, GenericParamAttributes 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, GenericParamAttributes flags) { if (set) attributes |= (int)flags; else attributes &= ~(int)flags; } /// /// Gets/sets variance (non, contra, co) /// public GenericParamAttributes Variance { get => (GenericParamAttributes)attributes & GenericParamAttributes.VarianceMask; set => ModifyAttributes(~GenericParamAttributes.VarianceMask, value & GenericParamAttributes.VarianceMask); } /// /// true if is set /// public bool IsNonVariant => Variance == GenericParamAttributes.NonVariant; /// /// true if is set /// public bool IsCovariant => Variance == GenericParamAttributes.Covariant; /// /// true if is set /// public bool IsContravariant => Variance == GenericParamAttributes.Contravariant; /// /// Gets/sets the special constraint /// public GenericParamAttributes SpecialConstraint { get => (GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask; set => ModifyAttributes(~GenericParamAttributes.SpecialConstraintMask, value & GenericParamAttributes.SpecialConstraintMask); } /// /// true if there are no special constraints /// public bool HasNoSpecialConstraint => ((GenericParamAttributes)attributes & GenericParamAttributes.SpecialConstraintMask) == GenericParamAttributes.NoSpecialConstraint; /// /// Gets/sets the bit /// public bool HasReferenceTypeConstraint { get => ((GenericParamAttributes)attributes & GenericParamAttributes.ReferenceTypeConstraint) != 0; set => ModifyAttributes(value, GenericParamAttributes.ReferenceTypeConstraint); } /// /// Gets/sets the bit /// public bool HasNotNullableValueTypeConstraint { get => ((GenericParamAttributes)attributes & GenericParamAttributes.NotNullableValueTypeConstraint) != 0; set => ModifyAttributes(value, GenericParamAttributes.NotNullableValueTypeConstraint); } /// /// Gets/sets the bit /// public bool HasDefaultConstructorConstraint { get => ((GenericParamAttributes)attributes & GenericParamAttributes.DefaultConstructorConstraint) != 0; set => ModifyAttributes(value, GenericParamAttributes.DefaultConstructorConstraint); } /// void IListListener.OnLazyAdd(int index, ref GenericParamConstraint value) => OnLazyAdd2(index, ref value); internal virtual void OnLazyAdd2(int index, ref GenericParamConstraint value) { #if DEBUG if (value.Owner != this) throw new InvalidOperationException("Added generic param constraint's Owner != this"); #endif } /// void IListListener.OnAdd(int index, GenericParamConstraint value) { if (value.Owner is not null) throw new InvalidOperationException("Generic param constraint is already owned by another generic param. Set Owner to null first."); value.Owner = this; } /// void IListListener.OnRemove(int index, GenericParamConstraint value) => value.Owner = null; /// void IListListener.OnResize(int index) { } /// void IListListener.OnClear() { foreach (var gpc in genericParamConstraints.GetEnumerable_NoLock()) gpc.Owner = null; } /// public override string ToString() { var o = owner; if (o is TypeDef) return $"!{number}"; if (o is MethodDef) return $"!!{number}"; return $"??{number}"; } } /// /// A GenericParam row created by the user and not present in the original .NET file /// public class GenericParamUser : GenericParam { /// /// Default constructor /// public GenericParamUser() { } /// /// Constructor /// /// The generic param number public GenericParamUser(ushort number) : this(number, 0) { } /// /// Constructor /// /// The generic param number /// Flags public GenericParamUser(ushort number, GenericParamAttributes flags) : this(number, flags, UTF8String.Empty) { } /// /// Constructor /// /// The generic param number /// Flags /// Name public GenericParamUser(ushort number, GenericParamAttributes flags, UTF8String name) { genericParamConstraints = new LazyList(this); this.number = number; attributes = (int)flags; this.name = name; } } /// /// Created from a row in the GenericParam table /// sealed class GenericParamMD : GenericParam, 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.GenericParam, 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), GetGenericParamContext(owner), list); Interlocked.CompareExchange(ref customDebugInfos, list, null); } /// protected override void InitializeGenericParamConstraints() { var list = readerModule.Metadata.GetGenericParamConstraintRidList(origRid); var tmp = new LazyList(list.Count, this, list, (list2, index) => readerModule.ResolveGenericParamConstraint(list2[index], GetGenericParamContext(owner))); Interlocked.CompareExchange(ref genericParamConstraints, tmp, null); } static GenericParamContext GetGenericParamContext(ITypeOrMethodDef tmOwner) { if (tmOwner is MethodDef md) return GenericParamContext.Create(md); return new GenericParamContext(tmOwner as TypeDef); } /// /// Constructor /// /// The module which contains this GenericParam row /// Row ID /// If is null /// If is invalid public GenericParamMD(ModuleDefMD readerModule, uint rid) { #if DEBUG if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.GenericParamTable.IsInvalidRID(rid)) throw new BadImageFormatException($"GenericParam rid {rid} does not exist"); #endif origRid = rid; this.rid = rid; this.readerModule = readerModule; bool b = readerModule.TablesStream.TryReadGenericParamRow(origRid, out var row); Debug.Assert(b); number = row.Number; attributes = row.Flags; name = readerModule.StringsStream.ReadNoNull(row.Name); owner = readerModule.GetOwner(this); if (row.Kind != 0) kind = readerModule.ResolveTypeDefOrRef(row.Kind, GetGenericParamContext(owner)); } internal GenericParamMD InitializeAll() { MemberMDInitializer.Initialize(Owner); MemberMDInitializer.Initialize(Number); MemberMDInitializer.Initialize(Flags); MemberMDInitializer.Initialize(Name); MemberMDInitializer.Initialize(Kind); MemberMDInitializer.Initialize(CustomAttributes); MemberMDInitializer.Initialize(GenericParamConstraints); return this; } /// internal override void OnLazyAdd2(int index, ref GenericParamConstraint value) { if (value.Owner != this) { // More than one owner... This module has invalid metadata. value = readerModule.ForceUpdateRowId(readerModule.ReadGenericParamConstraint(value.Rid, GetGenericParamContext(owner)).InitializeAll()); value.Owner = this; } } } }