obfuz/Plugins/dnlib/DotNet/SigComparer.cs

4733 lines
149 KiB
C#

// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.Reflection;
namespace dnlib.DotNet {
/// <summary>
/// Compares types
/// </summary>
public sealed class TypeEqualityComparer : IEqualityComparer<IType>, IEqualityComparer<ITypeDefOrRef>, IEqualityComparer<TypeRef>, IEqualityComparer<TypeDef>, IEqualityComparer<TypeSpec>, IEqualityComparer<TypeSig>, IEqualityComparer<ExportedType> {
readonly SigComparerOptions options;
/// <summary>
/// Default instance
/// </summary>
public static readonly TypeEqualityComparer Instance = new TypeEqualityComparer(0);
/// <summary>
/// Case insensitive names
/// </summary>
public static readonly TypeEqualityComparer CaseInsensitive = new TypeEqualityComparer(SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc.
/// </summary>
public static readonly TypeEqualityComparer CompareReferenceInSameModule = new TypeEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule);
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Comparison options</param>
public TypeEqualityComparer(SigComparerOptions options) => this.options = options;
/// <inheritdoc/>
public bool Equals(IType x, IType y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(IType obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(ITypeDefOrRef x, ITypeDefOrRef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(ITypeDefOrRef obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(TypeRef x, TypeRef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(TypeRef obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(TypeDef x, TypeDef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(TypeDef obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(TypeSpec x, TypeSpec y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(TypeSpec obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(TypeSig x, TypeSig y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(TypeSig obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(ExportedType x, ExportedType y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(ExportedType obj) => new SigComparer(options).GetHashCode(obj);
}
/// <summary>
/// Compares fields
/// </summary>
public sealed class FieldEqualityComparer : IEqualityComparer<IField>, IEqualityComparer<FieldDef>, IEqualityComparer<MemberRef> {
readonly SigComparerOptions options;
/// <summary>
/// Compares the declaring types
/// </summary>
public static readonly FieldEqualityComparer CompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType);
/// <summary>
/// Doesn't compare the declaring types
/// </summary>
public static readonly FieldEqualityComparer DontCompareDeclaringTypes = new FieldEqualityComparer(0);
/// <summary>
/// Compares the declaring types, case insensitive names
/// </summary>
public static readonly FieldEqualityComparer CaseInsensitiveCompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType | SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Doesn't compare the declaring types, case insensitive names
/// </summary>
public static readonly FieldEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new FieldEqualityComparer(SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc.
/// </summary>
public static readonly FieldEqualityComparer CompareReferenceInSameModule = new FieldEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule);
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Comparison options</param>
public FieldEqualityComparer(SigComparerOptions options) => this.options = options;
/// <inheritdoc/>
public bool Equals(IField x, IField y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(IField obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(FieldDef x, FieldDef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(FieldDef obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(MemberRef x, MemberRef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(MemberRef obj) => new SigComparer(options).GetHashCode(obj);
}
/// <summary>
/// Compares methods
/// </summary>
public sealed class MethodEqualityComparer : IEqualityComparer<IMethod>, IEqualityComparer<IMethodDefOrRef>, IEqualityComparer<MethodDef>, IEqualityComparer<MemberRef>, IEqualityComparer<MethodSpec> {
readonly SigComparerOptions options;
/// <summary>
/// Compares the declaring types
/// </summary>
public static readonly MethodEqualityComparer CompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType);
/// <summary>
/// Doesn't compare the declaring types
/// </summary>
public static readonly MethodEqualityComparer DontCompareDeclaringTypes = new MethodEqualityComparer(0);
/// <summary>
/// Compares the declaring types, case insensitive names
/// </summary>
public static readonly MethodEqualityComparer CaseInsensitiveCompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CompareMethodFieldDeclaringType | SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Doesn't compare the declaring types, case insensitive names
/// </summary>
public static readonly MethodEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new MethodEqualityComparer(SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc.
/// </summary>
public static readonly MethodEqualityComparer CompareReferenceInSameModule = new MethodEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule);
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Comparison options</param>
public MethodEqualityComparer(SigComparerOptions options) => this.options = options;
/// <inheritdoc/>
public bool Equals(IMethod x, IMethod y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(IMethod obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(IMethodDefOrRef x, IMethodDefOrRef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(IMethodDefOrRef obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(MethodDef x, MethodDef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(MethodDef obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(MemberRef x, MemberRef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(MemberRef obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(MethodSpec x, MethodSpec y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(MethodSpec obj) => new SigComparer(options).GetHashCode(obj);
}
/// <summary>
/// Compares properties
/// </summary>
public sealed class PropertyEqualityComparer : IEqualityComparer<PropertyDef> {
readonly SigComparerOptions options;
/// <summary>
/// Compares the declaring types
/// </summary>
public static readonly PropertyEqualityComparer CompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.ComparePropertyDeclaringType);
/// <summary>
/// Doesn't compare the declaring types
/// </summary>
public static readonly PropertyEqualityComparer DontCompareDeclaringTypes = new PropertyEqualityComparer(0);
/// <summary>
/// Compares the declaring types, case insensitive names
/// </summary>
public static readonly PropertyEqualityComparer CaseInsensitiveCompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.ComparePropertyDeclaringType | SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Doesn't compare the declaring types, case insensitive names
/// </summary>
public static readonly PropertyEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new PropertyEqualityComparer(SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc.
/// </summary>
public static readonly PropertyEqualityComparer CompareReferenceInSameModule = new PropertyEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule);
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Comparison options</param>
public PropertyEqualityComparer(SigComparerOptions options) => this.options = options;
/// <inheritdoc/>
public bool Equals(PropertyDef x, PropertyDef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(PropertyDef obj) => new SigComparer(options).GetHashCode(obj);
}
/// <summary>
/// Compares events
/// </summary>
public sealed class EventEqualityComparer : IEqualityComparer<EventDef> {
readonly SigComparerOptions options;
/// <summary>
/// Compares the declaring types
/// </summary>
public static readonly EventEqualityComparer CompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CompareEventDeclaringType);
/// <summary>
/// Doesn't compare the declaring types
/// </summary>
public static readonly EventEqualityComparer DontCompareDeclaringTypes = new EventEqualityComparer(0);
/// <summary>
/// Compares the declaring types, case insensitive names
/// </summary>
public static readonly EventEqualityComparer CaseInsensitiveCompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CompareEventDeclaringType | SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Doesn't compare the declaring types, case insensitive names
/// </summary>
public static readonly EventEqualityComparer CaseInsensitiveDontCompareDeclaringTypes = new EventEqualityComparer(SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Compares definitions in same module using reference comparison instead of comparing them by name, signature, etc.
/// </summary>
public static readonly EventEqualityComparer CompareReferenceInSameModule = new EventEqualityComparer(SigComparerOptions.ReferenceCompareForMemberDefsInSameModule);
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Comparison options</param>
public EventEqualityComparer(SigComparerOptions options) => this.options = options;
/// <inheritdoc/>
public bool Equals(EventDef x, EventDef y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(EventDef obj) => new SigComparer(options).GetHashCode(obj);
}
/// <summary>
/// Compares calling convention signatures
/// </summary>
public sealed class SignatureEqualityComparer : IEqualityComparer<CallingConventionSig>, IEqualityComparer<MethodBaseSig>, IEqualityComparer<MethodSig>, IEqualityComparer<PropertySig>, IEqualityComparer<FieldSig>, IEqualityComparer<LocalSig>, IEqualityComparer<GenericInstMethodSig> {
readonly SigComparerOptions options;
/// <summary>
/// Default instance
/// </summary>
public static readonly SignatureEqualityComparer Instance = new SignatureEqualityComparer(0);
/// <summary>
/// Case insensitive names
/// </summary>
public static readonly SignatureEqualityComparer CaseInsensitive = new SignatureEqualityComparer(SigComparerOptions.CaseInsensitiveAll);
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Comparison options</param>
public SignatureEqualityComparer(SigComparerOptions options) => this.options = options;
/// <inheritdoc/>
public bool Equals(CallingConventionSig x, CallingConventionSig y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(CallingConventionSig obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(MethodBaseSig x, MethodBaseSig y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(MethodBaseSig obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(MethodSig x, MethodSig y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(MethodSig obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(PropertySig x, PropertySig y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(PropertySig obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(FieldSig x, FieldSig y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(FieldSig obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(LocalSig x, LocalSig y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(LocalSig obj) => new SigComparer(options).GetHashCode(obj);
/// <inheritdoc/>
public bool Equals(GenericInstMethodSig x, GenericInstMethodSig y) => new SigComparer(options).Equals(x, y);
/// <inheritdoc/>
public int GetHashCode(GenericInstMethodSig obj) => new SigComparer(options).GetHashCode(obj);
}
/// <summary>
/// Decides how to compare types, sigs, etc
/// </summary>
[Flags]
public enum SigComparerOptions : uint {
/// <summary>
/// Don't compare a type's (assembly/module) scope
/// </summary>
DontCompareTypeScope = 1,
/// <summary>
/// Compares a method/field's declaring type.
/// </summary>
CompareMethodFieldDeclaringType = 2,
/// <summary>
/// Compares a property's declaring type
/// </summary>
ComparePropertyDeclaringType = 4,
/// <summary>
/// Compares an event's declaring type
/// </summary>
CompareEventDeclaringType = 8,
/// <summary>
/// Compares method / field / property / event declaring types
/// </summary>
CompareDeclaringTypes = CompareMethodFieldDeclaringType | ComparePropertyDeclaringType | CompareEventDeclaringType,
/// <summary>
/// Compares parameters after a sentinel in method sigs. Should not be enabled when
/// comparing <see cref="MethodSig"/>s against <see cref="MethodInfo"/>s since it's
/// not possible to get those sentinel params from a <see cref="MethodInfo"/>.
/// </summary>
CompareSentinelParams = 0x10,
/// <summary>
/// Compares assembly public key token
/// </summary>
CompareAssemblyPublicKeyToken = 0x20,
/// <summary>
/// Compares assembly version
/// </summary>
CompareAssemblyVersion = 0x40,
/// <summary>
/// Compares assembly locale
/// </summary>
CompareAssemblyLocale = 0x80,
/// <summary>
/// If set, a <see cref="TypeRef"/> and an <see cref="ExportedType"/> can reference the
/// global <c>&lt;Module&gt;</c> type.
/// </summary>
TypeRefCanReferenceGlobalType = 0x100,
/// <summary>
/// Don't compare a method/property's return type
/// </summary>
DontCompareReturnType = 0x200,
// Internal only
//SubstituteGenericParameters = 0x400,
/// <summary>
/// Type namespaces are case insensitive
/// </summary>
CaseInsensitiveTypeNamespaces = 0x800,
/// <summary>
/// Type names (not namespaces) are case insensitive
/// </summary>
CaseInsensitiveTypeNames = 0x1000,
/// <summary>
/// Type names and namespaces are case insensitive
/// </summary>
CaseInsensitiveTypes = CaseInsensitiveTypeNamespaces | CaseInsensitiveTypeNames,
/// <summary>
/// Method and field names are case insensitive
/// </summary>
CaseInsensitiveMethodFieldNames = 0x2000,
/// <summary>
/// Property names are case insensitive
/// </summary>
CaseInsensitivePropertyNames = 0x4000,
/// <summary>
/// Event names are case insensitive
/// </summary>
CaseInsensitiveEventNames = 0x8000,
/// <summary>
/// Type namespaces, type names, method names, field names, property names
/// and event names are all case insensitive
/// </summary>
CaseInsensitiveAll = CaseInsensitiveTypeNamespaces | CaseInsensitiveTypeNames |
CaseInsensitiveMethodFieldNames | CaseInsensitivePropertyNames |
CaseInsensitiveEventNames,
/// <summary>
/// A field that is <see cref="FieldAttributes.PrivateScope"/> can compare equal to
/// a <see cref="MemberRef"/>
/// </summary>
PrivateScopeFieldIsComparable = 0x10000,
/// <summary>
/// A method that is <see cref="MethodAttributes.PrivateScope"/> can compare equal to
/// a <see cref="MemberRef"/>
/// </summary>
PrivateScopeMethodIsComparable = 0x20000,
/// <summary>
/// A field that is <see cref="FieldAttributes.PrivateScope"/> and a method that is
/// <see cref="MethodAttributes.PrivateScope"/> can compare equal to a <see cref="MemberRef"/>
/// </summary>
PrivateScopeIsComparable = PrivateScopeFieldIsComparable | PrivateScopeMethodIsComparable,
/// <summary>
/// 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.
/// </summary>
RawSignatureCompare = 0x40000,
/// <summary>
/// Ignore required and optional modifiers when comparing <see cref="TypeSig"/>s.
/// They're already ignored when comparing eg. a <see cref="TypeSig"/> with a
/// <see cref="TypeRef"/>.
/// </summary>
IgnoreModifiers = 0x80000,
/// <summary>
/// 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.
/// </summary>
MscorlibIsNotSpecial = 0x100000,
/// <summary>
/// Don't project CLR compatible WinMD references back to the original CLR type/method before comparing
/// </summary>
DontProjectWinMDRefs = 0x200000,
/// <summary>
/// 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.
/// </summary>
DontCheckTypeEquivalence = 0x400000,
/// <summary>
/// When comparing types, don't compare a multi-dimensional array's lower bounds and sizes
/// </summary>
IgnoreMultiDimensionalArrayLowerBoundsAndSizes = 0x800000,
/// <summary>
/// When comparing MemberDefs in same module, use regular reference comparison instead
/// comparing the signature, name, and other properties.
/// </summary>
ReferenceCompareForMemberDefsInSameModule = 0x1000000,
}
/// <summary>
/// Compares types, signatures, methods, fields, properties, events
/// </summary>
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;
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Comparison options</param>
public SigComparer(SigComparerOptions options)
: this(options, null) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="options">Comparison options</param>
/// <param name="sourceModule">The module which the comparison take place in.</param>
public SigComparer(SigComparerOptions options, ModuleDef sourceModule) {
recursionCounter = new RecursionCounter();
this.options = options;
genericArguments = null;
this.sourceModule = sourceModule;
}
/// <summary>
/// <see cref="ElementType.FnPtr"/> is mapped to <see cref="System.IntPtr"/>, so use
/// the same hash code for both
/// </summary>
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);
}
/// <summary>
/// Compare members
/// </summary>
/// <param name="a">Member #1</param>
/// <param name="b">Member #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a member
/// </summary>
/// <param name="a">The member</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(ITypeDefOrRef a, ITypeDefOrRef b) => Equals((IType)a, (IType)b);
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
public int GetHashCode(ITypeDefOrRef a) => GetHashCode((IType)a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(TypeRef a, TypeDef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(ExportedType a, TypeDef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(TypeSpec a, TypeDef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(TypeSig a, TypeDef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(TypeSpec a, TypeRef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(ExportedType a, TypeRef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(TypeSig a, TypeRef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(TypeSig a, TypeSpec b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(ExportedType a, TypeSpec b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(ExportedType a, TypeSig b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
public int GetHashCode(TypeSpec a) {
if (a is null)
return 0;
return GetHashCode(a.TypeSig);
}
/// <summary>
/// Compares resolution scopes
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares implementation
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares resolution scope and implementation
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares files
/// </summary>
/// <param name="a">File #1</param>
/// <param name="b">File #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares a module with a file
/// </summary>
/// <param name="a">Module</param>
/// <param name="b">File</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares modules
/// </summary>
/// <param name="a">Module #1</param>
/// <param name="b">Module #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
/// <summary>
/// Compares modules
/// </summary>
/// <param name="a">Module #1</param>
/// <param name="b">Module #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares assemblies
/// </summary>
/// <param name="a">Assembly #1</param>
/// <param name="b">Assembly #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares type lists
/// </summary>
/// <param name="a">Type list #1</param>
/// <param name="b">Type list #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(IList<TypeSig> a, IList<TypeSig> 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;
}
/// <summary>
/// Gets the hash code of a type list
/// </summary>
/// <param name="a">The type list</param>
/// <returns>The hash code</returns>
public int GetHashCode(IList<TypeSig> a) => GetHashCode(a, true);
int GetHashCode(IList<TypeSig> a, bool substituteGenericParameters) {
//************************************************************************
// IMPORTANT: This code must match any other GetHashCode(IList<SOME_TYPE>)
//************************************************************************
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<uint> a, IList<uint> 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<int> a, IList<int> 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;
}
/// <summary>
/// Compares signatures
/// </summary>
/// <param name="a">Sig #1</param>
/// <param name="b">Sig #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a sig
/// </summary>
/// <param name="a">The sig</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares method/property sigs
/// </summary>
/// <param name="a">Method/property #1</param>
/// <param name="b">Method/property #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a method/property sig
/// </summary>
/// <param name="a">The method/property sig</param>
/// <returns>The hash code</returns>
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;
}
}
/// <summary>
/// Compares field sigs
/// </summary>
/// <param name="a">Field sig #1</param>
/// <param name="b">Field sig #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a field sig
/// </summary>
/// <param name="a">The field sig</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares local sigs
/// </summary>
/// <param name="a">Local sig #1</param>
/// <param name="b">Local sig #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a local sig
/// </summary>
/// <param name="a">The local sig</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares generic method instance sigs
/// </summary>
/// <param name="a">Generic inst method #1</param>
/// <param name="b">Generic inst method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a generic instance method sig
/// </summary>
/// <param name="a">The generic inst method sig</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a method
/// </summary>
/// <param name="a">The method</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(MemberRef a, MethodDef b) => Equals(b, a);
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a method
/// </summary>
/// <param name="a">The method</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares <c>MemberRef</c>s
/// </summary>
/// <param name="a"><c>MemberRef</c> #1</param>
/// <param name="b"><c>MemberRef</c> #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a <c>MemberRef</c>
/// </summary>
/// <param name="a">The <c>MemberRef</c></param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares <c>MethodSpec</c>s
/// </summary>
/// <param name="a"><c>MethodSpec</c> #1</param>
/// <param name="b"><c>MethodSpec</c> #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a <c>MethodSpec</c>
/// </summary>
/// <param name="a">The <c>MethodSpec</c></param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares <c>MemberRefParent</c>s
/// </summary>
/// <param name="a"><c>MemberRefParent</c> #1</param>
/// <param name="b"><c>MemberRefParent</c> #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a <c>MemberRefParent</c>
/// </summary>
/// <param name="a">The <c>MemberRefParent</c></param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a field
/// </summary>
/// <param name="a">The field</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(MemberRef a, FieldDef b) => Equals(b, a);
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a field
/// </summary>
/// <param name="a">The field</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares properties
/// </summary>
/// <param name="a">Property #1</param>
/// <param name="b">Property #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a property
/// </summary>
/// <param name="a">The property</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares events
/// </summary>
/// <param name="a">Event #1</param>
/// <param name="b">Event #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of an event
/// </summary>
/// <param name="a">The event</param>
/// <returns>The hash code</returns>
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;
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(Type a, IType b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(Type a, TypeDef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(Type a, TypeRef b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="b">Type #1</param>
/// <param name="a">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(Type a, TypeSpec b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(Type a, TypeSig b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Checks whether it's FnPtr&amp;, FnPtr*, FnPtr[], or FnPtr[...]
/// </summary>
/// <param name="a">The type</param>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <param name="declaringType">Root declaring type to check if we should
/// treat <paramref name="b"/> as a generic instance type</param>
/// <param name="treatAsGenericInst"><c>true</c> if we should treat <paramref name="b"/>
/// as a generic instance type</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares types
/// </summary>
/// <param name="a">Type #1</param>
/// <param name="b">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(Type a, ExportedType b) => Equals(b, a);
/// <summary>
/// Compares types
/// </summary>
/// <param name="b">Type #1</param>
/// <param name="a">Type #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
public int GetHashCode(Type a) => GetHashCode(a, false);
/// <summary>
/// Gets the hash code of a type
/// </summary>
/// <param name="a">The type</param>
/// <param name="treatAsGenericInst"><c>true</c> if we should treat <paramref name="a"/>
/// as a generic instance type</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Gets the hash code of a type list
/// </summary>
/// <param name="a">The type list</param>
/// <param name="declaringType">Root declaring type to check if we should
/// treat <paramref name="a"/> as a generic instance type</param>
/// <returns>The hash code</returns>
int GetHashCode(IList<Type> a, Type declaringType) {
//************************************************************************
// IMPORTANT: This code must match any other GetHashCode(IList<SOME_TYPE>)
//************************************************************************
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;
}
/// <summary>
/// Gets the hash code of a list with only generic method parameters (<see cref="ElementType.MVar"/>)
/// </summary>
/// <param name="numGenericParams">Number of generic method parameters</param>
/// <returns>Hash code</returns>
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<SOME_TYPE>)
//************************************************************************
uint hash = 0;
for (int i = 0; i < numGenericParams; i++) {
hash += (uint)(etypeHashCode + i);
hash = (hash << 13) | (hash >> 19);
}
return (int)hash;
}
/// <summary>
/// Gets the hash code of a TypeDef type
/// </summary>
/// <param name="a">The type</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares type lists
/// </summary>
/// <param name="a">Type list #1</param>
/// <param name="b">Type list #2</param>
/// <param name="declaringType">Root declaring type to check if we should
/// treat <paramref name="b"/> as a generic instance type</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
bool Equals(IList<TypeSig> a, IList<Type> 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;
}
/// <summary>
/// Compares modules
/// </summary>
/// <param name="a">Module #1</param>
/// <param name="b">Module #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares a file and a module
/// </summary>
/// <param name="a">File</param>
/// <param name="b">Module</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares modules
/// </summary>
/// <param name="a">Module #1</param>
/// <param name="b">Module #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares assemblies
/// </summary>
/// <param name="a">Assembly #1</param>
/// <param name="b">Assembly #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares method declaring types
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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);
}
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(MethodBase a, IMethod b) => Equals(b, a);
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(MethodBase a, MethodDef b) => Equals(b, a);
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares method sigs
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(MethodBase a, MethodSig b) => Equals(b, a);
/// <summary>
/// Compares method sigs
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(MethodBase a, MemberRef b) => Equals(b, a);
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares generic method args, making sure <paramref name="methodGenArgs"/> only
/// contains <see cref="ElementType.MVar"/>s.
/// </summary>
/// <param name="numMethodArgs">Number of generic method args in method #1</param>
/// <param name="methodGenArgs">Generic method args in method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
static bool GenericMethodArgsEquals(int numMethodArgs, IList<Type> 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;
}
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(MethodBase a, MethodSpec b) => Equals(b, a);
/// <summary>
/// Compares methods
/// </summary>
/// <param name="a">Method #1</param>
/// <param name="b">Method #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a <c>MethodBase</c>
/// </summary>
/// <param name="a">The <c>MethodBase</c></param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Gets the hash code of a parameter list
/// </summary>
/// <param name="a">The type list</param>
/// <param name="declaringType">Declaring type of method that owns parameter <paramref name="a"/></param>
/// <returns>The hash code</returns>
int GetHashCode(IList<ParameterInfo> a, Type declaringType) {
//************************************************************************
// IMPORTANT: This code must match any other GetHashCode(IList<SOME_TYPE>)
//************************************************************************
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);
/// <summary>
/// Compares calling conventions
/// </summary>
/// <param name="a">Calling convention</param>
/// <param name="b">Method</param>
/// <returns></returns>
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;
}
/// <summary>
/// Compares return types
/// </summary>
/// <param name="a">Return type #1</param>
/// <param name="b">MethodBase</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
/// <summary>
/// Compares parameter lists
/// </summary>
/// <param name="a">Type list #1</param>
/// <param name="b">Type list #2</param>
/// <param name="declaringType">Declaring type of method that owns parameter <paramref name="b"/></param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
bool Equals(IList<TypeSig> a, IList<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;
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;
}
/// <summary>
/// Compares parameter types
/// </summary>
/// <param name="a">Parameter type #1</param>
/// <param name="b">Parameter #2</param>
/// <param name="declaringType">Declaring type of method that owns parameter <paramref name="b"/></param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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<Type> reqMods2, IList<Type> 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<ITypeDefOrRef>(reqMods2.Count);
var optMods1 = new List<ITypeDefOrRef>(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<ITypeDefOrRef> a, IList<Type> 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;
}
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(FieldInfo a, IField b) => Equals(b, a);
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(FieldInfo a, FieldDef b) => Equals(b, a);
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
public bool Equals(FieldInfo a, MemberRef b) => Equals(b, a);
/// <summary>
/// Compares fields
/// </summary>
/// <param name="a">Field #1</param>
/// <param name="b">Field #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a field
/// </summary>
/// <param name="a">The field</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares properties
/// </summary>
/// <param name="a">Property #1</param>
/// <param name="b">Property #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of a property
/// </summary>
/// <param name="a">The property</param>
/// <returns>The hash code</returns>
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;
}
/// <summary>
/// Compares events
/// </summary>
/// <param name="a">Event #1</param>
/// <param name="b">Event #2</param>
/// <returns><c>true</c> if same, <c>false</c> otherwise</returns>
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;
}
/// <summary>
/// Gets the hash code of an event
/// </summary>
/// <param name="a">The event</param>
/// <returns>The hash code</returns>
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;
}
/// <inheritdoc/>
public override string ToString() => $"{recursionCounter} - {options}";
static bool InSameModule(IOwnerModule a, IOwnerModule b) => a.Module is { } module && module == b.Module;
}
}