// 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; namespace dnlib.DotNet { /// /// A high-level representation of a row in the MemberRef table /// public abstract class MemberRef : IHasCustomAttribute, IMethodDefOrRef, ICustomAttributeType, IField, IContainsGenericParameter, IHasCustomDebugInformation { /// /// The row id in its table /// protected uint rid; /// /// The owner module /// protected ModuleDef module; /// public MDToken MDToken => new MDToken(Table.MemberRef, rid); /// public uint Rid { get => rid; set => rid = value; } /// public int HasCustomAttributeTag => 6; /// public int MethodDefOrRefTag => 1; /// public int CustomAttributeTypeTag => 3; /// /// From column MemberRef.Class /// public IMemberRefParent Class { get => @class; set => @class = value; } /// protected IMemberRefParent @class; /// /// From column MemberRef.Name /// public UTF8String Name { get => name; set => name = value; } /// Name protected UTF8String name; /// /// From column MemberRef.Signature /// public CallingConventionSig Signature { get => signature; set => signature = value; } /// protected CallingConventionSig signature; /// /// 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 => 6; /// 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); /// public ITypeDefOrRef DeclaringType { get { var owner = @class; if (owner is ITypeDefOrRef tdr) return tdr; if (owner is MethodDef method) return method.DeclaringType; if (owner is ModuleRef mr) { var tr = GetGlobalTypeRef(mr); if (module is not null) return module.UpdateRowId(tr); return tr; } return null; } } TypeRefUser GetGlobalTypeRef(ModuleRef mr) { if (module is null) return CreateDefaultGlobalTypeRef(mr); var globalType = module.GlobalType; if (globalType is not null && new SigComparer().Equals(module, mr)) return new TypeRefUser(module, globalType.Namespace, globalType.Name, mr); var asm = module.Assembly; if (asm is null) return CreateDefaultGlobalTypeRef(mr); var mod = asm.FindModule(mr.Name); if (mod is null) return CreateDefaultGlobalTypeRef(mr); globalType = mod.GlobalType; if (globalType is null) return CreateDefaultGlobalTypeRef(mr); return new TypeRefUser(module, globalType.Namespace, globalType.Name, mr); } TypeRefUser CreateDefaultGlobalTypeRef(ModuleRef mr) { var tr = new TypeRefUser(module, string.Empty, "", mr); if (module is not null) module.UpdateRowId(tr); return tr; } bool IIsTypeOrMethod.IsType => false; bool IIsTypeOrMethod.IsMethod => IsMethodRef; bool IMemberRef.IsField => IsFieldRef; bool IMemberRef.IsTypeSpec => false; bool IMemberRef.IsTypeRef => false; bool IMemberRef.IsTypeDef => false; bool IMemberRef.IsMethodSpec => false; bool IMemberRef.IsMethodDef => false; bool IMemberRef.IsMemberRef => true; bool IMemberRef.IsFieldDef => false; bool IMemberRef.IsPropertyDef => false; bool IMemberRef.IsEventDef => false; bool IMemberRef.IsGenericParam => false; /// /// true if this is a method reference ( != null) /// public bool IsMethodRef => MethodSig is not null; /// /// true if this is a field reference ( != null) /// public bool IsFieldRef => FieldSig is not null; /// /// Gets/sets the method sig /// public MethodSig MethodSig { get => signature as MethodSig; set => signature = value; } /// /// Gets/sets the field sig /// public FieldSig FieldSig { get => signature as FieldSig; set => signature = value; } /// public ModuleDef Module => module; /// /// true if the method has a hidden 'this' parameter /// public bool HasThis { get { var ms = MethodSig; return ms is null ? false : ms.HasThis; } } /// /// true if the method has an explicit 'this' parameter /// public bool ExplicitThis { get { var ms = MethodSig; return ms is null ? false : ms.ExplicitThis; } } /// /// Gets the calling convention /// public CallingConvention CallingConvention { get { var ms = MethodSig; return ms is null ? 0 : ms.CallingConvention & CallingConvention.Mask; } } /// /// Gets/sets the method return type /// public TypeSig ReturnType { get => MethodSig?.RetType; set { var ms = MethodSig; if (ms is not null) ms.RetType = value; } } /// int IGenericParameterProvider.NumberOfGenericParameters => (int)(MethodSig?.GenParamCount ?? 0); /// /// Gets the full name /// public string FullName { get { var parent = @class; IList typeGenArgs = null; if (parent is TypeSpec) { if (((TypeSpec)parent).TypeSig is GenericInstSig sig) typeGenArgs = sig.GenericArguments; } var methodSig = MethodSig; if (methodSig is not null) return FullNameFactory.MethodFullName(GetDeclaringTypeFullName(parent), name, methodSig, typeGenArgs, null, null, null); var fieldSig = FieldSig; if (fieldSig is not null) return FullNameFactory.FieldFullName(GetDeclaringTypeFullName(parent), name, fieldSig, typeGenArgs, null); return string.Empty; } } /// /// Get the declaring type's full name /// /// Full name or null if there's no declaring type public string GetDeclaringTypeFullName() => GetDeclaringTypeFullName(@class); string GetDeclaringTypeFullName(IMemberRefParent parent) { if (parent is null) return null; if (parent is ITypeDefOrRef) return ((ITypeDefOrRef)parent).FullName; if (parent is ModuleRef) return $"[module:{((ModuleRef)parent).ToString()}]"; if (parent is MethodDef) { var declaringType = ((MethodDef)parent).DeclaringType; return declaringType?.FullName; } return null; // Should never be reached } /// /// Get the declaring type's name /// /// Name or null if there's no declaring type internal string GetDeclaringTypeName() => GetDeclaringTypeName(@class); string GetDeclaringTypeName(IMemberRefParent parent) { if (parent is null) return null; if (parent is ITypeDefOrRef) return ((ITypeDefOrRef)parent).Name; if (parent is ModuleRef) return $""; if (parent is MethodDef) { var declaringType = ((MethodDef)parent).DeclaringType; return declaringType?.Name; } return null; // Should never be reached } /// /// Resolves the method/field /// /// A or a instance or null /// if it couldn't be resolved. public IMemberForwarded Resolve() { if (module is null) return null; return module.Context.Resolver.Resolve(this); } /// /// Resolves the method/field /// /// A or a instance /// If the method/field couldn't be resolved public IMemberForwarded ResolveThrow() { var memberDef = Resolve(); if (memberDef is not null) return memberDef; throw new MemberRefResolveException($"Could not resolve method/field: {this} ({this.GetDefinitionAssembly()})"); } /// /// Resolves the field /// /// A instance or null if it couldn't be resolved. public FieldDef ResolveField() => Resolve() as FieldDef; /// /// Resolves the field /// /// A instance /// If the field couldn't be resolved public FieldDef ResolveFieldThrow() { var field = ResolveField(); if (field is not null) return field; throw new MemberRefResolveException($"Could not resolve field: {this} ({this.GetDefinitionAssembly()})"); } /// /// Resolves the method /// /// A instance or null if it couldn't be resolved. public MethodDef ResolveMethod() => Resolve() as MethodDef; /// /// Resolves the method /// /// A instance /// If the method couldn't be resolved public MethodDef ResolveMethodThrow() { var method = ResolveMethod(); if (method is not null) return method; throw new MemberRefResolveException($"Could not resolve method: {this} ({this.GetDefinitionAssembly()})"); } bool IContainsGenericParameter.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// /// Gets a that can be used as signature context /// /// Context passed to the constructor /// Field/method class owner /// protected static GenericParamContext GetSignatureGenericParamContext(GenericParamContext gpContext, IMemberRefParent @class) { TypeDef type = null; var method = gpContext.Method; if (@class is TypeSpec ts && ts.TypeSig is GenericInstSig gis) type = gis.GenericType.ToTypeDefOrRef().ResolveTypeDef(); return new GenericParamContext(type, method); } /// public override string ToString() => FullName; } /// /// A MemberRef row created by the user and not present in the original .NET file /// public class MemberRefUser : MemberRef { /// /// Constructor /// /// Owner module public MemberRefUser(ModuleDef module) => this.module = module; /// /// Constructor /// /// Owner module /// Name of ref public MemberRefUser(ModuleDef module, UTF8String name) { this.module = module; this.name = name; } /// /// Constructor /// /// Owner module /// Name of field ref /// Field sig public MemberRefUser(ModuleDef module, UTF8String name, FieldSig sig) : this(module, name, sig, null) { } /// /// Constructor /// /// Owner module /// Name of field ref /// Field sig /// Owner of field public MemberRefUser(ModuleDef module, UTF8String name, FieldSig sig, IMemberRefParent @class) { this.module = module; this.name = name; this.@class = @class; signature = sig; } /// /// Constructor /// /// Owner module /// Name of method ref /// Method sig public MemberRefUser(ModuleDef module, UTF8String name, MethodSig sig) : this(module, name, sig, null) { } /// /// Constructor /// /// Owner module /// Name of method ref /// Method sig /// Owner of method public MemberRefUser(ModuleDef module, UTF8String name, MethodSig sig, IMemberRefParent @class) { this.module = module; this.name = name; this.@class = @class; signature = sig; } } /// /// Created from a row in the MemberRef table /// sealed class MemberRefMD : MemberRef, IMDTokenProviderMD, IContainsGenericParameter2 { /// The module where this instance is located readonly ModuleDefMD readerModule; readonly uint origRid; readonly GenericParamContext gpContext; /// public uint OrigRid => origRid; bool IContainsGenericParameter2.ContainsGenericParameter => TypeHelper.ContainsGenericParameter(this); /// protected override void InitializeCustomAttributes() { var list = readerModule.Metadata.GetCustomAttributeRidList(Table.MemberRef, 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), gpContext, list); Interlocked.CompareExchange(ref customDebugInfos, list, null); } /// /// Constructor /// /// The module which contains this MemberRef row /// Row ID /// Generic parameter context /// If is null /// If is invalid public MemberRefMD(ModuleDefMD readerModule, uint rid, GenericParamContext gpContext) { #if DEBUG if (readerModule is null) throw new ArgumentNullException("readerModule"); if (readerModule.TablesStream.MemberRefTable.IsInvalidRID(rid)) throw new BadImageFormatException($"MemberRef rid {rid} does not exist"); #endif origRid = rid; this.rid = rid; this.readerModule = readerModule; this.gpContext = gpContext; module = readerModule; bool b = readerModule.TablesStream.TryReadMemberRefRow(origRid, out var row); Debug.Assert(b); name = readerModule.StringsStream.ReadNoNull(row.Name); @class = readerModule.ResolveMemberRefParent(row.Class, gpContext); signature = readerModule.ReadSignature(row.Signature, GetSignatureGenericParamContext(gpContext, @class)); } } }