/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using dnlib.DotNet; namespace dnSpy.AsmEditor.Compiler { sealed class ImportSigComparerOptions { public ModuleDef SourceModule { get; } public ModuleDef TargetModule { get; } public ImportSigComparerOptions(ModuleDef sourceModule, ModuleDef targetModule) { SourceModule = sourceModule; TargetModule = targetModule; } } sealed class ImportTypeEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer, IEqualityComparer { /*readonly*/ ImportSigComparer comparer; public ImportTypeEqualityComparer(ImportSigComparer comparer) => this.comparer = comparer; public bool Equals([AllowNull] TypeDef x, [AllowNull] TypeDef y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] TypeDef obj) => comparer.GetHashCode(obj); public bool Equals([AllowNull] ITypeDefOrRef x, [AllowNull] ITypeDefOrRef y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] ITypeDefOrRef obj) => comparer.GetHashCode(obj); public bool Equals([AllowNull] IType x, [AllowNull] IType y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] IType obj) => comparer.GetHashCode(obj); public bool Equals([AllowNull] TypeSig x, [AllowNull] TypeSig y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] TypeSig obj) => comparer.GetHashCode(obj); } sealed class ImportPropertyEqualityComparer : IEqualityComparer { /*readonly*/ ImportSigComparer comparer; public ImportPropertyEqualityComparer(ImportSigComparer comparer) => this.comparer = comparer; public bool Equals([AllowNull] PropertyDef x, [AllowNull] PropertyDef y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] PropertyDef obj) => comparer.GetHashCode(obj); } sealed class ImportEventEqualityComparer : IEqualityComparer { /*readonly*/ ImportSigComparer comparer; public ImportEventEqualityComparer(ImportSigComparer comparer) => this.comparer = comparer; public bool Equals([AllowNull] EventDef x, [AllowNull] EventDef y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] EventDef obj) => comparer.GetHashCode(obj); } sealed class ImportMethodEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer { /*readonly*/ ImportSigComparer comparer; public ImportMethodEqualityComparer(ImportSigComparer comparer) => this.comparer = comparer; public bool Equals([AllowNull] MethodDef x, [AllowNull] MethodDef y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] MethodDef obj) => comparer.GetHashCode(obj); public bool Equals([AllowNull] MemberRef x, [AllowNull] MemberRef y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] MemberRef obj) => comparer.GetHashCode(obj); public bool Equals([AllowNull] IMethod x, [AllowNull] IMethod y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] IMethod obj) => comparer.GetHashCode(obj); } sealed class ImportFieldEqualityComparer : IEqualityComparer, IEqualityComparer, IEqualityComparer { /*readonly*/ ImportSigComparer comparer; public ImportFieldEqualityComparer(ImportSigComparer comparer) => this.comparer = comparer; public bool Equals([AllowNull] FieldDef x, [AllowNull] FieldDef y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] FieldDef obj) => comparer.GetHashCode(obj); public bool Equals([AllowNull] MemberRef x, [AllowNull] MemberRef y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] MemberRef obj) => comparer.GetHashCode(obj); public bool Equals([AllowNull] IField x, [AllowNull] IField y) => comparer.Equals(x, y); public int GetHashCode([DisallowNull] IField obj) => comparer.GetHashCode(obj); } // Most code is from dnlib.DotNet.SigComparer struct ImportSigComparer { 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; readonly ImportSigComparerOptions importOptions; 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 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; public ImportSigComparer(ImportSigComparerOptions importOptions, SigComparerOptions options, ModuleDef sourceModule) { recursionCounter = new RecursionCounter(); this.options = options; this.importOptions = importOptions ?? throw new ArgumentNullException(nameof(importOptions)); this.sourceModule = sourceModule; } int GetHashCode_FnPtr_SystemIntPtr() => 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); 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; 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; 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); } 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; } 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; } public bool Equals(ITypeDefOrRef? a, ITypeDefOrRef? b) => Equals((IType?)a, (IType?)b); public int GetHashCode(ITypeDefOrRef? a) => GetHashCode((IType?)a); 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; } 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; } public bool Equals(TypeRef? a, TypeDef? b) => Equals(b, a); public bool Equals(TypeDef? a, TypeRef? b) { if ((object?)a == 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; 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); } else { result = false; //TODO: Handle the case where scope is null } if (result && !TypeRefCanReferenceGlobalType && a.IsGlobalModuleType) result = false; recursionCounter.Decrement(); return result; } public bool Equals(ExportedType? a, TypeDef? b) => Equals(b, a); public bool Equals(TypeDef? a, ExportedType? b) { if ((object?)a == 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; 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 && !TypeRefCanReferenceGlobalType && a.IsGlobalModuleType) result = false; recursionCounter.Decrement(); return result; } public bool Equals(TypeSpec? a, TypeDef? b) => Equals(b, a); public bool Equals(TypeDef? a, TypeSpec? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; return Equals(a, b.TypeSig); } public bool Equals(TypeSig? a, TypeDef? b) => Equals(b, a); public bool Equals(TypeDef? a, TypeSig? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; 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; } public bool Equals(TypeSpec? a, TypeRef? b) => Equals(b, a); public bool Equals(TypeRef? a, TypeSpec? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; return Equals(a, b.TypeSig); } public bool Equals(ExportedType? a, TypeRef? b) => Equals(b, a); public bool Equals(TypeRef? a, ExportedType? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_TypeNames(a.Name, b.TypeName) && Equals_TypeNamespaces(a.Namespace, b.TypeNamespace) && EqualsScope(a, b); recursionCounter.Decrement(); return result; } public bool Equals(TypeSig? a, TypeRef? b) => Equals(b, a); public bool Equals(TypeRef? a, TypeSig? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; 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; } public bool Equals(TypeSig? a, TypeSpec? b) => Equals(b, a); public bool Equals(TypeSpec? a, TypeSig? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; return Equals(a.TypeSig, b); } public bool Equals(ExportedType? a, TypeSpec? b) => Equals(b, a); public bool Equals(TypeSpec? a, ExportedType? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; return Equals(a.TypeSig, b); } public bool Equals(ExportedType? a, TypeSig? b) => Equals(b, a); public bool Equals(TypeSig? a, ExportedType? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; 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. HASHCODE_MAGIC_GLOBAL_TYPE; 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; bool result = Equals_TypeNames(a.Name, b.Name) && Equals_TypeNamespaces(a.Namespace, b.Namespace) && EqualsResolutionScope(a, b); recursionCounter.Decrement(); return result; } public int GetHashCode(TypeRef? a) { if (a is null) return TypeRefCanReferenceGlobalType ? GetHashCodeGlobalType() : 0; 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; } 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; bool result = Equals_TypeNames(a.TypeName, b.TypeName) && Equals_TypeNamespaces(a.TypeNamespace, b.TypeNamespace) && EqualsImplementation(a, b); recursionCounter.Decrement(); return result; } public int GetHashCode(ExportedType? a) { if (a is null) return TypeRefCanReferenceGlobalType ? GetHashCodeGlobalType() : 0; 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; } public bool Equals(TypeDef? a, TypeDef? b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; result = Equals_TypeNames(a.Name, b.Name) && Equals_TypeNamespaces(a.Namespace, b.Namespace) && Equals(a.DeclaringType, b.DeclaringType) && (DontCompareTypeScope || TypeDefScopeEquals(a, b)); recursionCounter.Decrement(); return result; } public int GetHashCode(TypeDef? a) { if (a is null || a.IsGlobalModuleType) return GetHashCodeGlobalType(); 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; } 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; } public int GetHashCode(TypeSpec? a) { if (a is null) return 0; return GetHashCode(a.TypeSig); } 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; // 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); 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; recursionCounter.Decrement(); return result; } 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; // 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); 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; recursionCounter.Decrement(); return result; } bool EqualsScope(TypeRef? a, ExportedType? b) { if ((object?)a == 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; // 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); 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 result = false; recursionCounter.Decrement(); return result; } 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); } bool Equals(IModule? a, FileDef? b) { if ((object?)a == 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); } 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) || (IsTargetOrSourceModule(a) && IsTargetOrSourceModule(b)); } 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(IAssembly a) => a.IsCorLib(); 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; } 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 || dnlib_Utils.Equals(a.Version, b.Version)) && (!CompareAssemblyLocale || dnlib_Utils.LocaleEquals(a.Culture, b.Culture)); if (!result) result = IsTargetOrSourceAssembly(a) && IsTargetOrSourceAssembly(b); recursionCounter.Decrement(); return result; } bool IsTargetOrSourceAssembly(IAssembly? a) => IsTargetAssembly(a) || IsSourceAssembly(a); bool IsTargetAssembly(IAssembly? a) => a is not null && __AssemblyEquals(a, importOptions.TargetModule.Assembly); bool IsSourceAssembly(IAssembly? a) => a is not null && __AssemblyEquals(a, importOptions.SourceModule.Assembly); bool IsTargetOrSourceModule(IModule? a) => IsTargetModule(a) || IsSourceModule(a); bool IsTargetModule(IModule? a) => a is not null && __ModuleEquals(a, importOptions.TargetModule); bool IsSourceModule(IModule? a) => a is not null && __ModuleEquals(a, importOptions.SourceModule); bool __AssemblyEquals(IAssembly? a, AssemblyDef? b) { if ((object?)a == b) return true; if (a is null || b is null) return false; if (a is AssemblyDef a2) return a2 == b; return AssemblyNameComparer.CompareAll.Equals(a, b); } bool __ModuleEquals(IModule? a, ModuleDef? b) { if ((object?)a == b) return true; if (a is null || b is null) return false; if (a is ModuleDef a2) return a2 == b; return StringComparer.OrdinalIgnoreCase.Equals(a.Name, b.Name); } 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 (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 = (ArraySig)a, arb = (ArraySig)b; result = ara.Rank == arb.Rank && Equals(ara.Sizes, arb.Sizes) && LowerBoundsEquals(ara.LowerBounds, arb.LowerBounds) && Equals(a.Next, b.Next); break; case ElementType.ValueType: case ElementType.Class: if (RawSignatureCompare) result = TokenEquals(((ClassOrValueTypeSig)a).TypeDefOrRef, ((ClassOrValueTypeSig)b).TypeDefOrRef); else result = Equals((IType)((ClassOrValueTypeSig)a).TypeDefOrRef, (IType)((ClassOrValueTypeSig)b).TypeDefOrRef); break; case ElementType.Var: case ElementType.MVar: result = ((GenericSig)a).Number == ((GenericSig)b).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 is null ? null : gt1.TypeDefOrRef, gt2 is null ? null : 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(((FnPtrSig)a).Signature, ((FnPtrSig)b).Signature); break; case ElementType.CModReqd: case ElementType.CModOpt: if (RawSignatureCompare) result = TokenEquals(((ModifierSig)a).Modifier, ((ModifierSig)b).Modifier) && Equals(a.Next, b.Next); else result = Equals((IType)((ModifierSig)a).Modifier, (IType)((ModifierSig)b).Modifier) && Equals(a.Next, b.Next); break; case ElementType.ValueArray: result = ((ValueArraySig)a).Size == ((ValueArraySig)b).Size && Equals(a.Next, b.Next); break; case ElementType.Module: result = ((ModuleSig)a).Index == ((ModuleSig)b).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; } public int GetHashCode(TypeSig? a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash; 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)((TypeDefOrRefSig)a).TypeDefOrRef); break; case ElementType.Sentinel: hash = HASHCODE_MAGIC_ET_SENTINEL; break; case ElementType.Ptr: hash = HASHCODE_MAGIC_ET_PTR + GetHashCode(a.Next); break; case ElementType.ByRef: hash = HASHCODE_MAGIC_ET_BYREF + GetHashCode(a.Next); break; case ElementType.SZArray: hash = HASHCODE_MAGIC_ET_SZARRAY + GetHashCode(a.Next); 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); break; case ElementType.Array: // Don't include sizes and lower bounds since GetHashCode(Type) doesn't (and can't). var ara = (ArraySig)a; hash = HASHCODE_MAGIC_ET_ARRAY + (int)ara.Rank + GetHashCode(ara.Next); break; case ElementType.Var: hash = HASHCODE_MAGIC_ET_VAR + (int)((GenericVar)a).Number; break; case ElementType.MVar: hash = HASHCODE_MAGIC_ET_MVAR + (int)((GenericMVar)a).Number; break; case ElementType.GenericInst: var gia = (GenericInstSig)a; hash = HASHCODE_MAGIC_ET_GENERICINST; hash += GetHashCode(gia.GenericType); hash += GetHashCode(gia.GenericArguments); break; case ElementType.FnPtr: hash = GetHashCode_FnPtr_SystemIntPtr(); break; case ElementType.ValueArray: hash = HASHCODE_MAGIC_ET_VALUEARRAY + (int)((ValueArraySig)a).Size + GetHashCode(a.Next); break; case ElementType.Module: hash = HASHCODE_MAGIC_ET_MODULE + (int)((ModuleSig)a).Index + GetHashCode(a.Next); break; case ElementType.End: case ElementType.R: case ElementType.Internal: default: hash = 0; break; } recursionCounter.Decrement(); return hash; } public bool Equals(IList? a, IList? b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; if (a.Count != b.Count) result = false; else { int i; for (i = 0; i < a.Count; i++) { if (!Equals(a[i], b[i])) break; } result = i == a.Count; } recursionCounter.Decrement(); return result; } public int GetHashCode(IList? a) { 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]); hash = (hash << 13) | (hash >> 19); } recursionCounter.Decrement(); return (int)hash; } bool Equals(IList? a, IList? b) { if (a == b) return true; if (a is null || b is null) return false; if (a.Count != b.Count) return false; for (int i = 0; i < a.Count; i++) { if (a[i] != b[i]) return false; } return true; } bool LowerBoundsEquals(IList? a, IList? b) { if (a == b) return true; if (a is null || b is null) return false; if (a.Count != 0 && b.Count != 0 && a.Count != b.Count) return false; int count = Math.Max(a.Count, b.Count); for (int i = 0; i < count; i++) { var ai = i >= a.Count ? 0 : a[i]; var bi = i >= b.Count ? 0 : b[i]; if (ai != bi) return false; } return true; } public bool Equals(CallingConventionSig? a, CallingConventionSig? b) => Equals(a, b, true); bool Equals(CallingConventionSig? a, CallingConventionSig? b, bool compareHasThisFlag) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; var mask = compareHasThisFlag ? ~(CallingConvention)0 : ~(CallingConvention)0 & ~CallingConvention.HasThis; if ((a.GetCallingConvention() & mask) != (b.GetCallingConvention() & mask)) 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: MethodBaseSig ma = (MethodBaseSig)a, mb = (MethodBaseSig)b; result = ma is not null && mb is not null && Equals(ma, mb, compareHasThisFlag); break; case CallingConvention.Field: FieldSig fa = (FieldSig)a, fb = (FieldSig)b; result = fa is not null && fb is not null && Equals(fa, fb); break; case CallingConvention.LocalSig: LocalSig la = (LocalSig)a, lb = (LocalSig)b; result = la is not null && lb is not null && Equals(la, lb); break; case CallingConvention.GenericInst: GenericInstMethodSig ga = (GenericInstMethodSig)a, gb = (GenericInstMethodSig)b; result = ga is not null && gb is not null && Equals(ga, gb); break; case CallingConvention.Unmanaged: default: result = false; break; } } recursionCounter.Decrement(); return result; } 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: MethodBaseSig? ma = a as MethodBaseSig; hash = ma is null ? 0 : GetHashCode(ma); break; case CallingConvention.Field: FieldSig? fa = a as FieldSig; hash = fa is null ? 0 : GetHashCode(fa); break; case CallingConvention.LocalSig: LocalSig? la = a as LocalSig; hash = la is null ? 0 : GetHashCode(la); break; case CallingConvention.GenericInst: GenericInstMethodSig? ga = a as GenericInstMethodSig; hash = ga is null ? 0 : GetHashCode(ga); break; case CallingConvention.Unmanaged: default: hash = GetHashCode_CallingConvention(a); break; } recursionCounter.Decrement(); return hash; } public bool Equals(MethodBaseSig? a, MethodBaseSig? b) => Equals(a, b, true); bool Equals(MethodBaseSig? a, MethodBaseSig? b, bool compareHasThisFlag) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; var mask = compareHasThisFlag ? ~(CallingConvention)0 : ~(CallingConvention)0 & ~CallingConvention.HasThis; bool result = (a.GetCallingConvention() & mask) == (b.GetCallingConvention() & mask) && (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; } 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) { 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; } } 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; } 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; } 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; } 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; } 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; } 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; } 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; } 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; } public bool Equals(MemberRef? a, MethodDef? b) => Equals(b, a); public bool Equals(MethodDef? a, MemberRef? b) { if ((object?)a == b) return true; // both are null if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; result = (PrivateScopeMethodIsComparable || !a.IsPrivateScope) && Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.Signature, b.Signature) && (!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.Class)); recursionCounter.Decrement(); return result; } public bool Equals(MethodDef? a, MethodDef? b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result; result = Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.Signature, b.Signature) && (!CompareMethodFieldDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); return result; } public int GetHashCode(MethodDef? a) { 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; } 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; bool result = Equals_MethodFieldNames(a.Name, b.Name) && Equals(a.Signature, b.Signature) && (!CompareMethodFieldDeclaringType || Equals(a.Class, b.Class)); recursionCounter.Decrement(); return result; } public int GetHashCode(MemberRef? a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash = GetHashCode_MethodFieldName(a.Name); hash += GetHashCode(a.Signature); if (CompareMethodFieldDeclaringType) hash += GetHashCode(a.Class); recursionCounter.Decrement(); return hash; } 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; } public int GetHashCode(MethodSpec? a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; int hash = GetHashCode(a.Method); recursionCounter.Decrement(); return hash; } 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 is null ? null : omoda.Assembly, omodb is null ? null : 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; } 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; } 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; } 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; } public bool Equals(MemberRef? a, FieldDef? b) => Equals(b, a); public bool Equals(FieldDef? a, MemberRef? b) { if ((object?)a == 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; } public bool Equals(FieldDef? a, FieldDef? b) { if (a == b) return true; if (a is null || b is null) 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; } public int GetHashCode(FieldDef? a) { 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; } public bool Equals(PropertyDef? a, PropertyDef? b) { if (a == b) return true; if (a is null || b is null) return false; if (!recursionCounter.Increment()) return false; bool result = Equals_PropertyNames(a.Name, b.Name) && // The mcs compiler doesn't set the HasThis flag even if it's an instance property so ignore it // when comparing properties. Equals(a.Type, b.Type, false) && (!ComparePropertyDeclaringType || Equals(a.DeclaringType, b.DeclaringType)); recursionCounter.Decrement(); return result; } public int GetHashCode(PropertyDef? a) { if (a is null) return 0; if (!recursionCounter.Increment()) return 0; var sig = a.PropertySig; int hash = GetHashCode_PropertyName(a.Name) + GetHashCode(sig is null ? null : sig.RetType); if (ComparePropertyDeclaringType) hash += GetHashCode(a.DeclaringType); recursionCounter.Decrement(); return hash; } public bool Equals(EventDef? a, EventDef? b) { if (a == b) return true; if (a is null || b is null) 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; } public int GetHashCode(EventDef? a) { 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 == 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 is null ? null : module.Assembly; static int GetHashCode_ElementType_MVar(int numGenericParams) => GetHashCode(numGenericParams, HASHCODE_MAGIC_ET_MVAR); static int GetHashCode(int numGenericParams, int etypeHashCode) { uint hash = 0; for (int i = 0; i < numGenericParams; i++) { hash += (uint)(etypeHashCode + i); hash = (hash << 13) | (hash >> 19); } return (int)hash; } public override string ToString() => $"{recursionCounter} - {options}"; } // From dnlib.DotNet.dnlib_Utils static class dnlib_Utils { static int CompareTo(Version? a, Version? b) { if (a is null) a = new Version(); if (b is null) b = new Version(); if (a.Major != b.Major) return a.Major.CompareTo(b.Major); if (a.Minor != b.Minor) return a.Minor.CompareTo(b.Minor); if (GetDefaultVersionValue(a.Build) != GetDefaultVersionValue(b.Build)) return GetDefaultVersionValue(a.Build).CompareTo(GetDefaultVersionValue(b.Build)); return GetDefaultVersionValue(a.Revision).CompareTo(GetDefaultVersionValue(b.Revision)); } internal static bool Equals(Version a, Version b) => CompareTo(a, b) == 0; static int GetDefaultVersionValue(int val) => val == -1 ? 0 : val; static int LocaleCompareTo(UTF8String a, UTF8String b) => GetCanonicalLocale(a).CompareTo(GetCanonicalLocale(b)); internal static bool LocaleEquals(UTF8String a, UTF8String b) => LocaleCompareTo(a, b) == 0; static string GetCanonicalLocale(UTF8String locale) => GetCanonicalLocale(UTF8String.ToSystemStringOrEmpty(locale)); static string GetCanonicalLocale(string locale) { var s = locale.ToUpperInvariant(); if (s == "NEUTRAL") s = string.Empty; return s; } } }