// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using System.Reflection; namespace dnlib.DotNet { /// /// Compares types /// public sealed class TypeEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer { readonly SigComparerOptions options; /// /// Default instance /// public static readonly TypeEqualityComparer Instance = new TypeEqualityComparer(0); /// /// Case insensitive names /// public static readonly TypeEqualityComparer CaseInsensitive = new TypeEqualityComparer(SigComparerOptions.CaseInsensitiveAll); /// /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. /// public static readonly TypeEqualityComparer CompareReferenceInSameModule = new TypeEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); /// /// Constructor /// /// Comparison options public TypeEqualityComparer(SigComparerOptions options) => this.options = options; /// public bool Equals(IType x, IType y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(IType obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(ITypeDefOrRef x, ITypeDefOrRef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(ITypeDefOrRef obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(TypeRef x, TypeRef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(TypeRef obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(TypeDef x, TypeDef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(TypeDef obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(TypeSpec x, TypeSpec y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(TypeSpec obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(TypeSig x, TypeSig y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(TypeSig obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(ExportedType x, ExportedType y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(ExportedType obj) => new SigComparer(options).GetHashCode(obj); } /// /// Compares fields /// public sealed class FieldEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer { readonly SigComparerOptions options; /// /// Compares the declaring types /// public static readonly FieldEqualityComparer CompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType); /// /// Doesn't compare the declaring types /// public static readonly FieldEqualityComparer DontCompareDeclaringTypes = new FieldEqualityComparer(0); /// /// Compares the declaring types, case insensitive names /// public static readonly FieldEqualityComparer CaseInsensitiveCompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType | SigComparerOptions.CaseInsensitiveAll); /// /// Doesn't compare the declaring types, case insensitive names /// public static readonly FieldEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CaseInsensitiveAll); /// /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. /// public static readonly FieldEqualityComparer CompareReferenceInSameModule = new FieldEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); /// /// Constructor /// /// Comparison options public FieldEqualityComparer(SigComparerOptions options) => this.options = options; /// public bool Equals(IField x, IField y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(IField obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(FieldDef x, FieldDef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(FieldDef obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(MemberRef x, MemberRef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(MemberRef obj) => new SigComparer(options).GetHashCode(obj); } /// /// Compares methods /// public sealed class MethodEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer { readonly SigComparerOptions options; /// /// Compares the declaring types /// public static readonly MethodEqualityComparer CompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType); /// /// Doesn't compare the declaring types /// public static readonly MethodEqualityComparer DontCompareDeclaringTypes = new MethodEqualityComparer(0); /// /// Compares the declaring types, case insensitive names /// public static readonly MethodEqualityComparer CaseInsensitiveCompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType | SigComparerOptions.CaseInsensitiveAll); /// /// Doesn't compare the declaring types, case insensitive names /// public static readonly MethodEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CaseInsensitiveAll); /// /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. /// public static readonly MethodEqualityComparer CompareReferenceInSameModule = new MethodEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); /// /// Constructor /// /// Comparison options public MethodEqualityComparer(SigComparerOptions options) => this.options = options; /// public bool Equals(IMethod x, IMethod y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(IMethod obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(IMethodDefOrRef x, IMethodDefOrRef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(IMethodDefOrRef obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(MethodDef x, MethodDef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(MethodDef obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(MemberRef x, MemberRef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(MemberRef obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(MethodSpec x, MethodSpec y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(MethodSpec obj) => new SigComparer(options).GetHashCode(obj); } /// /// Compares properties /// public sealed class PropertyEqualityComparer : IEqualityComparer { readonly SigComparerOptions options; /// /// Compares the declaring types /// public static readonly PropertyEqualityComparer CompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.ComparePropertyDeclaringType); /// /// Doesn't compare the declaring types /// public static readonly PropertyEqualityComparer DontCompareDeclaringTypes = new PropertyEqualityComparer(0); /// /// Compares the declaring types, case insensitive names /// public static readonly PropertyEqualityComparer CaseInsensitiveCompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.ComparePropertyDeclaringType | SigComparerOptions.CaseInsensitiveAll); /// /// Doesn't compare the declaring types, case insensitive names /// public static readonly PropertyEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.CaseInsensitiveAll); /// /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. /// public static readonly PropertyEqualityComparer CompareReferenceInSameModule = new PropertyEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); /// /// Constructor /// /// Comparison options public PropertyEqualityComparer(SigComparerOptions options) => this.options = options; /// public bool Equals(PropertyDef x, PropertyDef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(PropertyDef obj) => new SigComparer(options).GetHashCode(obj); } /// /// Compares events /// public sealed class EventEqualityComparer : IEqualityComparer { readonly SigComparerOptions options; /// /// Compares the declaring types /// public static readonly EventEqualityComparer CompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CompareEventDeclaringType); /// /// Doesn't compare the declaring types /// public static readonly EventEqualityComparer DontCompareDeclaringTypes = new EventEqualityComparer(0); /// /// Compares the declaring types, case insensitive names /// public static readonly EventEqualityComparer CaseInsensitiveCompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CompareEventDeclaringType | SigComparerOptions.CaseInsensitiveAll); /// /// Doesn't compare the declaring types, case insensitive names /// public static readonly EventEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CaseInsensitiveAll); /// /// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc. /// public static readonly EventEqualityComparer CompareReferenceInSameModule = new EventEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule); /// /// Constructor /// /// Comparison options public EventEqualityComparer(SigComparerOptions options) => this.options = options; /// public bool Equals(EventDef x, EventDef y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(EventDef obj) => new SigComparer(options).GetHashCode(obj); } /// /// Compares calling convention signatures /// public sealed class SignatureEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer { readonly SigComparerOptions options; /// /// Default instance /// public static readonly SignatureEqualityComparer Instance = new SignatureEqualityComparer(0); /// /// Case insensitive names /// public static readonly SignatureEqualityComparer CaseInsensitive = new SignatureEqualityComparer(SigComparerOptions.CaseInsensitiveAll); /// /// Constructor /// /// Comparison options public SignatureEqualityComparer(SigComparerOptions options) => this.options = options; /// public bool Equals(CallingConventionSig x, CallingConventionSig y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(CallingConventionSig obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(MethodBaseSig x, MethodBaseSig y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(MethodBaseSig obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(MethodSig x, MethodSig y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(MethodSig obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(PropertySig x, PropertySig y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(PropertySig obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(FieldSig x, FieldSig y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(FieldSig obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(LocalSig x, LocalSig y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(LocalSig obj) => new SigComparer(options).GetHashCode(obj); /// public bool Equals(GenericInstMethodSig x, GenericInstMethodSig y) => new SigComparer(options).Equals(x, y); /// public int GetHashCode(GenericInstMethodSig obj) => new SigComparer(options).GetHashCode(obj); } /// /// Decides how to compare types, sigs, etc /// [Flags] public enum SigComparerOptions : uint { /// /// Don't compare a type's (assembly/module) scope /// DontCompareTypeScope = 1, /// /// Compares a method/field's declaring type. /// CompareMethodFieldDeclaringType = 2, /// /// Compares a property's declaring type /// ComparePropertyDeclaringType = 4, /// /// Compares an event's declaring type /// CompareEventDeclaringType = 8, /// /// Compares method / field / property / event declaring types /// CompareDeclaringTypes = CompareMethodFieldDeclaringType | ComparePropertyDeclaringType | CompareEventDeclaringType, /// /// Compares parameters after a sentinel in method sigs. Should not be enabled when /// comparing s against s since it's /// not possible to get those sentinel params from a . /// CompareSentinelParams = 0x10, /// /// Compares assembly public key token /// CompareAssemblyPublicKeyToken = 0x20, /// /// Compares assembly version /// CompareAssemblyVersion = 0x40, /// /// Compares assembly locale /// CompareAssemblyLocale = 0x80, /// /// If set, a and an can reference the /// global <Module> type. /// TypeRefCanReferenceGlobalType = 0x100, /// /// Don't compare a method/property's return type /// DontCompareReturnType = 0x200, // Internal only //SubstituteGenericParameters = 0x400, /// /// Type namespaces are case insensitive /// CaseInsensitiveTypeNamespaces = 0x800, /// /// Type names (not namespaces) are case insensitive /// CaseInsensitiveTypeNames = 0x1000, /// /// Type names and namespaces are case insensitive /// CaseInsensitiveTypes = CaseInsensitiveTypeNamespaces | CaseInsensitiveTypeNames, /// /// Method and field names are case insensitive /// CaseInsensitiveMethodFieldNames = 0x2000, /// /// Property names are case insensitive /// CaseInsensitivePropertyNames = 0x4000, /// /// Event names are case insensitive /// CaseInsensitiveEventNames = 0x8000, /// /// Type namespaces, type names, method names, field names, property names /// and event names are all case insensitive /// CaseInsensitiveAll = CaseInsensitiveTypeNamespaces | CaseInsensitiveTypeNames | CaseInsensitiveMethodFieldNames | CaseInsensitivePropertyNames | CaseInsensitiveEventNames, /// /// A field that is can compare equal to /// a /// PrivateScopeFieldIsComparable = 0x10000, /// /// A method that is can compare equal to /// a /// PrivateScopeMethodIsComparable = 0x20000, /// /// A field that is and a method that is /// can compare equal to a /// PrivateScopeIsComparable = PrivateScopeFieldIsComparable | PrivateScopeMethodIsComparable, /// /// Raw (bit by bit) comparison of signatures. This matches what the CLR does when it /// compares signatures. This means that metadata tokens will be compared. /// RawSignatureCompare = 0x40000, /// /// Ignore required and optional modifiers when comparing s. /// They're already ignored when comparing eg. a with a /// . /// IgnoreModifiers = 0x80000, /// /// By default, all module and assembly compares when they're both the system library /// (eg. mscorlib or System.Runtime.dll) return true, even if they're really different, /// eg. mscorlib (.NET Framework 2.0) vs mscorlib (Windows CE). If this flag is set, the system /// library is compared just like any other module/assembly. /// MscorlibIsNotSpecial = 0x100000, /// /// Don't project CLR compatible WinMD references back to the original CLR type/method before comparing /// DontProjectWinMDRefs = 0x200000, /// /// Don't check type equivalence when comparing types. Starting with .NET Framework 4.0, two different /// types can be considered equivalent if eg. a TypeIdentifierAttribute is used. /// DontCheckTypeEquivalence = 0x400000, /// /// When comparing types, don't compare a multi-dimensional array's lower bounds and sizes /// IgnoreMultiDimensionalArrayLowerBoundsAndSizes = 0x800000, /// /// When comparing MemberDefs in same module, use regular reference comparison instead /// comparing the signature, name, and other properties. /// ReferenceCompareForMemberDefsInSameModule = 0x1000000, } /// /// Compares types, signatures, methods, fields, properties, events /// public struct SigComparer { const SigComparerOptions SigComparerOptions_DontSubstituteGenericParameters = (SigComparerOptions)0x400; const int HASHCODE_MAGIC_GLOBAL_TYPE = 1654396648; const int HASHCODE_MAGIC_NESTED_TYPE = -1049070942; const int HASHCODE_MAGIC_ET_MODULE = -299744851; const int HASHCODE_MAGIC_ET_VALUEARRAY = -674970533; const int HASHCODE_MAGIC_ET_GENERICINST = -2050514639; const int HASHCODE_MAGIC_ET_VAR = 1288450097; const int HASHCODE_MAGIC_ET_MVAR = -990598495; const int HASHCODE_MAGIC_ET_ARRAY = -96331531; const int HASHCODE_MAGIC_ET_SZARRAY = 871833535; const int HASHCODE_MAGIC_ET_BYREF = -634749586; const int HASHCODE_MAGIC_ET_PTR = 1976400808; const int HASHCODE_MAGIC_ET_SENTINEL = 68439620; RecursionCounter recursionCounter; SigComparerOptions options; GenericArguments genericArguments; readonly ModuleDef sourceModule; bool DontCompareTypeScope => (options & SigComparerOptions.DontCompareTypeScope) != 0; bool CompareMethodFieldDeclaringType => (options & SigComparerOptions.CompareMethodFieldDeclaringType) != 0; bool ComparePropertyDeclaringType => (options & SigComparerOptions.ComparePropertyDeclaringType) != 0; bool CompareEventDeclaringType => (options & SigComparerOptions.CompareEventDeclaringType) != 0; bool CompareSentinelParams => (options & SigComparerOptions.CompareSentinelParams) != 0; bool CompareAssemblyPublicKeyToken => (options & SigComparerOptions.CompareAssemblyPublicKeyToken) != 0; bool CompareAssemblyVersion => (options & SigComparerOptions.CompareAssemblyVersion) != 0; bool CompareAssemblyLocale => (options & SigComparerOptions.CompareAssemblyLocale) != 0; bool TypeRefCanReferenceGlobalType => (options & SigComparerOptions.TypeRefCanReferenceGlobalType) != 0; bool DontCompareReturnType => (options & SigComparerOptions.DontCompareReturnType) != 0; bool DontSubstituteGenericParameters => (options & SigComparerOptions_DontSubstituteGenericParameters) != 0; bool CaseInsensitiveTypeNamespaces => (options & SigComparerOptions.CaseInsensitiveTypeNamespaces) != 0; bool CaseInsensitiveTypeNames => (options & SigComparerOptions.CaseInsensitiveTypeNames) != 0; bool CaseInsensitiveMethodFieldNames => (options & SigComparerOptions.CaseInsensitiveMethodFieldNames) != 0; bool CaseInsensitivePropertyNames => (options & SigComparerOptions.CaseInsensitivePropertyNames) != 0; bool CaseInsensitiveEventNames => (options & SigComparerOptions.CaseInsensitiveEventNames) != 0; bool PrivateScopeFieldIsComparable => (options & SigComparerOptions.PrivateScopeFieldIsComparable) != 0; bool PrivateScopeMethodIsComparable => (options & SigComparerOptions.PrivateScopeMethodIsComparable) != 0; bool RawSignatureCompare => (options & SigComparerOptions.RawSignatureCompare) != 0; bool IgnoreModifiers => (options & SigComparerOptions.IgnoreModifiers) != 0; bool MscorlibIsNotSpecial => (options & SigComparerOptions.MscorlibIsNotSpecial) != 0; bool DontProjectWinMDRefs => (options & SigComparerOptions.DontProjectWinMDRefs) != 0; bool DontCheckTypeEquivalence => (options & SigComparerOptions.DontCheckTypeEquivalence) != 0; bool IgnoreMultiDimensionalArrayLowerBoundsAndSizes => (options & SigComparerOptions.IgnoreMultiDimensionalArrayLowerBoundsAndSizes) != 0; bool ReferenceCompareForMemberDefsInSameModule => (options & SigComparerOptions.ReferenceCompareForMemberDefsInSameModule) != 0; /// /// Constructor /// /// Comparison options public SigComparer(SigComparerOptions options) : this(options, null) { } /// /// Constructor /// /// Comparison options /// The module which the comparison take place in. public SigComparer(SigComparerOptions options, ModuleDef sourceModule) { recursionCounter = new RecursionCounter(); this.options = options; genericArguments = null; this.sourceModule = sourceModule; } /// /// is mapped to , so use /// the same hash code for both /// int GetHashCode_FnPtr_SystemIntPtr() { // ******************************************** // IMPORTANT: This must match GetHashCode(TYPE) // ******************************************** return GetHashCode_TypeNamespace("System") + GetHashCode_TypeName("IntPtr"); } bool Equals_Names(bool caseInsensitive, UTF8String a, UTF8String b) { if (caseInsensitive) return UTF8String.ToSystemStringOrEmpty(a).Equals(UTF8String.ToSystemStringOrEmpty(b), StringComparison.OrdinalIgnoreCase); return UTF8String.Equals(a, b); } bool Equals_Names(bool caseInsensitive, string a, string b) { if (caseInsensitive) return (a ?? string.Empty).Equals(b ?? string.Empty, StringComparison.OrdinalIgnoreCase); return (a ?? string.Empty) == (b ?? string.Empty); } int GetHashCode_Name(bool caseInsensitive, string a) { if (caseInsensitive) return (a ?? string.Empty).ToUpperInvariant().GetHashCode(); return (a ?? string.Empty).GetHashCode(); } bool Equals_TypeNamespaces(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitiveTypeNamespaces, a, b); bool Equals_TypeNamespaces(UTF8String a, string b) => Equals_Names(CaseInsensitiveTypeNamespaces, UTF8String.ToSystemStringOrEmpty(a), b); int GetHashCode_TypeNamespace(UTF8String a) => GetHashCode_Name(CaseInsensitiveTypeNamespaces, UTF8String.ToSystemStringOrEmpty(a)); int GetHashCode_TypeNamespace(string a) => GetHashCode_Name(CaseInsensitiveTypeNamespaces, a); bool Equals_TypeNames(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitiveTypeNames, a, b); bool Equals_TypeNames(UTF8String a, string b) => Equals_Names(CaseInsensitiveTypeNames, UTF8String.ToSystemStringOrEmpty(a), b); int GetHashCode_TypeName(UTF8String a) => GetHashCode_Name(CaseInsensitiveTypeNames, UTF8String.ToSystemStringOrEmpty(a)); int GetHashCode_TypeName(string a) => GetHashCode_Name(CaseInsensitiveTypeNames, a); bool Equals_MethodFieldNames(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitiveMethodFieldNames, a, b); bool Equals_MethodFieldNames(UTF8String a, string b) => Equals_Names(CaseInsensitiveMethodFieldNames, UTF8String.ToSystemStringOrEmpty(a), b); int GetHashCode_MethodFieldName(UTF8String a) => GetHashCode_Name(CaseInsensitiveMethodFieldNames, UTF8String.ToSystemStringOrEmpty(a)); int GetHashCode_MethodFieldName(string a) => GetHashCode_Name(CaseInsensitiveMethodFieldNames, a); bool Equals_PropertyNames(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitivePropertyNames, a, b); bool Equals_PropertyNames(UTF8String a, string b) => Equals_Names(CaseInsensitivePropertyNames, UTF8String.ToSystemStringOrEmpty(a), b); int GetHashCode_PropertyName(UTF8String a) => GetHashCode_Name(CaseInsensitivePropertyNames, UTF8String.ToSystemStringOrEmpty(a)); int GetHashCode_PropertyName(string a) => GetHashCode_Name(CaseInsensitivePropertyNames, a); bool Equals_EventNames(UTF8String a, UTF8String b) => Equals_Names(CaseInsensitiveEventNames, a, b); bool Equals_EventNames(UTF8String a, string b) => Equals_Names(CaseInsensitiveEventNames, UTF8String.ToSystemStringOrEmpty(a), b); int GetHashCode_EventName(UTF8String a) => GetHashCode_Name(CaseInsensitiveEventNames, UTF8String.ToSystemStringOrEmpty(a)); int GetHashCode_EventName(string a) => GetHashCode_Name(CaseInsensitiveEventNames, a); SigComparerOptions ClearOptions(SigComparerOptions flags) { var old = options; options &= ~flags; return old; } SigComparerOptions SetOptions(SigComparerOptions flags) { var old = options; options |= flags; return old; } void RestoreOptions(SigComparerOptions oldFlags) => options = oldFlags; void InitializeGenericArguments() { if (genericArguments is null) genericArguments = new GenericArguments(); } static GenericInstSig GetGenericInstanceType(IMemberRefParent parent) { var ts = parent as TypeSpec; if (ts is null) return null; return ts.TypeSig.RemoveModifiers() as GenericInstSig; } bool Equals(IAssembly aAsm, IAssembly bAsm, TypeRef b) { if (Equals(aAsm, bAsm)) return true; // Could be an exported type. Resolve it and check again. var td = b.Resolve(sourceModule); return td is not null && Equals(aAsm, td.Module.Assembly); } bool Equals(IAssembly aAsm, IAssembly bAsm, ExportedType b) { if (Equals(aAsm, bAsm)) return true; var td = b.Resolve(); return td is not null && Equals(aAsm, td.Module.Assembly); } bool Equals(IAssembly aAsm, TypeRef a, IAssembly bAsm, TypeRef b) { if (Equals(aAsm, bAsm)) return true; // Could be exported types. Resolve them and check again. var tda = a.Resolve(sourceModule); var tdb = b.Resolve(sourceModule); return tda is not null && tdb is not null && Equals(tda.Module.Assembly, tdb.Module.Assembly); } bool Equals(IAssembly aAsm, ExportedType a, IAssembly bAsm, ExportedType b) { if (Equals(aAsm, bAsm)) return true; var tda = a.Resolve(); var tdb = b.Resolve(); return tda is not null && tdb is not null && Equals(tda.Module.Assembly, tdb.Module.Assembly); } bool Equals(IAssembly aAsm, TypeRef a, IAssembly bAsm, ExportedType b) { if (Equals(aAsm, bAsm)) return true; // Could be an exported type. Resolve it and check again. var tda = a.Resolve(sourceModule); var tdb = b.Resolve(); return tda is not null && tdb is not null && Equals(tda.Module.Assembly, tdb.Module.Assembly); } bool Equals(TypeDef a, IModule bMod, TypeRef b) { if (Equals(a.Module, bMod) && Equals(a.DefinitionAssembly, b.DefinitionAssembly)) return true; // Could be an exported type. Resolve it and check again. var td = b.Resolve(sourceModule); if (td is null) return false; if (!DontCheckTypeEquivalence) { if (TIAHelper.Equivalent(a, td)) return true; } return Equals(a.Module, td.Module) && Equals(a.DefinitionAssembly, td.DefinitionAssembly); } bool Equals(TypeDef a, FileDef bFile, ExportedType b) { if (Equals(a.Module, bFile) && Equals(a.DefinitionAssembly, b.DefinitionAssembly)) return true; var td = b.Resolve(); return td is not null && Equals(a.Module, td.Module) && Equals(a.DefinitionAssembly, td.DefinitionAssembly); } bool TypeDefScopeEquals(TypeDef a, TypeDef b) { if (a is null || b is null) return false; if (!DontCheckTypeEquivalence) { if (TIAHelper.Equivalent(a, b)) return true; } return Equals(a.Module, b.Module); } bool Equals(TypeRef a, IModule ma, TypeRef b, IModule mb) { if (Equals(ma, mb) && Equals(a.DefinitionAssembly, b.DefinitionAssembly)) return true; // Could be exported types. Resolve them and check again. var tda = a.Resolve(sourceModule); var tdb = b.Resolve(sourceModule); return tda is not null && tdb is not null && Equals(tda.Module, tdb.Module) && Equals(tda.DefinitionAssembly, tdb.DefinitionAssembly); } bool Equals(TypeRef a, IModule ma, ExportedType b, FileDef fb) { if (Equals(ma, fb) && Equals(a.DefinitionAssembly, b.DefinitionAssembly)) return true; // Could be an exported type. Resolve it and check again. var tda = a.Resolve(sourceModule); var tdb = b.Resolve(); return tda is not null && tdb is not null && Equals(tda.Module, tdb.Module) && Equals(tda.DefinitionAssembly, tdb.DefinitionAssembly); } bool Equals(Assembly aAsm, IAssembly bAsm, TypeRef b) { if (Equals(bAsm, aAsm)) return true; // Could be an exported type. Resolve it and check again. var td = b.Resolve(sourceModule); return td is not null && Equals(td.Module.Assembly, aAsm); } bool Equals(Assembly aAsm, IAssembly bAsm, ExportedType b) { if (Equals(bAsm, aAsm)) return true; var td = b.Resolve(); return td is not null && Equals(td.Module.Assembly, aAsm); } bool Equals(Type a, IModule bMod, TypeRef b) { if (Equals(bMod, a.Module) && Equals(b.DefinitionAssembly, a.Assembly)) return true; // Could be an exported type. Resolve it and check again. var td = b.Resolve(sourceModule); return td is not null && Equals(td.Module, a.Module) && Equals(td.DefinitionAssembly, a.Assembly); } bool Equals(Type a, FileDef bFile, ExportedType b) { if (Equals(bFile, a.Module) && Equals(b.DefinitionAssembly, a.Assembly)) return true; var td = b.Resolve(); return td is not null && Equals(td.Module, a.Module) && Equals(td.DefinitionAssembly, a.Assembly); } /// /// Compare members /// /// Member #1 /// Member #2 /// true if same, false otherwise public bool Equals(IMemberRef a, IMemberRef b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; IType ta, tb; IField fa, fb; IMethod ma, mb; PropertyDef pa, pb; EventDef ea, eb; if ((ta = a as IType) is not null && (tb = b as IType) is not null) result = Equals(ta, tb); else if ((fa = a as IField) is not null && (fb = b as IField) is not null && fa.IsField && fb.IsField) result = Equals(fa, fb); else if ((ma = a as IMethod) is not null && (mb = b as IMethod) is not null) result = Equals(ma, mb); else if ((pa = a as PropertyDef) is not null && (pb = b as PropertyDef) is not null) result = Equals(pa, pb); else if ((ea = a as EventDef) is not null && (eb = b as EventDef) is not null) result = Equals(ea, eb); else result = false; recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a member /// /// The member /// The hash code public int GetHashCode(IMemberRef a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int result; IType ta; IField fa; IMethod ma; PropertyDef pa; EventDef ea; if ((ta = a as IType) is not null) result = GetHashCode(ta); else if ((fa = a as IField) is not null) result = GetHashCode(fa); else if ((ma = a as IMethod) is not null) result = GetHashCode(ma); else if ((pa = a as PropertyDef) is not null) result = GetHashCode(pa); else if ((ea = a as EventDef) is not null) result = GetHashCode(ea); else result = 0; // Should never be reached recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(ITypeDefOrRef a, ITypeDefOrRef b) => Equals((IType)a, (IType)b); /// /// Gets the hash code of a type /// /// The type /// The hash code public int GetHashCode(ITypeDefOrRef a) => GetHashCode((IType)a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(IType a, IType b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; TypeDef tda, tdb; TypeRef tra, trb; TypeSpec tsa, tsb; TypeSig sa, sb; ExportedType eta, etb; if ((tda = a as TypeDef) is not null & (tdb = b as TypeDef) is not null) result = Equals(tda, tdb); else if ((tra = a as TypeRef) is not null & (trb = b as TypeRef) is not null) result = Equals(tra, trb); else if ((tsa = a as TypeSpec) is not null & (tsb = b as TypeSpec) is not null) result = Equals(tsa, tsb); else if ((sa = a as TypeSig) is not null & (sb = b as TypeSig) is not null) result = Equals(sa, sb); else if ((eta = a as ExportedType) is not null & (etb = b as ExportedType) is not null) result = Equals(eta, etb); else if (tda is not null && trb is not null) result = Equals(tda, trb); // TypeDef vs TypeRef else if (tra is not null && tdb is not null) result = Equals(tdb, tra); // TypeDef vs TypeRef else if (tda is not null && tsb is not null) result = Equals(tda, tsb); // TypeDef vs TypeSpec else if (tsa is not null && tdb is not null) result = Equals(tdb, tsa); // TypeDef vs TypeSpec else if (tda is not null && sb is not null) result = Equals(tda, sb); // TypeDef vs TypeSig else if (sa is not null && tdb is not null) result = Equals(tdb, sa); // TypeDef vs TypeSig else if (tda is not null && etb is not null) result = Equals(tda, etb); // TypeDef vs ExportedType else if (eta is not null && tdb is not null) result = Equals(tdb, eta); // TypeDef vs ExportedType else if (tra is not null && tsb is not null) result = Equals(tra, tsb); // TypeRef vs TypeSpec else if (tsa is not null && trb is not null) result = Equals(trb, tsa); // TypeRef vs TypeSpec else if (tra is not null && sb is not null) result = Equals(tra, sb); // TypeRef vs TypeSig else if (sa is not null && trb is not null) result = Equals(trb, sa); // TypeRef vs TypeSig else if (tra is not null && etb is not null) result = Equals(tra, etb); // TypeRef vs ExportedType else if (eta is not null && trb is not null) result = Equals(trb, eta); // TypeRef vs ExportedType else if (tsa is not null && sb is not null) result = Equals(tsa, sb); // TypeSpec vs TypeSig else if (sa is not null && tsb is not null) result = Equals(tsb, sa); // TypeSpec vs TypeSig else if (tsa is not null && etb is not null) result = Equals(tsa, etb); // TypeSpec vs ExportedType else if (eta is not null && tsb is not null) result = Equals(tsb, eta); // TypeSpec vs ExportedType else if (sa is not null && etb is not null) result = Equals(sa, etb); // TypeSig vs ExportedType else if (eta is not null && sb is not null) result = Equals(sb, eta); // TypeSig vs ExportedType else result = false; // Should never be reached recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a type /// /// The type /// The hash code public int GetHashCode(IType a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; TypeDef td; TypeRef tr; TypeSpec ts; TypeSig sig; ExportedType et; if ((td = a as TypeDef) is not null) hash = GetHashCode(td); else if ((tr = a as TypeRef) is not null) hash = GetHashCode(tr); else if ((ts = a as TypeSpec) is not null) hash = GetHashCode(ts); else if ((sig = a as TypeSig) is not null) hash = GetHashCode(sig); else if ((et = a as ExportedType) is not null) hash = GetHashCode(et); else hash = 0; // Should never be reached recursionCounter.Decrement(); return hash; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeRef a, TypeDef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeDef a, TypeRef b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; IModule bMod; AssemblyRef bAsm; TypeRef dtb; if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; if (tra is not null) { result = Equals(tra, b); goto exit; } } var scope = b.ResolutionScope; if (!Equals_TypeNames(a.Name, b.Name) || !Equals_TypeNamespaces(a.Namespace, b.Namespace)) result = false; else if ((dtb = scope as TypeRef) is not null) // nested type result = Equals(a.DeclaringType, dtb); // Compare enclosing types else if (a.DeclaringType is not null) { // a is nested, b isn't result = false; } else if (DontCompareTypeScope) result = true; else if ((bMod = scope as IModule) is not null) // 'b' is defined in the same assembly as 'a' result = Equals(a, bMod, b); else if ((bAsm = scope as AssemblyRef) is not null) { var aMod = a.Module; result = aMod is not null && Equals(aMod.Assembly, bAsm, b); if (!result) { if (!DontCheckTypeEquivalence) { var tdb = b.Resolve(); result = TypeDefScopeEquals(a, tdb); } } } else { result = false; //TODO: Handle the case where scope is null } if (result && !TypeRefCanReferenceGlobalType && a.IsGlobalModuleType) result = false; exit: ; recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(ExportedType a, TypeDef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeDef a, ExportedType b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; ExportedType dtb; FileDef bFile; AssemblyRef bAsm; if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; if (tra is not null) { result = Equals(tra, b); goto exit; } } var scope = b.Implementation; if (!Equals_TypeNames(a.Name, b.TypeName) || !Equals_TypeNamespaces(a.Namespace, b.TypeNamespace)) result = false; else if ((dtb = scope as ExportedType) is not null) { // nested type result = Equals(a.DeclaringType, dtb); // Compare enclosing types } else if (a.DeclaringType is not null) { result = false; // a is nested, b isn't } else if (DontCompareTypeScope) result = true; else { if ((bFile = scope as FileDef) is not null) result = Equals(a, bFile, b); else if ((bAsm = scope as AssemblyRef) is not null) { var aMod = a.Module; result = aMod is not null && Equals(aMod.Assembly, bAsm, b); } else result = false; if (!result && !DontCheckTypeEquivalence) { var tdb = b.Resolve(); result = TypeDefScopeEquals(a, tdb); } } if (result && !TypeRefCanReferenceGlobalType && a.IsGlobalModuleType) result = false; exit: ; recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSpec a, TypeDef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeDef a, TypeSpec b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; return Equals(a, b.TypeSig); } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSig a, TypeDef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeDef a, TypeSig b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; //************************************************************* // If this code gets updated, update GetHashCode(TypeSig), // Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too //************************************************************* if (b is TypeDefOrRefSig b2) result = Equals(a, (IType)b2.TypeDefOrRef); else if (b is ModifierSig || b is PinnedSig) result = Equals(a, b.Next); else result = false; recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSpec a, TypeRef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeRef a, TypeSpec b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; return Equals(a, b.TypeSig); } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(ExportedType a, TypeRef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeRef a, ExportedType b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; if (!DontProjectWinMDRefs) { a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; } bool result = Equals_TypeNames(a.Name, b.TypeName) && Equals_TypeNamespaces(a.Namespace, b.TypeNamespace) && EqualsScope(a, b); recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSig a, TypeRef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeRef a, TypeSig b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; //************************************************************* // If this code gets updated, update GetHashCode(TypeSig), // Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too //************************************************************* if (b is TypeDefOrRefSig b2) result = Equals(a, (IType)b2.TypeDefOrRef); else if (b is ModifierSig || b is PinnedSig) result = Equals(a, b.Next); else result = false; recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSig a, TypeSpec b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSpec a, TypeSig b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; return Equals(a.TypeSig, b); } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(ExportedType a, TypeSpec b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSpec a, ExportedType b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; return Equals(a.TypeSig, b); } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(ExportedType a, TypeSig b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSig a, ExportedType b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; //************************************************************* // If this code gets updated, update GetHashCode(TypeSig), // Equals(TypeRef,TypeSig) and Equals(TypeSig,ExportedType) too //************************************************************* if (a is TypeDefOrRefSig a2) result = Equals(a2.TypeDefOrRef, b); else if (a is ModifierSig || a is PinnedSig) result = Equals(a.Next, b); else result = false; recursionCounter.Decrement(); return result; } int GetHashCodeGlobalType() { // We don't always know the name+namespace of the global type, eg. when it's // referenced by a ModuleRef. Use the same hash for all global types. return HASHCODE_MAGIC_GLOBAL_TYPE; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeRef a, TypeRef b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; if (!DontProjectWinMDRefs) { a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; } bool result = Equals_TypeNames(a.Name, b.Name) && Equals_TypeNamespaces(a.Namespace, b.Namespace) && EqualsResolutionScope(a, b); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a type /// /// The type /// The hash code public int GetHashCode(TypeRef a) { // ************************************************************************************ // IMPORTANT: This hash code must match the Type/TypeRef/TypeDef/ExportedType // hash code and GetHashCode_FnPtr_SystemIntPtr() method // ************************************************************************************ // See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType() if (a is null) return TypeRefCanReferenceGlobalType ? GetHashCodeGlobalType() : 0; if (!DontProjectWinMDRefs) a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; int hash; hash = GetHashCode_TypeName(a.Name); if (a.ResolutionScope is TypeRef) hash += HASHCODE_MAGIC_NESTED_TYPE; else hash += GetHashCode_TypeNamespace(a.Namespace); return hash; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(ExportedType a, ExportedType b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; if (!DontProjectWinMDRefs) { a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; } bool result = Equals_TypeNames(a.TypeName, b.TypeName) && Equals_TypeNamespaces(a.TypeNamespace, b.TypeNamespace) && EqualsImplementation(a, b); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a type /// /// The type /// The hash code public int GetHashCode(ExportedType a) { // ************************************************************************************ // IMPORTANT: This hash code must match the Type/TypeRef/TypeDef/ExportedType // hash code and GetHashCode_FnPtr_SystemIntPtr() method // ************************************************************************************ // See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType() if (a is null) return TypeRefCanReferenceGlobalType ? GetHashCodeGlobalType() : 0; if (!DontProjectWinMDRefs) a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; int hash; hash = GetHashCode_TypeName(a.TypeName); if (a.Implementation is ExportedType) hash += HASHCODE_MAGIC_NESTED_TYPE; else hash += GetHashCode_TypeNamespace(a.TypeNamespace); return hash; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeDef a, TypeDef b) { if (a == b) return true; if (a is null || b is null) return false; if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; bool result; if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); var trb = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b); if (tra is not null || trb is not null) { result = Equals((IType)tra ?? a, (IType)trb ?? b); goto exit; } } result = Equals_TypeNames(a.Name, b.Name) && Equals_TypeNamespaces(a.Namespace, b.Namespace) && Equals(a.DeclaringType, b.DeclaringType) && (DontCompareTypeScope || TypeDefScopeEquals(a, b)); exit: ; recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a type /// /// The type /// The hash code public int GetHashCode(TypeDef a) { // ************************************************************************************ // IMPORTANT: This hash code must match the Type/TypeRef/TypeDef/ExportedType // hash code and GetHashCode_FnPtr_SystemIntPtr() method // ************************************************************************************ // See GetHashCode(Type) for the reason why null returns GetHashCodeGlobalType() if (a is null || a.IsGlobalModuleType) return GetHashCodeGlobalType(); if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); if (tra is not null) return GetHashCode(tra); } int hash; hash = GetHashCode_TypeName(a.Name); if (a.DeclaringType is not null) hash += HASHCODE_MAGIC_NESTED_TYPE; else hash += GetHashCode_TypeNamespace(a.Namespace); return hash; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSpec a, TypeSpec b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals(a.TypeSig, b.TypeSig); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a type /// /// The type /// The hash code public int GetHashCode(TypeSpec a) { if (a is null) return 0; return GetHashCode(a.TypeSig); } /// /// Compares resolution scopes /// /// Type #1 /// Type #2 /// true if same, false otherwise bool EqualsResolutionScope(TypeRef a, TypeRef b) { if (a == b) return true; if (a is null || b is null) return false; var ra = a.ResolutionScope; var rb = b.ResolutionScope; if (ra == rb) return true; if (ra is null || rb is null) return false; if (!recursionCounter.Increment()) return false; bool result; TypeRef ea, eb; IModule ma, mb; AssemblyRef aa, ab; ModuleDef modDef; bool resolveCheck = true; // if one of them is a TypeRef, the other one must be too if ((ea = ra as TypeRef) is not null | (eb = rb as TypeRef) is not null) { result = Equals(ea, eb); resolveCheck = false; } else if (DontCompareTypeScope) result = true; // only compare if both are modules else if ((ma = ra as IModule) is not null & (mb = rb as IModule) is not null) result = Equals(a, ma, b, mb); // only compare if both are assemblies else if ((aa = ra as AssemblyRef) is not null & (ab = rb as AssemblyRef) is not null) result = Equals(aa, a, ab, b); else if (aa is not null && rb is ModuleRef) { var bMod = b.Module; result = bMod is not null && Equals(bMod.Assembly, b, aa, a); } else if (ab is not null && ra is ModuleRef) { var aMod = a.Module; result = aMod is not null && Equals(aMod.Assembly, a, ab, b); } else if (aa is not null && (modDef = rb as ModuleDef) is not null) result = Equals(modDef.Assembly, aa, a); else if (ab is not null && (modDef = ra as ModuleDef) is not null) result = Equals(modDef.Assembly, ab, b); else { result = false; resolveCheck = false; } if (!result && resolveCheck) { if (!DontCheckTypeEquivalence) { var td1 = a.Resolve(); var td2 = b.Resolve(); if (td1 is not null && td2 is not null) result = TypeDefScopeEquals(td1, td2); } } recursionCounter.Decrement(); return result; } /// /// Compares implementation /// /// Type #1 /// Type #2 /// true if same, false otherwise bool EqualsImplementation(ExportedType a, ExportedType b) { if (a == b) return true; if (a is null || b is null) return false; var ia = a.Implementation; var ib = b.Implementation; if (ia == ib) return true; if (ia is null || ib is null) return false; if (!recursionCounter.Increment()) return false; bool result; ExportedType ea, eb; FileDef fa, fb; AssemblyRef aa, ab; bool checkResolve = true; // if one of them is an ExportedType, the other one must be too if ((ea = ia as ExportedType) is not null | (eb = ib as ExportedType) is not null) { result = Equals(ea, eb); checkResolve = false; } else if (DontCompareTypeScope) result = true; // only compare if both are files else if ((fa = ia as FileDef) is not null & (fb = ib as FileDef) is not null) result = Equals(fa, fb); // only compare if both are assemblies else if ((aa = ia as AssemblyRef) is not null & (ab = ib as AssemblyRef) is not null) result = Equals(aa, a, ab, b); else if (fa is not null && ab is not null) result = Equals(a.DefinitionAssembly, ab, b); else if (fb is not null && aa is not null) result = Equals(b.DefinitionAssembly, aa, a); else { result = false; checkResolve = false; } if (!result && checkResolve && !DontCheckTypeEquivalence) { var td1 = a.Resolve(); var td2 = b.Resolve(); if (td1 is not null && td2 is not null) result = TypeDefScopeEquals(td1, td2); } recursionCounter.Decrement(); return result; } /// /// Compares resolution scope and implementation /// /// Type #1 /// Type #2 /// true if same, false otherwise bool EqualsScope(TypeRef a, ExportedType b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; var ra = a.ResolutionScope; var ib = b.Implementation; if (ra == ib) return true; if (ra is null || ib is null) return false; if (!recursionCounter.Increment()) return false; bool result; TypeRef ea; ExportedType eb; IModule ma; FileDef fb; AssemblyRef aa, ab; bool checkResolve = true; // If one is a nested type, the other one must be too if ((ea = ra as TypeRef) is not null | (eb = ib as ExportedType) is not null) { result = Equals(ea, eb); checkResolve = false; } else if (DontCompareTypeScope) result = true; else if ((ma = ra as IModule) is not null & (fb = ib as FileDef) is not null) result = Equals(a, ma, b, fb); else if ((aa = ra as AssemblyRef) is not null & (ab = ib as AssemblyRef) is not null) result = Equals(aa, a, ab, b); else if (ma is not null && ab is not null) result = Equals(a.DefinitionAssembly, ab, b); else if (fb is not null && aa is not null) result = Equals(b.DefinitionAssembly, aa, a); else { checkResolve = false; result = false; } if (!result && checkResolve && !DontCheckTypeEquivalence) { var td1 = a.Resolve(); var td2 = b.Resolve(); if (td1 is not null && td2 is not null) result = TypeDefScopeEquals(td1, td2); } recursionCounter.Decrement(); return result; } /// /// Compares files /// /// File #1 /// File #2 /// true if same, false otherwise bool Equals(FileDef a, FileDef b) { if (a == b) return true; if (a is null || b is null) return false; return UTF8String.CaseInsensitiveEquals(a.Name, b.Name); } /// /// Compares a module with a file /// /// Module /// File /// true if same, false otherwise bool Equals(IModule a, FileDef b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; //TODO: You should compare against the module's file name, not the name in the metadata! return UTF8String.CaseInsensitiveEquals(a.Name, b.Name); } /// /// Compares modules /// /// Module #1 /// Module #2 /// true if same, false otherwise internal bool Equals(IModule a, IModule b) { if (a == b) return true; if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; return UTF8String.CaseInsensitiveEquals(a.Name, b.Name); } static bool IsCorLib(ModuleDef a) => a is not null && a.IsManifestModule && a.Assembly.IsCorLib(); static bool IsCorLib(IModule a) { var mod = a as ModuleDef; return mod is not null && mod.IsManifestModule && mod.Assembly.IsCorLib(); } static bool IsCorLib(Module a) => a is not null && a.Assembly.ManifestModule == a && a.Assembly == typeof(void).Assembly; static bool IsCorLib(IAssembly a) => a.IsCorLib(); static bool IsCorLib(Assembly a) => a == typeof(void).Assembly; /// /// Compares modules /// /// Module #1 /// Module #2 /// true if same, false otherwise bool Equals(ModuleDef a, ModuleDef b) { if (a == b) return true; if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; if (!recursionCounter.Increment()) return false; bool result = Equals((IModule)a, (IModule)b) && Equals(a.Assembly, b.Assembly); recursionCounter.Decrement(); return result; } /// /// Compares assemblies /// /// Assembly #1 /// Assembly #2 /// true if same, false otherwise bool Equals(IAssembly a, IAssembly b) { if (a == b) return true; if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; if (!recursionCounter.Increment()) return false; bool result = UTF8String.CaseInsensitiveEquals(a.Name, b.Name) && (!CompareAssemblyPublicKeyToken || PublicKeyBase.TokenEquals(a.PublicKeyOrToken, b.PublicKeyOrToken)) && (!CompareAssemblyVersion || Utils.Equals(a.Version, b.Version)) && (!CompareAssemblyLocale || Utils.LocaleEquals(a.Culture, b.Culture)); recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSig a, TypeSig b) { if (IgnoreModifiers) { a = a.RemoveModifiers(); b = b.RemoveModifiers(); } if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (!DontProjectWinMDRefs) { a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; } if (a.ElementType != b.ElementType) { // Signatures must be identical. It's possible to have a U4 in a sig (short form // of System.UInt32), or a ValueType + System.UInt32 TypeRef (long form), but these // should not match in a sig (also the long form is invalid). result = false; } else { switch (a.ElementType) { case ElementType.Void: case ElementType.Boolean: case ElementType.Char: case ElementType.I1: case ElementType.U1: case ElementType.I2: case ElementType.U2: case ElementType.I4: case ElementType.U4: case ElementType.I8: case ElementType.U8: case ElementType.R4: case ElementType.R8: case ElementType.String: case ElementType.TypedByRef: case ElementType.I: case ElementType.U: case ElementType.Object: case ElementType.Sentinel: result = true; break; case ElementType.Ptr: case ElementType.ByRef: case ElementType.SZArray: case ElementType.Pinned: result = Equals(a.Next, b.Next); break; case ElementType.Array: ArraySig ara = a as ArraySig, arb = b as ArraySig; result = ara.Rank == arb.Rank && (IgnoreMultiDimensionalArrayLowerBoundsAndSizes || (Equals(ara.Sizes, arb.Sizes) && Equals(ara.LowerBounds, arb.LowerBounds))) && Equals(a.Next, b.Next); break; case ElementType.ValueType: case ElementType.Class: if (RawSignatureCompare) result = TokenEquals((a as ClassOrValueTypeSig).TypeDefOrRef, (b as ClassOrValueTypeSig).TypeDefOrRef); else result = Equals((IType)(a as ClassOrValueTypeSig).TypeDefOrRef, (IType)(b as ClassOrValueTypeSig).TypeDefOrRef); break; case ElementType.Var: case ElementType.MVar: result = (a as GenericSig).Number == (b as GenericSig).Number; break; case ElementType.GenericInst: var gia = (GenericInstSig)a; var gib = (GenericInstSig)b; if (RawSignatureCompare) { var gt1 = gia.GenericType; var gt2 = gib.GenericType; result = TokenEquals(gt1?.TypeDefOrRef, gt2?.TypeDefOrRef) && Equals(gia.GenericArguments, gib.GenericArguments); } else { result = Equals(gia.GenericType, gib.GenericType) && Equals(gia.GenericArguments, gib.GenericArguments); } break; case ElementType.FnPtr: result = Equals((a as FnPtrSig).Signature, (b as FnPtrSig).Signature); break; case ElementType.CModReqd: case ElementType.CModOpt: if (RawSignatureCompare) result = TokenEquals((a as ModifierSig).Modifier, (b as ModifierSig).Modifier) && Equals(a.Next, b.Next); else result = Equals((IType)(a as ModifierSig).Modifier, (IType)(b as ModifierSig).Modifier) && Equals(a.Next, b.Next); break; case ElementType.ValueArray: result = (a as ValueArraySig).Size == (b as ValueArraySig).Size && Equals(a.Next, b.Next); break; case ElementType.Module: result = (a as ModuleSig).Index == (b as ModuleSig).Index && Equals(a.Next, b.Next); break; case ElementType.End: case ElementType.R: case ElementType.Internal: default: result = false; break; } } recursionCounter.Decrement(); return result; } static bool TokenEquals(ITypeDefOrRef a, ITypeDefOrRef b) { if (a == b) return true; if (a is null || b is null) return false; return a.MDToken == b.MDToken; } /// /// Gets the hash code of a type /// /// The type /// The hash code public int GetHashCode(TypeSig a) => GetHashCode(a, true); int GetHashCode(TypeSig a, bool substituteGenericParameters) { // ******************************************** // IMPORTANT: This must match GetHashCode(Type) // ******************************************** if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; if (substituteGenericParameters && genericArguments is not null) { var t = a; a = genericArguments.Resolve(a); substituteGenericParameters = t == a; } switch (a.ElementType) { case ElementType.Void: case ElementType.Boolean: case ElementType.Char: case ElementType.I1: case ElementType.U1: case ElementType.I2: case ElementType.U2: case ElementType.I4: case ElementType.U4: case ElementType.I8: case ElementType.U8: case ElementType.R4: case ElementType.R8: case ElementType.String: case ElementType.TypedByRef: case ElementType.I: case ElementType.U: case ElementType.Object: case ElementType.ValueType: case ElementType.Class: // When comparing an ExportedType/TypeDef/TypeRef to a TypeDefOrRefSig/Class/ValueType, // the ET is ignored, so we must ignore it when calculating the hash. hash = GetHashCode((IType)(a as TypeDefOrRefSig).TypeDefOrRef); break; case ElementType.Sentinel: hash = HASHCODE_MAGIC_ET_SENTINEL; break; case ElementType.Ptr: hash = HASHCODE_MAGIC_ET_PTR + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.ByRef: hash = HASHCODE_MAGIC_ET_BYREF + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.SZArray: hash = HASHCODE_MAGIC_ET_SZARRAY + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.CModReqd: case ElementType.CModOpt: case ElementType.Pinned: // When comparing an ExportedType/TypeDef/TypeRef to a ModifierSig/PinnedSig, // the ET is ignored, so we must ignore it when calculating the hash. hash = GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.Array: // Don't include sizes and lower bounds since GetHashCode(Type) doesn't (and can't). // Also, if IgnoreMultiDimensionArrayLowerBoundsAndSizes is set, we shouldn't include them either. var ara = (ArraySig)a; hash = HASHCODE_MAGIC_ET_ARRAY + (int)ara.Rank + GetHashCode(ara.Next, substituteGenericParameters); break; case ElementType.Var: hash = HASHCODE_MAGIC_ET_VAR + (int)(a as GenericVar).Number; break; case ElementType.MVar: hash = HASHCODE_MAGIC_ET_MVAR + (int)(a as GenericMVar).Number; break; case ElementType.GenericInst: var gia = (GenericInstSig)a; hash = HASHCODE_MAGIC_ET_GENERICINST; hash += GetHashCode(gia.GenericType, substituteGenericParameters); hash += GetHashCode(gia.GenericArguments, substituteGenericParameters); break; case ElementType.FnPtr: hash = GetHashCode_FnPtr_SystemIntPtr(); break; case ElementType.ValueArray: hash = HASHCODE_MAGIC_ET_VALUEARRAY + (int)(a as ValueArraySig).Size + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.Module: hash = HASHCODE_MAGIC_ET_MODULE + (int)(a as ModuleSig).Index + GetHashCode(a.Next, substituteGenericParameters); break; case ElementType.End: case ElementType.R: case ElementType.Internal: default: hash = 0; break; } recursionCounter.Decrement(); return hash; } /// /// Compares type lists /// /// Type list #1 /// Type list #2 /// true if same, false otherwise public bool Equals(IList a, IList b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (a.Count != b.Count) result = false; else { int i; for (i = 0; i < a.Count; i++) { if (!Equals(a[i], b[i])) break; } result = i == a.Count; } recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a type list /// /// The type list /// The hash code public int GetHashCode(IList a) => GetHashCode(a, true); int GetHashCode(IList a, bool substituteGenericParameters) { //************************************************************************ // IMPORTANT: This code must match any other GetHashCode(IList) //************************************************************************ if (a is null) return 0; if (!recursionCounter.Increment()) return 0; uint hash = 0; for (int i = 0; i < a.Count; i++) { hash += (uint)GetHashCode(a[i], substituteGenericParameters); hash = (hash << 13) | (hash >> 19); } recursionCounter.Decrement(); return (int)hash; } bool Equals(IList a, IList b) { if (a == b) return true; if (a is null || b is null) return false; if (a.Count != b.Count) return false; for (int i = 0; i < a.Count; i++) { if (a[i] != b[i]) return false; } return true; } bool Equals(IList a, IList b) { if (a == b) return true; if (a is null || b is null) return false; if (a.Count != b.Count) return false; for (int i = 0; i < a.Count; i++) { if (a[i] != b[i]) return false; } return true; } /// /// Compares signatures /// /// Sig #1 /// Sig #2 /// true if same, false otherwise public bool Equals(CallingConventionSig a, CallingConventionSig b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (a.GetCallingConvention() != b.GetCallingConvention()) result = false; else { switch (a.GetCallingConvention() & CallingConvention.Mask) { case CallingConvention.Default: case CallingConvention.C: case CallingConvention.StdCall: case CallingConvention.ThisCall: case CallingConvention.FastCall: case CallingConvention.VarArg: case CallingConvention.Property: case CallingConvention.NativeVarArg: case CallingConvention.Unmanaged: MethodBaseSig ma = a as MethodBaseSig, mb = b as MethodBaseSig; result = ma is not null && mb is not null && Equals(ma, mb); break; case CallingConvention.Field: FieldSig fa = a as FieldSig, fb = b as FieldSig; result = fa is not null && fb is not null && Equals(fa, fb); break; case CallingConvention.LocalSig: LocalSig la = a as LocalSig, lb = b as LocalSig; result = la is not null && lb is not null && Equals(la, lb); break; case CallingConvention.GenericInst: GenericInstMethodSig ga = a as GenericInstMethodSig, gb = b as GenericInstMethodSig; result = ga is not null && gb is not null && Equals(ga, gb); break; default: result = false; break; } } recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a sig /// /// The sig /// The hash code public int GetHashCode(CallingConventionSig a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; switch (a.GetCallingConvention() & CallingConvention.Mask) { case CallingConvention.Default: case CallingConvention.C: case CallingConvention.StdCall: case CallingConvention.ThisCall: case CallingConvention.FastCall: case CallingConvention.VarArg: case CallingConvention.Property: case CallingConvention.NativeVarArg: case CallingConvention.Unmanaged: var ma = a as MethodBaseSig; hash = ma is null ? 0 : GetHashCode(ma); break; case CallingConvention.Field: var fa = a as FieldSig; hash = fa is null ? 0 : GetHashCode(fa); break; case CallingConvention.LocalSig: var la = a as LocalSig; hash = la is null ? 0 : GetHashCode(la); break; case CallingConvention.GenericInst: var ga = a as GenericInstMethodSig; hash = ga is null ? 0 : GetHashCode(ga); break; default: hash = GetHashCode_CallingConvention(a); break; } recursionCounter.Decrement(); return hash; } /// /// Compares method/property sigs /// /// Method/property #1 /// Method/property #2 /// true if same, false otherwise public bool Equals(MethodBaseSig a, MethodBaseSig b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = a.GetCallingConvention() == b.GetCallingConvention() && (DontCompareReturnType || Equals(a.RetType, b.RetType)) && Equals(a.Params, b.Params) && (!a.Generic || a.GenParamCount == b.GenParamCount) && (!CompareSentinelParams || Equals(a.ParamsAfterSentinel, b.ParamsAfterSentinel)); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a method/property sig /// /// The method/property sig /// The hash code public int GetHashCode(MethodBaseSig a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; hash = GetHashCode_CallingConvention(a) + GetHashCode(a.Params); if (!DontCompareReturnType) hash += GetHashCode(a.RetType); if (a.Generic) hash += GetHashCode_ElementType_MVar((int)a.GenParamCount); if (CompareSentinelParams) hash += GetHashCode(a.ParamsAfterSentinel); recursionCounter.Decrement(); return hash; } int GetHashCode_CallingConvention(CallingConventionSig a) => GetHashCode(a.GetCallingConvention()); int GetHashCode(CallingConvention a) { //******************************************************************* // IMPORTANT: This hash must match the Reflection call conv hash code //******************************************************************* switch (a & CallingConvention.Mask) { case CallingConvention.Default: case CallingConvention.C: case CallingConvention.StdCall: case CallingConvention.ThisCall: case CallingConvention.FastCall: case CallingConvention.VarArg: case CallingConvention.Property: case CallingConvention.GenericInst: case CallingConvention.Unmanaged: case CallingConvention.NativeVarArg: case CallingConvention.Field: return (int)(a & (CallingConvention.Generic | CallingConvention.HasThis | CallingConvention.ExplicitThis)); case CallingConvention.LocalSig: default: return (int)a; } } /// /// Compares field sigs /// /// Field sig #1 /// Field sig #2 /// true if same, false otherwise public bool Equals(FieldSig a, FieldSig b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = a.GetCallingConvention() == b.GetCallingConvention() && Equals(a.Type, b.Type); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a field sig /// /// The field sig /// The hash code public int GetHashCode(FieldSig a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; hash = GetHashCode_CallingConvention(a) + GetHashCode(a.Type); recursionCounter.Decrement(); return hash; } /// /// Compares local sigs /// /// Local sig #1 /// Local sig #2 /// true if same, false otherwise public bool Equals(LocalSig a, LocalSig b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = a.GetCallingConvention() == b.GetCallingConvention() && Equals(a.Locals, b.Locals); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a local sig /// /// The local sig /// The hash code public int GetHashCode(LocalSig a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; hash = GetHashCode_CallingConvention(a) + GetHashCode(a.Locals); recursionCounter.Decrement(); return hash; } /// /// Compares generic method instance sigs /// /// Generic inst method #1 /// Generic inst method #2 /// true if same, false otherwise public bool Equals(GenericInstMethodSig a, GenericInstMethodSig b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = a.GetCallingConvention() == b.GetCallingConvention() && Equals(a.GenericArguments, b.GenericArguments); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a generic instance method sig /// /// The generic inst method sig /// The hash code public int GetHashCode(GenericInstMethodSig a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; hash = GetHashCode_CallingConvention(a) + GetHashCode(a.GenericArguments); recursionCounter.Decrement(); return hash; } /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(IMethod a, IMethod b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; MethodDef mda, mdb; MemberRef mra, mrb; MethodSpec msa, msb; if ((mda = a as MethodDef) is not null & (mdb = b as MethodDef) is not null) result = Equals(mda, mdb); else if ((mra = a as MemberRef) is not null & (mrb = b as MemberRef) is not null) result = Equals(mra, mrb); else if ((msa = a as MethodSpec) is not null && (msb = b as MethodSpec) is not null) result = Equals(msa, msb); else if (mda is not null && mrb is not null) result = Equals(mda, mrb); else if (mra is not null && mdb is not null) result = Equals(mdb, mra); else result = false; recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a method /// /// The method /// The hash code public int GetHashCode(IMethod a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; MethodDef mda; MemberRef mra; MethodSpec msa; if ((mda = a as MethodDef) is not null) hash = GetHashCode(mda); else if ((mra = a as MemberRef) is not null) hash = GetHashCode(mra); else if ((msa = a as MethodSpec) is not null) hash = GetHashCode(msa); else hash = 0; recursionCounter.Decrement(); return hash; } /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MemberRef a, MethodDef b) => Equals(b, a); /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodDef a, MemberRef b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (!DontProjectWinMDRefs) { var mra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; if (mra is not null) { result = Equals(mra, b); goto exit; } } result = (PrivateScopeMethodIsComparable || !a.IsPrivateScope) && Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.Signature, b.Signature) && (!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.Class)); exit: ; recursionCounter.Decrement(); return result; } /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodDef a, MethodDef b) { if (a == b) return true; if (a is null || b is null) return false; if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; bool result; if (!DontProjectWinMDRefs) { var mra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); var mrb = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b); if (mra is not null || mrb is not null) { result = Equals((IMethod)mra ?? a, (IMethod)mrb ?? b); goto exit; } } result = Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.Signature, b.Signature) && (!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); exit: ; recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a method /// /// The method /// The hash code public int GetHashCode(MethodDef a) { // *********************************************************************** // IMPORTANT: This hash code must match the MemberRef/MethodBase hash code // *********************************************************************** if (a is null) return 0; if (!DontProjectWinMDRefs) { var mra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); if (mra is not null) return GetHashCode(mra); } if (!recursionCounter.Increment()) return 0; int hash = GetHashCode_MethodFieldName(a.Name) + GetHashCode(a.Signature); if (CompareMethodFieldDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } /// /// Compares MemberRefs /// /// MemberRef #1 /// MemberRef #2 /// true if same, false otherwise public bool Equals(MemberRef a, MemberRef b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; if (!DontProjectWinMDRefs) { a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; b = WinMDHelpers.ToCLR(b.Module ?? sourceModule, b) ?? b; } bool result = Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.Signature, b.Signature) && (!CompareMethodFieldDeclaringType || Equals(a.Class, b.Class)); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a MemberRef /// /// The MemberRef /// The hash code public int GetHashCode(MemberRef a) { // ******************************************************************************** // IMPORTANT: This hash code must match the MethodDef/FieldDef/MethodBase hash code // ******************************************************************************** if (a is null) return 0; if (!recursionCounter.Increment()) return 0; if (!DontProjectWinMDRefs) a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; int hash = GetHashCode_MethodFieldName(a.Name); GenericInstSig git; if (CompareMethodFieldDeclaringType && !DontSubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) is not null) { InitializeGenericArguments(); genericArguments.PushTypeArgs(git.GenericArguments); hash += GetHashCode(a.Signature); genericArguments.PopTypeArgs(); } else hash += GetHashCode(a.Signature); if (CompareMethodFieldDeclaringType) hash += GetHashCode(a.Class); recursionCounter.Decrement(); return hash; } /// /// Compares MethodSpecs /// /// MethodSpec #1 /// MethodSpec #2 /// true if same, false otherwise public bool Equals(MethodSpec a, MethodSpec b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals(a.Method, b.Method) && Equals(a.Instantiation, b.Instantiation); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a MethodSpec /// /// The MethodSpec /// The hash code public int GetHashCode(MethodSpec a) { // ************************************************************* // IMPORTANT: This hash code must match the MethodBase hash code // ************************************************************* if (a is null) return 0; if (!recursionCounter.Increment()) return 0; var gim = a.GenericInstMethodSig; if (gim is not null) { InitializeGenericArguments(); genericArguments.PushMethodArgs(gim.GenericArguments); } int hash = GetHashCode(a.Method); if (gim is not null) genericArguments.PopMethodArgs(); recursionCounter.Decrement(); return hash; } /// /// Compares MemberRefParents /// /// MemberRefParent #1 /// MemberRefParent #2 /// true if same, false otherwise bool Equals(IMemberRefParent a, IMemberRefParent b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; ITypeDefOrRef ita, itb; ModuleRef moda, modb; MethodDef ma, mb; TypeDef td; if ((ita = a as ITypeDefOrRef) is not null && (itb = b as ITypeDefOrRef) is not null) result = Equals((IType)ita, (IType)itb); else if ((moda = a as ModuleRef) is not null & (modb = b as ModuleRef) is not null) { ModuleDef omoda = moda.Module, omodb = modb.Module; result = Equals((IModule)moda, (IModule)modb) && Equals(omoda?.Assembly, omodb?.Assembly); } else if ((ma = a as MethodDef) is not null && (mb = b as MethodDef) is not null) result = Equals(ma, mb); else if (modb is not null && (td = a as TypeDef) is not null) result = EqualsGlobal(td, modb); else if (moda is not null && (td = b as TypeDef) is not null) result = EqualsGlobal(td, moda); else result = false; recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a MemberRefParent /// /// The MemberRefParent /// The hash code int GetHashCode(IMemberRefParent a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; ITypeDefOrRef ita; MethodDef ma; if ((ita = a as ITypeDefOrRef) is not null) hash = GetHashCode((IType)ita); else if (a is ModuleRef) hash = GetHashCodeGlobalType(); else if ((ma = a as MethodDef) is not null) { // Only use the declaring type so we get the same hash code when hashing a MethodBase. hash = GetHashCode(ma.DeclaringType); } else hash = 0; recursionCounter.Decrement(); return hash; } /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(IField a, IField b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; FieldDef fa, fb; MemberRef ma, mb; if ((fa = a as FieldDef) is not null & (fb = b as FieldDef) is not null) result = Equals(fa, fb); else if ((ma = a as MemberRef) is not null & (mb = b as MemberRef) is not null) result = Equals(ma, mb); else if (fa is not null && mb is not null) result = Equals(fa, mb); else if (fb is not null && ma is not null) result = Equals(fb, ma); else result = false; recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a field /// /// The field /// The hash code public int GetHashCode(IField a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; FieldDef fa; MemberRef ma; if ((fa = a as FieldDef) is not null) hash = GetHashCode(fa); else if ((ma = a as MemberRef) is not null) hash = GetHashCode(ma); else hash = 0; recursionCounter.Decrement(); return hash; } /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(MemberRef a, FieldDef b) => Equals(b, a); /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(FieldDef a, MemberRef b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = (PrivateScopeFieldIsComparable || !a.IsPrivateScope) && Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.Signature, b.Signature) && (!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.Class)); recursionCounter.Decrement(); return result; } /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(FieldDef a, FieldDef b) { if (a == b) return true; if (a is null || b is null) return false; if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.Signature, b.Signature) && (!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a field /// /// The field /// The hash code public int GetHashCode(FieldDef a) { // ********************************************************************** // IMPORTANT: This hash code must match the MemberRef/FieldInfo hash code // ********************************************************************** if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash = GetHashCode_MethodFieldName(a.Name) + GetHashCode(a.Signature); if (CompareMethodFieldDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } /// /// Compares properties /// /// Property #1 /// Property #2 /// true if same, false otherwise public bool Equals(PropertyDef a, PropertyDef b) { if (a == b) return true; if (a is null || b is null) return false; if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_PropertyNames(a.Name, b.Name) && Equals(a.Type, b.Type) && (!ComparePropertyDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a property /// /// The property /// The hash code public int GetHashCode(PropertyDef a) { // *************************************************************** // IMPORTANT: This hash code must match the PropertyInfo hash code // *************************************************************** if (a is null) return 0; if (!recursionCounter.Increment()) return 0; var sig = a.PropertySig; int hash = GetHashCode_PropertyName(a.Name) + GetHashCode(sig?.RetType); if (ComparePropertyDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } /// /// Compares events /// /// Event #1 /// Event #2 /// true if same, false otherwise public bool Equals(EventDef a, EventDef b) { if (a == b) return true; if (a is null || b is null) return false; if (ReferenceCompareForMemberDefsInSameModule && InSameModule(a, b)) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_EventNames(a.Name, b.Name) && Equals((IType)a.EventType, (IType)b.EventType) && (!CompareEventDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of an event /// /// The event /// The hash code public int GetHashCode(EventDef a) { // ************************************************************ // IMPORTANT: This hash code must match the EventInfo hash code // ************************************************************ if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash = GetHashCode_EventName(a.Name) + GetHashCode((IType)a.EventType); if (CompareEventDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } // Compares a with b, and a must be the global type bool EqualsGlobal(TypeDef a, ModuleRef b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = a.IsGlobalModuleType && Equals((IModule)a.Module, (IModule)b) && Equals(a.DefinitionAssembly, GetAssembly(b.Module)); recursionCounter.Decrement(); return result; } static AssemblyDef GetAssembly(ModuleDef module) => module?.Assembly; /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(Type a, IType b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(IType a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. if (a is null) return false; if (!recursionCounter.Increment()) return false; bool result; TypeDef td; TypeRef tr; TypeSpec ts; TypeSig sig; ExportedType et; if ((td = a as TypeDef) is not null) result = Equals(td, b); else if ((tr = a as TypeRef) is not null) result = Equals(tr, b); else if ((ts = a as TypeSpec) is not null) result = Equals(ts, b); else if ((sig = a as TypeSig) is not null) result = Equals(sig, b); else if ((et = a as ExportedType) is not null) result = Equals(et, b); else result = false; recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(Type a, TypeDef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeDef a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. if (a is null) return false; if (b is null) return a.IsGlobalModuleType; if (!recursionCounter.Increment()) return false; bool result; if (!DontProjectWinMDRefs) { var tra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); if (tra is not null) { result = Equals(tra, b); goto exit; } } result = !b.HasElementType && Equals_TypeNames(a.Name, ReflectionExtensions.Unescape(b.Name)) && Equals_TypeNamespaces(a.Namespace, b) && EnclosingTypeEquals(a.DeclaringType, b.DeclaringType) && (DontCompareTypeScope || Equals(a.Module, b.Module)); exit: ; recursionCounter.Decrement(); return result; } bool EnclosingTypeEquals(TypeDef a, Type b) { // b is null doesn't mean that b is the global type if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; return Equals(a, b); } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(Type a, TypeRef b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeRef a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. if (a is null) return false; if (b is null) return false; // Must use a ModuleRef to reference the global type, so always fail if (!recursionCounter.Increment()) return false; bool result; TypeRef dta; IModule aMod; AssemblyRef aAsm; if (!DontProjectWinMDRefs) a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; var scope = a.ResolutionScope; if (!b.IsTypeDef()) result = false; else if (!Equals_TypeNames(a.Name, ReflectionExtensions.Unescape(b.Name)) || !Equals_TypeNamespaces(a.Namespace, b)) result = false; else if ((dta = scope as TypeRef) is not null) // nested type result = Equals(dta, b.DeclaringType); // Compare enclosing types else if (b.IsNested) result = false; // b is nested, a isn't else if (DontCompareTypeScope) result = true; else if ((aMod = scope as IModule) is not null) // 'a' is defined in the same assembly as 'b' result = Equals(b, aMod, a); else if ((aAsm = scope as AssemblyRef) is not null) result = Equals(b.Assembly, aAsm, a); else { result = false; //TODO: Handle the case where scope is null } recursionCounter.Decrement(); return result; } bool Equals_TypeNamespaces(UTF8String a, Type b) { if (b.IsNested) return true; return Equals_TypeNamespaces(a, b.Namespace); } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(Type a, TypeSpec b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSpec a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. if (a is null) return false; if (b is null) return false; // Must use a ModuleRef to reference the global type, so always fail return Equals(a.TypeSig, b); } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(Type a, TypeSig b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(TypeSig a, Type b) => Equals(a, b, null, false); bool Equals(ITypeDefOrRef a, Type b, Type declaringType) { if (a is TypeSpec ts) return Equals(ts.TypeSig, b, declaringType); return Equals(a, b); } /// /// Checks whether it's FnPtr&, FnPtr*, FnPtr[], or FnPtr[...] /// /// The type static bool IsFnPtrElementType(Type a) { if (a is null || !a.HasElementType) return false; var et = a.GetElementType(); if (et is null || et.HasElementType) return false; if (et != typeof(IntPtr)) // FnPtr is mapped to System.IntPtr return false; if (!a.FullName.StartsWith("(fnptr)")) return false; return true; } /// /// Compares types /// /// Type #1 /// Type #2 /// Root declaring type to check if we should /// treat as a generic instance type /// true if we should treat /// as a generic instance type /// true if same, false otherwise bool Equals(TypeSig a, Type b, Type declaringType, bool? treatAsGenericInst = null) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. if (a is null) return false; if (b is null) return false; // Must use a ModuleRef to reference the global type, so always fail if (!recursionCounter.Increment()) return false; bool result; bool treatAsGenericInst2 = treatAsGenericInst ?? declaringType.MustTreatTypeAsGenericInstType(b); if (genericArguments is not null) a = genericArguments.Resolve(a); switch (a.ElementType) { case ElementType.Void: case ElementType.Boolean: case ElementType.Char: case ElementType.I1: case ElementType.U1: case ElementType.I2: case ElementType.U2: case ElementType.I4: case ElementType.U4: case ElementType.I8: case ElementType.U8: case ElementType.R4: case ElementType.R8: case ElementType.String: case ElementType.TypedByRef: case ElementType.I: case ElementType.U: case ElementType.Object: result = Equals(((TypeDefOrRefSig)a).TypeDefOrRef, b, declaringType); break; case ElementType.Ptr: if (!b.IsPointer) result = false; else if (IsFnPtrElementType(b)) { a = a.Next.RemoveModifiers(); result = a is not null && a.ElementType == ElementType.FnPtr; } else result = Equals(a.Next, b.GetElementType(), declaringType); break; case ElementType.ByRef: if (!b.IsByRef) result = false; else if (IsFnPtrElementType(b)) { a = a.Next.RemoveModifiers(); result = a is not null && a.ElementType == ElementType.FnPtr; } else result = Equals(a.Next, b.GetElementType(), declaringType); break; case ElementType.SZArray: if (!b.IsArray || !b.IsSZArray()) result = false; else if (IsFnPtrElementType(b)) { a = a.Next.RemoveModifiers(); result = a is not null && a.ElementType == ElementType.FnPtr; } else result = Equals(a.Next, b.GetElementType(), declaringType); break; case ElementType.Pinned: result = Equals(a.Next, b, declaringType); break; case ElementType.Array: if (!b.IsArray || b.IsSZArray()) result = false; else { var ara = a as ArraySig; result = ara.Rank == b.GetArrayRank() && (IsFnPtrElementType(b) ? (a = a.Next.RemoveModifiers()) is not null && a.ElementType == ElementType.FnPtr : Equals(a.Next, b.GetElementType(), declaringType)); } break; case ElementType.ValueType: case ElementType.Class: result = Equals((a as ClassOrValueTypeSig).TypeDefOrRef, b, declaringType); break; case ElementType.Var: result = b.IsGenericParameter && b.GenericParameterPosition == (a as GenericSig).Number && b.DeclaringMethod is null; break; case ElementType.MVar: result = b.IsGenericParameter && b.GenericParameterPosition == (a as GenericSig).Number && b.DeclaringMethod is not null; break; case ElementType.GenericInst: if (!(b.IsGenericType && !b.IsGenericTypeDefinition) && !treatAsGenericInst2) { result = false; break; } var gia = (GenericInstSig)a; result = Equals(gia.GenericType, b.GetGenericTypeDefinition(), null, false); result = result && Equals(gia.GenericArguments, b.GetGenericArguments(), declaringType); break; case ElementType.CModReqd: case ElementType.CModOpt: result = Equals(a.Next, b, declaringType); break; case ElementType.FnPtr: // At least in method sigs, this will be mapped to System.IntPtr result = b == typeof(IntPtr); break; case ElementType.Sentinel: case ElementType.ValueArray: case ElementType.Module: case ElementType.End: case ElementType.R: case ElementType.Internal: default: result = false; break; } recursionCounter.Decrement(); return result; } /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(Type a, ExportedType b) => Equals(b, a); /// /// Compares types /// /// Type #1 /// Type #2 /// true if same, false otherwise public bool Equals(ExportedType a, Type b) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. if (a is null) return false; if (b is null) return false; // Must use a ModuleRef to reference the global type, so always fail if (!recursionCounter.Increment()) return false; bool result; ExportedType dta; FileDef aFile; AssemblyRef aAsm; if (!DontProjectWinMDRefs) a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; var scope = a.Implementation; if (!b.IsTypeDef()) result = false; else if (!Equals_TypeNames(a.TypeName, ReflectionExtensions.Unescape(b.Name)) || !Equals_TypeNamespaces(a.TypeNamespace, b)) result = false; else if ((dta = scope as ExportedType) is not null) // nested type result = Equals(dta, b.DeclaringType); // Compare enclosing types else if (b.IsNested) result = false; // b is nested, a isn't else if (DontCompareTypeScope) result = true; else if ((aFile = scope as FileDef) is not null) result = Equals(b, aFile, a); else if ((aAsm = scope as AssemblyRef) is not null) result = Equals(b.Assembly, aAsm, a); else result = false; recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a type /// /// The type /// The hash code public int GetHashCode(Type a) => GetHashCode(a, false); /// /// Gets the hash code of a type /// /// The type /// true if we should treat /// as a generic instance type /// The hash code public int GetHashCode(Type a, bool treatAsGenericInst) => GetHashCode(a, null, treatAsGenericInst); int GetHashCode(Type a, Type declaringType, bool? treatAsGenericInst = null) { // ************************************************************************** // IMPORTANT: This hash code must match the TypeSig/TypeDef/TypeRef hash code // ************************************************************************** if (a is null) // Could be global type return GetHashCode_TypeDef(a); if (!recursionCounter.Increment()) return 0; int hash; bool treatAsGenericInst2 = treatAsGenericInst ?? declaringType.MustTreatTypeAsGenericInstType(a); switch (treatAsGenericInst2 ? ElementType.GenericInst : a.GetElementType2()) { case ElementType.Void: case ElementType.Boolean: case ElementType.Char: case ElementType.I1: case ElementType.U1: case ElementType.I2: case ElementType.U2: case ElementType.I4: case ElementType.U4: case ElementType.I8: case ElementType.U8: case ElementType.R4: case ElementType.R8: case ElementType.String: case ElementType.TypedByRef: case ElementType.I: case ElementType.U: case ElementType.Object: case ElementType.ValueType: case ElementType.Class: hash = GetHashCode_TypeDef(a); break; case ElementType.FnPtr: hash = GetHashCode_FnPtr_SystemIntPtr(); break; case ElementType.Sentinel: hash = HASHCODE_MAGIC_ET_SENTINEL; break; case ElementType.Ptr: hash = HASHCODE_MAGIC_ET_PTR + (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType(), declaringType)); break; case ElementType.ByRef: hash = HASHCODE_MAGIC_ET_BYREF + (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType(), declaringType)); break; case ElementType.SZArray: hash = HASHCODE_MAGIC_ET_SZARRAY + (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType(), declaringType)); break; case ElementType.CModReqd: case ElementType.CModOpt: case ElementType.Pinned: hash = GetHashCode(a.GetElementType(), declaringType); break; case ElementType.Array: // The type doesn't store sizes and lower bounds, so can't use them to // create the hash hash = HASHCODE_MAGIC_ET_ARRAY + a.GetArrayRank() + (IsFnPtrElementType(a) ? GetHashCode_FnPtr_SystemIntPtr() : GetHashCode(a.GetElementType(), declaringType)); break; case ElementType.Var: hash = HASHCODE_MAGIC_ET_VAR + a.GenericParameterPosition; break; case ElementType.MVar: hash = HASHCODE_MAGIC_ET_MVAR + a.GenericParameterPosition; break; case ElementType.GenericInst: hash = HASHCODE_MAGIC_ET_GENERICINST + GetHashCode(a.GetGenericTypeDefinition(), false) + GetHashCode(a.GetGenericArguments(), declaringType); break; case ElementType.ValueArray: case ElementType.Module: case ElementType.End: case ElementType.R: case ElementType.Internal: default: hash = 0; break; } recursionCounter.Decrement(); return hash; } /// /// Gets the hash code of a type list /// /// The type list /// Root declaring type to check if we should /// treat as a generic instance type /// The hash code int GetHashCode(IList a, Type declaringType) { //************************************************************************ // IMPORTANT: This code must match any other GetHashCode(IList) //************************************************************************ if (a is null) return 0; if (!recursionCounter.Increment()) return 0; uint hash = 0; for (int i = 0; i < a.Count; i++) { hash += (uint)GetHashCode(a[i], declaringType); hash = (hash << 13) | (hash >> 19); } recursionCounter.Decrement(); return (int)hash; } /// /// Gets the hash code of a list with only generic method parameters () /// /// Number of generic method parameters /// Hash code static int GetHashCode_ElementType_MVar(int numGenericParams) => GetHashCode(numGenericParams, HASHCODE_MAGIC_ET_MVAR); static int GetHashCode(int numGenericParams, int etypeHashCode) { //************************************************************************ // IMPORTANT: This code must match any other GetHashCode(IList) //************************************************************************ uint hash = 0; for (int i = 0; i < numGenericParams; i++) { hash += (uint)(etypeHashCode + i); hash = (hash << 13) | (hash >> 19); } return (int)hash; } /// /// Gets the hash code of a TypeDef type /// /// The type /// The hash code public int GetHashCode_TypeDef(Type a) { // ************************************************************************************ // IMPORTANT: This hash code must match the Type/TypeRef/TypeDef/ExportedType // hash code and GetHashCode_FnPtr_SystemIntPtr() method // ************************************************************************************ // A global method/field's declaring type is null. This is the reason we must // return GetHashCodeGlobalType() here. if (a is null) return GetHashCodeGlobalType(); int hash; hash = GetHashCode_TypeName(ReflectionExtensions.Unescape(a.Name)); if (a.IsNested) hash += HASHCODE_MAGIC_NESTED_TYPE; else hash += GetHashCode_TypeNamespace(a.Namespace); return hash; } /// /// Compares type lists /// /// Type list #1 /// Type list #2 /// Root declaring type to check if we should /// treat as a generic instance type /// true if same, false otherwise bool Equals(IList a, IList b, Type declaringType) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (a.Count != b.Count) result = false; else { int i; for (i = 0; i < a.Count; i++) { if (!Equals(a[i], b[i], declaringType)) break; } result = i == a.Count; } recursionCounter.Decrement(); return result; } /// /// Compares modules /// /// Module #1 /// Module #2 /// true if same, false otherwise bool Equals(ModuleDef a, Module b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; if (!recursionCounter.Increment()) return false; bool result = Equals((IModule)a, b) && Equals(a.Assembly, b.Assembly); recursionCounter.Decrement(); return result; } /// /// Compares a file and a module /// /// File /// Module /// true if same, false otherwise bool Equals(FileDef a, Module b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; // Use b.Name since it's the filename we want to compare, not b.ScopeName return UTF8String.ToSystemStringOrEmpty(a.Name).Equals(b.Name, StringComparison.OrdinalIgnoreCase); } /// /// Compares modules /// /// Module #1 /// Module #2 /// true if same, false otherwise bool Equals(IModule a, Module b) { if ((object)a == b) return true; if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; // Use b.ScopeName and not b.Name since b.Name is just the file name w/o path return UTF8String.ToSystemStringOrEmpty(a.Name).Equals(b.ScopeName, StringComparison.OrdinalIgnoreCase); } /// /// Compares assemblies /// /// Assembly #1 /// Assembly #2 /// true if same, false otherwise bool Equals(IAssembly a, Assembly b) { if ((object)a == b) return true; if (a is null || b is null) return false; if (!MscorlibIsNotSpecial && IsCorLib(a) && IsCorLib(b)) return true; if (!recursionCounter.Increment()) return false; var bAsmName = b.GetName(); bool result = UTF8String.ToSystemStringOrEmpty(a.Name).Equals(bAsmName.Name, StringComparison.OrdinalIgnoreCase) && (!CompareAssemblyPublicKeyToken || PublicKeyBase.TokenEquals(a.PublicKeyOrToken, new PublicKeyToken(bAsmName.GetPublicKeyToken()))) && (!CompareAssemblyVersion || Utils.Equals(a.Version, bAsmName.Version)) && (!CompareAssemblyLocale || Utils.LocaleEquals(a.Culture, bAsmName.CultureInfo.Name)); recursionCounter.Decrement(); return result; } /// /// Compares method declaring types /// /// Method #1 /// Method #2 /// true if same, false otherwise bool DeclaringTypeEquals(IMethod a, MethodBase b) { // If this is disabled, always return true, even if one is null, etc. if (!CompareMethodFieldDeclaringType) return true; if ((object)a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; MethodDef md; MemberRef mr; MethodSpec ms; if ((md = a as MethodDef) is not null) result = DeclaringTypeEquals(md, b); else if ((mr = a as MemberRef) is not null) result = DeclaringTypeEquals(mr, b); else if ((ms = a as MethodSpec) is not null) result = DeclaringTypeEquals(ms, b); else result = false; recursionCounter.Decrement(); return result; } bool DeclaringTypeEquals(MethodDef a, MethodBase b) { // If this is disabled, always return true, even if one is null, etc. if (!CompareMethodFieldDeclaringType) return true; if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; return Equals(a.DeclaringType, b.DeclaringType); } bool DeclaringTypeEquals(MemberRef a, MethodBase b) { // If this is disabled, always return true, even if one is null, etc. if (!CompareMethodFieldDeclaringType) return true; if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; return Equals(a.Class, b.DeclaringType, b.Module); } bool DeclaringTypeEquals(MethodSpec a, MethodBase b) { // If this is disabled, always return true, even if one is null, etc. if (!CompareMethodFieldDeclaringType) return true; if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; return DeclaringTypeEquals(a.Method, b); } /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodBase a, IMethod b) => Equals(b, a); /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(IMethod a, MethodBase b) { if ((object)a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; MethodDef md; MemberRef mr; MethodSpec ms; if ((md = a as MethodDef) is not null) result = Equals(md, b); else if ((mr = a as MemberRef) is not null) result = Equals(mr, b); else if ((ms = a as MethodSpec) is not null) result = Equals(ms, b); else result = false; recursionCounter.Decrement(); return result; } /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodBase a, MethodDef b) => Equals(b, a); /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodDef a, MethodBase b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (!DontProjectWinMDRefs) { var mra = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a); if (mra is not null) { result = Equals(mra, b); goto exit; } } var amSig = a.MethodSig; result = Equals_MethodFieldNames(a.Name, b.Name) && amSig is not null && ((amSig.Generic && b.IsGenericMethodDefinition && b.IsGenericMethod) || (!amSig.Generic && !b.IsGenericMethodDefinition && !b.IsGenericMethod)) && Equals(amSig, b) && (!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); exit: ; recursionCounter.Decrement(); return result; } /// /// Compares method sigs /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodBase a, MethodSig b) => Equals(b, a); /// /// Compares method sigs /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodSig a, MethodBase b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; if (!CompareMethodFieldDeclaringType && b.DeclaringType.IsGenericButNotGenericTypeDefinition()) { var t = b; b = b.Module.ResolveMethod(b.MetadataToken); if (b.IsGenericButNotGenericMethodDefinition()) b = ((MethodInfo)b).MakeGenericMethod(t.GetGenericArguments()); } bool result = Equals(a.GetCallingConvention(), b) && (DontCompareReturnType || ReturnTypeEquals(a.RetType, b)) && Equals(a.Params, b.GetParameters(), b.DeclaringType) && (!a.Generic || a.GenParamCount == b.GetGenericArguments().Length); recursionCounter.Decrement(); return result; } /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodBase a, MemberRef b) => Equals(b, a); /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MemberRef a, MethodBase b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (!DontProjectWinMDRefs) a = WinMDHelpers.ToCLR(a.Module ?? sourceModule, a) ?? a; if (b.IsGenericMethod && !b.IsGenericMethodDefinition) { // 'a' must be a method ref in a generic type. This comparison must match // the MethodSpec vs MethodBase comparison code. result = a.IsMethodRef && a.MethodSig.Generic; var oldOptions = ClearOptions(SigComparerOptions.CompareMethodFieldDeclaringType); SetOptions(SigComparerOptions_DontSubstituteGenericParameters); result = result && Equals(a, b.Module.ResolveMethod(b.MetadataToken)); RestoreOptions(oldOptions); result = result && DeclaringTypeEquals(a, b); result = result && GenericMethodArgsEquals((int)a.MethodSig.GenParamCount, b.GetGenericArguments()); } else { var amSig = a.MethodSig; result = Equals_MethodFieldNames(a.Name, b.Name) && amSig is not null && ((amSig.Generic && b.IsGenericMethodDefinition && b.IsGenericMethod) || (!amSig.Generic && !b.IsGenericMethodDefinition && !b.IsGenericMethod)); GenericInstSig git; if (CompareMethodFieldDeclaringType && !DontSubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) is not null) { InitializeGenericArguments(); genericArguments.PushTypeArgs(git.GenericArguments); result = result && Equals(amSig, b); genericArguments.PopTypeArgs(); } else result = result && Equals(amSig, b); result = result && (!CompareMethodFieldDeclaringType || Equals(a.Class, b.DeclaringType, b.Module)); } recursionCounter.Decrement(); return result; } /// /// Compares generic method args, making sure only /// contains s. /// /// Number of generic method args in method #1 /// Generic method args in method #2 /// true if same, false otherwise static bool GenericMethodArgsEquals(int numMethodArgs, IList methodGenArgs) { if (numMethodArgs != methodGenArgs.Count) return false; for (int i = 0; i < numMethodArgs; i++) { if (methodGenArgs[i].GetElementType2() != ElementType.MVar) return false; } return true; } bool Equals(IMemberRefParent a, Type b, Module bModule) { // Global methods and fields have their DeclaringType set to null. Assume // null always means the global type. if (a is null) return false; if (!recursionCounter.Increment()) return false; bool result; ITypeDefOrRef ita; ModuleRef moda; MethodDef ma; TypeDef td; if ((ita = a as ITypeDefOrRef) is not null) result = Equals((IType)ita, b); else if ((moda = a as ModuleRef) is not null) { var omoda = moda.Module; result = b is null && // b is null => it's the global type Equals(moda, bModule) && Equals(omoda?.Assembly, bModule.Assembly); } else if ((ma = a as MethodDef) is not null) result = Equals(ma.DeclaringType, b); else if (b is null && (td = a as TypeDef) is not null) result = td.IsGlobalModuleType; else result = false; recursionCounter.Decrement(); return result; } /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodBase a, MethodSpec b) => Equals(b, a); /// /// Compares methods /// /// Method #1 /// Method #2 /// true if same, false otherwise public bool Equals(MethodSpec a, MethodBase b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; // Make sure it's a MethodSpec bool result = b.IsGenericMethod && !b.IsGenericMethodDefinition; // Don't compare declaring types yet because the resolved method has the wrong // declaring type (its declaring type is a generic type def). // NOTE: We must not push generic method args when comparing a.Method var oldOptions = ClearOptions(SigComparerOptions.CompareMethodFieldDeclaringType); SetOptions(SigComparerOptions_DontSubstituteGenericParameters); result = result && Equals(a.Method, b.Module.ResolveMethod(b.MetadataToken)); RestoreOptions(oldOptions); result = result && DeclaringTypeEquals(a.Method, b); var gim = a.GenericInstMethodSig; result = result && gim is not null && Equals(gim.GenericArguments, b.GetGenericArguments(), b.DeclaringType); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a MethodBase /// /// The MethodBase /// The hash code public int GetHashCode(MethodBase a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; // *********************************************************************** // IMPORTANT: This hash code must match the MemberRef/MethodSpec hash code // *********************************************************************** int hash = GetHashCode_MethodFieldName(a.Name) + GetHashCode_MethodSig(a); if (CompareMethodFieldDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } int GetHashCode_MethodSig(MethodBase a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; if (!CompareMethodFieldDeclaringType && a.DeclaringType.IsGenericButNotGenericTypeDefinition()) { var t = a; a = a.Module.ResolveMethod(a.MetadataToken); if (t.IsGenericButNotGenericMethodDefinition()) a = ((MethodInfo)a).MakeGenericMethod(t.GetGenericArguments()); } hash = GetHashCode_CallingConvention(a.CallingConvention, a.IsGenericMethod) + GetHashCode(a.GetParameters(), a.DeclaringType); if (!DontCompareReturnType) hash += GetHashCode_ReturnType(a); if (a.IsGenericMethod) hash += GetHashCode_ElementType_MVar(a.GetGenericArguments().Length); recursionCounter.Decrement(); return hash; } /// /// Gets the hash code of a parameter list /// /// The type list /// Declaring type of method that owns parameter /// The hash code int GetHashCode(IList a, Type declaringType) { //************************************************************************ // IMPORTANT: This code must match any other GetHashCode(IList) //************************************************************************ if (a is null) return 0; if (!recursionCounter.Increment()) return 0; uint hash = 0; for (int i = 0; i < a.Count; i++) { hash += (uint)GetHashCode(a[i], declaringType); hash = (hash << 13) | (hash >> 19); } recursionCounter.Decrement(); return (int)hash; } int GetHashCode_ReturnType(MethodBase a) { var mi = a as MethodInfo; if (mi is not null) return GetHashCode(mi.ReturnParameter, a.DeclaringType); return GetHashCode(typeof(void)); } int GetHashCode(ParameterInfo a, Type declaringType) => GetHashCode(a.ParameterType, declaringType); /// /// Compares calling conventions /// /// Calling convention /// Method /// static bool Equals(CallingConvention a, MethodBase b) { var bc = b.CallingConvention; if (((a & CallingConvention.Generic) != 0) != b.IsGenericMethod) return false; if (((a & CallingConvention.HasThis) != 0) != ((bc & CallingConventions.HasThis) != 0)) return false; if (((a & CallingConvention.ExplicitThis) != 0) != ((bc & CallingConventions.ExplicitThis) != 0)) return false; var cca = a & CallingConvention.Mask; switch (bc & CallingConventions.Any) { case CallingConventions.Standard: if (cca == CallingConvention.VarArg || cca == CallingConvention.NativeVarArg) return false; break; case CallingConventions.VarArgs: if (cca != CallingConvention.VarArg && cca != CallingConvention.NativeVarArg) return false; break; case CallingConventions.Any: default: break; } return true; } static int GetHashCode_CallingConvention(CallingConventions a, bool isGeneric) { //************************************************************** // IMPORTANT: This hash must match the other call conv hash code //************************************************************** CallingConvention cc = 0; if (isGeneric) cc |= CallingConvention.Generic; if ((a & CallingConventions.HasThis) != 0) cc |= CallingConvention.HasThis; if ((a & CallingConventions.ExplicitThis) != 0) cc |= CallingConvention.ExplicitThis; return (int)cc; } /// /// Compares return types /// /// Return type #1 /// MethodBase /// true if same, false otherwise bool ReturnTypeEquals(TypeSig a, MethodBase b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; var mi = b as MethodInfo; if (mi is not null) result = Equals(a, mi.ReturnParameter, b.DeclaringType); else if (b is ConstructorInfo) result = IsSystemVoid(a); else result = false; recursionCounter.Decrement(); return result; } static bool IsSystemVoid(TypeSig a) => a.RemovePinnedAndModifiers().GetElementType() == ElementType.Void; /// /// Compares parameter lists /// /// Type list #1 /// Type list #2 /// Declaring type of method that owns parameter /// true if same, false otherwise bool Equals(IList a, IList b, Type declaringType) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (a.Count != b.Count) result = false; else { int i; for (i = 0; i < a.Count; i++) { if (!Equals(a[i], b[i], declaringType)) break; } result = i == a.Count; } recursionCounter.Decrement(); return result; } /// /// Compares parameter types /// /// Parameter type #1 /// Parameter #2 /// Declaring type of method that owns parameter /// true if same, false otherwise bool Equals(TypeSig a, ParameterInfo b, Type declaringType) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = ModifiersEquals(a, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out var a2) && Equals(a2, b.ParameterType, declaringType); recursionCounter.Decrement(); return result; } bool ModifiersEquals(TypeSig a, IList reqMods2, IList optMods2, out TypeSig aAfterModifiers) { aAfterModifiers = a; if (!(a is ModifierSig)) return reqMods2.Count == 0 && optMods2.Count == 0; if (!recursionCounter.Increment()) return false; bool result; var reqMods1 = new List(reqMods2.Count); var optMods1 = new List(optMods2.Count); while (true) { var modifierSig = aAfterModifiers as ModifierSig; if (modifierSig is null) break; if (modifierSig is CModOptSig) optMods1.Add(modifierSig.Modifier); else reqMods1.Add(modifierSig.Modifier); // This can only loop forever if the user created a loop. It's not possible // to create a loop with invalid metadata. aAfterModifiers = aAfterModifiers.Next; } optMods1.Reverse(); reqMods1.Reverse(); result = reqMods1.Count == reqMods2.Count && optMods1.Count == optMods2.Count && ModifiersEquals(reqMods1, reqMods2) && ModifiersEquals(optMods1, optMods2); recursionCounter.Decrement(); return result; } bool ModifiersEquals(IList a, IList b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (a.Count != b.Count) result = false; else { int i; for (i = 0; i < b.Count; i++) { if (!Equals(a[i], b[i])) break; } result = i == b.Count; } recursionCounter.Decrement(); return result; } /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(FieldInfo a, IField b) => Equals(b, a); /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(IField a, FieldInfo b) { if ((object)a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; FieldDef fa; MemberRef ma; if ((fa = a as FieldDef) is not null) result = Equals(fa, b); else if ((ma = a as MemberRef) is not null) result = Equals(ma, b); else result = false; recursionCounter.Decrement(); return result; } /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(FieldInfo a, FieldDef b) => Equals(b, a); /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(FieldDef a, FieldInfo b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.FieldSig, b) && (!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); return result; } bool Equals(FieldSig a, FieldInfo b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; if (!CompareMethodFieldDeclaringType && b.DeclaringType.IsGenericButNotGenericTypeDefinition()) b = b.Module.ResolveField(b.MetadataToken); bool result = ModifiersEquals(a.Type, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out var a2) && Equals(a2, b.FieldType, b.DeclaringType); recursionCounter.Decrement(); return result; } /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(FieldInfo a, MemberRef b) => Equals(b, a); /// /// Compares fields /// /// Field #1 /// Field #2 /// true if same, false otherwise public bool Equals(MemberRef a, FieldInfo b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_MethodFieldNames(a.Name, b.Name); GenericInstSig git; if (CompareMethodFieldDeclaringType && !DontSubstituteGenericParameters && (git = GetGenericInstanceType(a.Class)) is not null) { InitializeGenericArguments(); genericArguments.PushTypeArgs(git.GenericArguments); result = result && Equals(a.FieldSig, b); genericArguments.PopTypeArgs(); } else result = result && Equals(a.FieldSig, b); result = result && (!CompareMethodFieldDeclaringType || Equals(a.Class, b.DeclaringType, b.Module)); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a field /// /// The field /// The hash code public int GetHashCode(FieldInfo a) { // ************************************************************ // IMPORTANT: This hash code must match the MemberRef hash code // ************************************************************ if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash = GetHashCode_MethodFieldName(a.Name) + GetHashCode_FieldSig(a); if (CompareMethodFieldDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } int GetHashCode_FieldSig(FieldInfo a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; if (!CompareMethodFieldDeclaringType && a.DeclaringType.IsGenericButNotGenericTypeDefinition()) a = a.Module.ResolveField(a.MetadataToken); hash = GetHashCode_CallingConvention(0, false) + GetHashCode(a.FieldType, a.DeclaringType); recursionCounter.Decrement(); return hash; } /// /// Compares properties /// /// Property #1 /// Property #2 /// true if same, false otherwise public bool Equals(PropertyDef a, PropertyInfo b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_PropertyNames(a.Name, b.Name) && Equals(a.PropertySig, b) && (!ComparePropertyDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); return result; } bool Equals(PropertySig a, PropertyInfo b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = ModifiersEquals(a.RetType, b.GetRequiredCustomModifiers(), b.GetOptionalCustomModifiers(), out var a2) && Equals(a2, b.PropertyType, b.DeclaringType); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of a property /// /// The property /// The hash code public int GetHashCode(PropertyInfo a) { // ************************************************************** // IMPORTANT: This hash code must match the PropertyDef hash code // ************************************************************** if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash = GetHashCode_PropertyName(a.Name) + GetHashCode(a.PropertyType, a.DeclaringType); if (ComparePropertyDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } /// /// Compares events /// /// Event #1 /// Event #2 /// true if same, false otherwise public bool Equals(EventDef a, EventInfo b) { if ((object)a == (object)b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_EventNames(a.Name, b.Name) && Equals(a.EventType, b.EventHandlerType, b.DeclaringType) && (!CompareEventDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); return result; } /// /// Gets the hash code of an event /// /// The event /// The hash code public int GetHashCode(EventInfo a) { // *********************************************************** // IMPORTANT: This hash code must match the EventDef hash code // *********************************************************** if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash = GetHashCode_EventName(a.Name) + GetHashCode(a.EventHandlerType, a.DeclaringType); if (CompareEventDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } /// public override string ToString() => $"{recursionCounter} - {options}"; static bool InSameModule(IOwnerModule a, IOwnerModule b) => a.Module is { } module && module == b.Module; } }