// 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 Param table /// [DebuggerDisplay("{Sequence} {Name}")] public abstract class ParamDef : IHasConstant, IHasCustomAttribute, IHasFieldMarshal, IHasCustomDebugInformation { /// /// The row id in its table /// protected uint rid; #if THREAD_SAFE readonly Lock theLock = Lock.Create(); #endif /// public MDToken MDToken => new MDToken(Table.Param, rid); /// public uint Rid { get => rid; set => rid = value; } /// public int HasConstantTag => 1; /// public int HasCustomAttributeTag => 4; /// public int HasFieldMarshalTag => 1; /// /// Gets the declaring method /// public MethodDef DeclaringMethod { get => declaringMethod; internal set => declaringMethod = value; } /// protected MethodDef declaringMethod; /// /// From column Param.Flags /// public ParamAttributes Attributes { get => (ParamAttributes)attributes; set => attributes = (int)value; } /// Attributes protected int attributes; /// /// From column Param.Sequence /// public ushort Sequence { get => sequence; set => sequence = value; } /// protected ushort sequence; /// /// From column Param.Name /// public UTF8String Name { get => name; set => name = value; } /// Name protected UTF8String name; /// 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; /// 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 bool HasCustomAttributes => CustomAttributes.Count > 0; /// public int HasCustomDebugInformationTag => 4; /// 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 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; /// public string FullName { get { var n = name; if (UTF8String.IsNullOrEmpty(n)) return $"A_{sequence}"; return n.String; } } /// /// 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, ParamAttributes flags) { if (set) attributes |= (int)flags; else attributes &= ~(int)flags; } /// /// Gets/sets the bit /// public bool IsIn { get => ((ParamAttributes)attributes & ParamAttributes.In) != 0; set => ModifyAttributes(value, ParamAttributes.In); } /// /// Gets/sets the bit /// public bool IsOut { get => ((ParamAttributes)attributes & ParamAttributes.Out) != 0; set => ModifyAttributes(value, ParamAttributes.Out); } /// /// Gets/sets the bit /// public bool IsLcid { get => ((ParamAttributes)attributes & ParamAttributes.Lcid) != 0; set => ModifyAttributes(value, ParamAttributes.Lcid); } /// /// Gets/sets the bit /// public bool IsRetval { get => ((ParamAttributes)attributes & ParamAttributes.Retval) != 0; set => ModifyAttributes(value, ParamAttributes.Retval); } /// /// Gets/sets the bit /// public bool IsOptional { get => ((ParamAttributes)attributes & ParamAttributes.Optional) != 0; set => ModifyAttributes(value, ParamAttributes.Optional); } /// /// Gets/sets the bit /// public bool HasDefault { get => ((ParamAttributes)attributes & ParamAttributes.HasDefault) != 0; set => ModifyAttributes(value, ParamAttributes.HasDefault); } /// /// Gets/sets the bit /// public bool HasFieldMarshal { get => ((ParamAttributes)attributes & ParamAttributes.HasFieldMarshal) != 0; set => ModifyAttributes(value, ParamAttributes.HasFieldMarshal); } } /// /// A Param row created by the user and not present in the original .NET file /// public class ParamDefUser : ParamDef { /// /// Default constructor /// public ParamDefUser() { } /// /// Constructor /// /// Name public ParamDefUser(UTF8String name) : this(name, 0) { } /// /// Constructor /// /// Name /// Sequence public ParamDefUser(UTF8String name, ushort sequence) : this(name, sequence, 0) { } /// /// Constructor /// /// Name /// Sequence /// Flags public ParamDefUser(UTF8String name, ushort sequence, ParamAttributes flags) { this.name = name; this.sequence = sequence; attributes = (int)flags; } } /// /// Created from a row in the Param table /// sealed class ParamDefMD : ParamDef, IMDTokenProviderMD { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; /// public uint OrigRid => origRid; /// protected override MarshalType GetMarshalType_NoLock() => readerModule.ReadMarshalType(Table.Param, origRid, GenericParamContext.Create(declaringMethod)); /// protected override Constant GetConstant_NoLock() => readerModule.ResolveConstant(readerModule.Metadata.GetConstantRid(Table.Param, origRid)); /// protected override void InitializeCustomAttributes() { var list = readerModule.Metadata.GetCustomAttributeRidList(Table.Param, 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), GenericParamContext.Create(declaringMethod), list); Interlocked.CompareExchange(ref customDebugInfos, list, null); } /// /// Constructor /// /// The module which contains this Param row /// Row ID /// If is null /// If is invalid public ParamDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.ParamTable.IsInvalidRID(rid)) throw new BadImageFormatException($"Param rid {rid} does not exist"); #endif origRid = rid; this.rid = rid; this.readerModule = readerModule; bool b = readerModule.TablesStream.TryReadParamRow(origRid, out var row); Debug.Assert(b); attributes = row.Flags; sequence = row.Sequence; name = readerModule.StringsStream.ReadNoNull(row.Name); declaringMethod = readerModule.GetOwner(this); } internal ParamDefMD InitializeAll() { MemberMDInitializer.Initialize(DeclaringMethod); MemberMDInitializer.Initialize(Attributes); MemberMDInitializer.Initialize(Sequence); MemberMDInitializer.Initialize(Name); MemberMDInitializer.Initialize(MarshalType); MemberMDInitializer.Initialize(Constant); MemberMDInitializer.Initialize(CustomAttributes); return this; } } }