// 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 Event table /// public abstract class EventDef : 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.Event, rid); /// public uint Rid { get => rid; set => rid = value; } /// public int HasCustomAttributeTag => 10; /// public int HasSemanticTag => 0; /// /// From column Event.EventFlags /// public EventAttributes Attributes { get => (EventAttributes)attributes; set => attributes = (int)value; } /// protected int attributes; /// /// From column Event.Name /// public UTF8String Name { get => name; set => name = value; } /// Name protected UTF8String name; /// /// From column Event.EventType /// public ITypeDefOrRef EventType { get => eventType; set => eventType = value; } /// protected ITypeDefOrRef eventType; /// /// 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 => 10; /// 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 adder method /// public MethodDef AddMethod { get { if (otherMethods is null) InitializeEventMethods(); return addMethod; } set { if (otherMethods is null) InitializeEventMethods(); addMethod = value; } } /// /// Gets/sets the invoker method /// public MethodDef InvokeMethod { get { if (otherMethods is null) InitializeEventMethods(); return invokeMethod; } set { if (otherMethods is null) InitializeEventMethods(); invokeMethod = value; } } /// /// Gets/sets the remover method /// public MethodDef RemoveMethod { get { if (otherMethods is null) InitializeEventMethods(); return removeMethod; } set { if (otherMethods is null) InitializeEventMethods(); removeMethod = value; } } /// /// Gets the other methods /// public IList OtherMethods { get { if (otherMethods is null) InitializeEventMethods(); return otherMethods; } } void InitializeEventMethods() { #if THREAD_SAFE theLock.EnterWriteLock(); try { #endif if (otherMethods is null) InitializeEventMethods_NoLock(); #if THREAD_SAFE } finally { theLock.ExitWriteLock(); } #endif } /// /// Initializes , , /// and . /// protected virtual void InitializeEventMethods_NoLock() => otherMethods = new List(); /// protected MethodDef addMethod; /// protected MethodDef invokeMethod; /// protected MethodDef removeMethod; /// protected IList otherMethods; /// Reset , , , protected void ResetMethods() => otherMethods = null; /// /// true if there are no methods attached to this event /// public bool IsEmpty => // The first property access initializes the other fields we access here AddMethod is null && removeMethod is null && invokeMethod is null && otherMethods.Count == 0; /// public bool HasCustomAttributes => CustomAttributes.Count > 0; /// /// true if is not empty /// public bool HasOtherMethods => OtherMethods.Count > 0; /// /// 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.Events.Remove(this); // Will set DeclaringType2 = null if (value is not null) value.Events.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 event /// public string FullName => FullNameFactory.EventFullName(declaringType2?.FullName, name, eventType, 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 => false; bool IMemberRef.IsEventDef => true; 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, EventAttributes flags) { if (set) attributes |= (int)flags; else attributes &= ~(int)flags; } /// /// Gets/sets the bit /// public bool IsSpecialName { get => ((EventAttributes)attributes & EventAttributes.SpecialName) != 0; set => ModifyAttributes(value, EventAttributes.SpecialName); } /// /// Gets/sets the bit /// public bool IsRuntimeSpecialName { get => ((EventAttributes)attributes & EventAttributes.RTSpecialName) != 0; set => ModifyAttributes(value, EventAttributes.RTSpecialName); } /// public override string ToString() => FullName; } /// /// An Event row created by the user and not present in the original .NET file /// public class EventDefUser : EventDef { /// /// Default constructor /// public EventDefUser() { } /// /// Constructor /// /// Name public EventDefUser(UTF8String name) : this(name, null, 0) { } /// /// Constructor /// /// Name /// Type public EventDefUser(UTF8String name, ITypeDefOrRef type) : this(name, type, 0) { } /// /// Constructor /// /// Name /// Type /// Flags public EventDefUser(UTF8String name, ITypeDefOrRef type, EventAttributes flags) { this.name = name; eventType = type; attributes = (int)flags; } } /// /// Created from a row in the Event table /// sealed class EventDefMD : EventDef, 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.Event, 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 Event row /// Row ID /// If is null /// If is invalid public EventDefMD(ModuleDefMD readerModule, uint rid) { #if DEBUG if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.EventTable.IsInvalidRID(rid)) throw new BadImageFormatException($"Event rid {rid} does not exist"); #endif origRid = rid; this.rid = rid; this.readerModule = readerModule; bool b = readerModule.TablesStream.TryReadEventRow(origRid, out var row); Debug.Assert(b); attributes = row.EventFlags; name = readerModule.StringsStream.ReadNoNull(row.Name); declaringType2 = readerModule.GetOwnerType(this); eventType = readerModule.ResolveTypeDefOrRef(row.EventType, new GenericParamContext(declaringType2)); } internal EventDefMD InitializeAll() { MemberMDInitializer.Initialize(Attributes); MemberMDInitializer.Initialize(Name); MemberMDInitializer.Initialize(EventType); MemberMDInitializer.Initialize(CustomAttributes); MemberMDInitializer.Initialize(AddMethod); MemberMDInitializer.Initialize(InvokeMethod); MemberMDInitializer.Initialize(RemoveMethod); MemberMDInitializer.Initialize(OtherMethods); MemberMDInitializer.Initialize(DeclaringType); return this; } /// protected override void InitializeEventMethods_NoLock() { IList newOtherMethods; var dt = declaringType2 as TypeDefMD; if (dt is null) newOtherMethods = new List(); else dt.InitializeEvent(this, out addMethod, out invokeMethod, out removeMethod, out newOtherMethods); otherMethods = newOtherMethods; } } }