File: Linker\TypeReferenceEqualityComparer.cs
Web Access
Project: src\src\tools\illink\src\linker\Mono.Linker.csproj (illink)
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil;
 
namespace Mono.Linker
{
    // Copied from https://github.com/jbevain/cecil/blob/master/Mono.Cecil/TypeReferenceComparer.cs
    internal sealed class TypeReferenceEqualityComparer : EqualityComparer<TypeReference>
    {
        public readonly ITryResolveMetadata _resolver;
 
        public TypeReferenceEqualityComparer(ITryResolveMetadata resolver)
        {
            _resolver = resolver;
        }
 
        public override bool Equals(TypeReference? x, TypeReference? y)
        {
            return AreEqual(x, y, _resolver);
        }
 
        public override int GetHashCode(TypeReference obj)
        {
            return GetHashCodeFor(obj);
        }
 
        public static bool AreEqual(TypeReference? a, TypeReference? b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact)
        {
            if (ReferenceEquals(a, b))
                return true;
 
            if (a == null || b == null)
                return false;
 
            var aMetadataType = a.MetadataType;
            var bMetadataType = b.MetadataType;
 
            if (aMetadataType == MetadataType.GenericInstance || bMetadataType == MetadataType.GenericInstance)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                return AreEqual((GenericInstanceType)a, (GenericInstanceType)b, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.Array || bMetadataType == MetadataType.Array)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                var a1 = (ArrayType)a;
                var b1 = (ArrayType)b;
                if (a1.Rank != b1.Rank)
                    return false;
 
                return AreEqual(a1.ElementType, b1.ElementType, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.Var || bMetadataType == MetadataType.Var)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                return AreEqual((GenericParameter)a, (GenericParameter)b, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.MVar || bMetadataType == MetadataType.MVar)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                return AreEqual((GenericParameter)a, (GenericParameter)b, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.ByReference || bMetadataType == MetadataType.ByReference)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                return AreEqual(((ByReferenceType)a).ElementType, ((ByReferenceType)b).ElementType, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.Pointer || bMetadataType == MetadataType.Pointer)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                return AreEqual(((PointerType)a).ElementType, ((PointerType)b).ElementType, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.RequiredModifier || bMetadataType == MetadataType.RequiredModifier)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                var a1 = (RequiredModifierType)a;
                var b1 = (RequiredModifierType)b;
 
                return AreEqual(a1.ModifierType, b1.ModifierType, resolver, comparisonMode) && AreEqual(a1.ElementType, b1.ElementType, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.OptionalModifier || bMetadataType == MetadataType.OptionalModifier)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                var a1 = (OptionalModifierType)a;
                var b1 = (OptionalModifierType)b;
 
                return AreEqual(a1.ModifierType, b1.ModifierType, resolver, comparisonMode) && AreEqual(a1.ElementType, b1.ElementType, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.Pinned || bMetadataType == MetadataType.Pinned)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                return AreEqual(((PinnedType)a).ElementType, ((PinnedType)b).ElementType, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.Sentinel || bMetadataType == MetadataType.Sentinel)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                return AreEqual(((SentinelType)a).ElementType, ((SentinelType)b).ElementType, resolver, comparisonMode);
            }
 
            if (aMetadataType == MetadataType.FunctionPointer || bMetadataType == MetadataType.FunctionPointer)
            {
                if (aMetadataType != bMetadataType)
                    return false;
 
                return AreEqual((FunctionPointerType)a, (FunctionPointerType)b, resolver, comparisonMode);
            }
 
            if (!a.Name.Equals(b.Name) || !a.Namespace.Equals(b.Namespace))
                return false;
 
            var xDefinition = resolver.TryResolve(a);
            var yDefinition = resolver.TryResolve(b);
            if (xDefinition == null || yDefinition == null)
                return false;
 
            // For loose signature the types could be in different assemblies, as long as the type names match we will consider them equal
            if (comparisonMode == TypeComparisonMode.SignatureOnlyLoose)
            {
                if (xDefinition.Module.Name != yDefinition.Module.Name)
                    return false;
 
                if (xDefinition.Module.Assembly.Name.Name != yDefinition.Module.Assembly.Name.Name)
                    return false;
 
                return xDefinition.FullName == yDefinition.FullName;
            }
 
            return xDefinition == yDefinition;
        }
 
        static bool AreEqual(GenericParameter a, GenericParameter b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact)
        {
            if (ReferenceEquals(a, b))
                return true;
 
            if (a.Position != b.Position)
                return false;
 
            if (a.Type != b.Type)
                return false;
 
            var aOwnerType = a.Owner as TypeReference;
            if (aOwnerType != null && AreEqual(aOwnerType, b.Owner as TypeReference, resolver, comparisonMode))
                return true;
 
            var aOwnerMethod = a.Owner as MethodReference;
            if (aOwnerMethod != null && comparisonMode != TypeComparisonMode.SignatureOnlyLoose && MethodReferenceComparer.AreEqual(aOwnerMethod, b.Owner as MethodReference, resolver))
                return true;
 
            return comparisonMode == TypeComparisonMode.SignatureOnly || comparisonMode == TypeComparisonMode.SignatureOnlyLoose;
        }
 
        static bool AreEqual(GenericInstanceType a, GenericInstanceType b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact)
        {
            if (ReferenceEquals(a, b))
                return true;
 
            var aGenericArgumentsCount = a.GenericArguments.Count;
            if (aGenericArgumentsCount != b.GenericArguments.Count)
                return false;
 
            if (!AreEqual(a.ElementType, b.ElementType, resolver, comparisonMode))
                return false;
 
            for (int i = 0; i < aGenericArgumentsCount; i++)
                if (!AreEqual(a.GenericArguments[i], b.GenericArguments[i], resolver, comparisonMode))
                    return false;
 
            return true;
        }
 
        static bool AreEqual(FunctionPointerType a, FunctionPointerType b, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact)
        {
            if (ReferenceEquals(a, b))
                return true;
 
            if (a.CallingConvention != b.CallingConvention)
                return false;
 
            if (a.HasThis != b.HasThis)
                return false;
 
            if (a.ExplicitThis != b.ExplicitThis)
                return false;
 
            if (a.HasParameters != b.HasParameters)
                return false;
 
            if (a.ContainsGenericParameter != b.ContainsGenericParameter)
                return false;
 
            if (a.Parameters.Count != b.Parameters.Count)
                return false;
 
            for (int i = 0; i < a.Parameters.Count; i++)
            {
                if (!AreEqual(a.Parameters[i].ParameterType, b.Parameters[i].ParameterType, resolver, comparisonMode))
                    return false;
            }
 
            if (!AreEqual(a.ReturnType, b.ReturnType, resolver, comparisonMode))
                return false;
 
            return true;
        }
 
        public static int GetHashCodeFor(TypeReference obj)
        {
            // a very good prime number
            const int hashCodeMultiplier = 486187739;
            // prime numbers
            const int genericInstanceTypeMultiplier = 31;
            const int byReferenceMultiplier = 37;
            const int pointerMultiplier = 41;
            const int requiredModifierMultiplier = 43;
            const int optionalModifierMultiplier = 47;
            const int pinnedMultiplier = 53;
            const int sentinelMultiplier = 59;
            const int functionPointerMultiplier = 61;
 
            var metadataType = obj.MetadataType;
 
            if (metadataType == MetadataType.GenericInstance)
            {
                var genericInstanceType = (GenericInstanceType)obj;
                var hashCode = GetHashCodeFor(genericInstanceType.ElementType) * hashCodeMultiplier + genericInstanceTypeMultiplier;
                for (var i = 0; i < genericInstanceType.GenericArguments.Count; i++)
                    hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor(genericInstanceType.GenericArguments[i]);
                return hashCode;
            }
 
            if (metadataType == MetadataType.Array)
            {
                var arrayType = (ArrayType)obj;
                return GetHashCodeFor(arrayType.ElementType) * hashCodeMultiplier + arrayType.Rank.GetHashCode();
            }
 
            if (metadataType == MetadataType.Var || metadataType == MetadataType.MVar)
            {
                var genericParameter = (GenericParameter)obj;
                var hashCode = genericParameter.Position.GetHashCode() * hashCodeMultiplier + ((int)metadataType).GetHashCode();
 
                var ownerTypeReference = genericParameter.Owner as TypeReference;
                if (ownerTypeReference != null)
                    return hashCode * hashCodeMultiplier + GetHashCodeFor(ownerTypeReference);
 
                var ownerMethodReference = genericParameter.Owner as MethodReference;
                if (ownerMethodReference != null)
                    return hashCode * hashCodeMultiplier + MethodReferenceComparer.GetHashCodeFor(ownerMethodReference);
 
                throw new InvalidOperationException("Generic parameter encountered with invalid owner");
            }
 
            if (metadataType == MetadataType.ByReference)
            {
                var byReferenceType = (ByReferenceType)obj;
                return GetHashCodeFor(byReferenceType.ElementType) * hashCodeMultiplier * byReferenceMultiplier;
            }
 
            if (metadataType == MetadataType.Pointer)
            {
                var pointerType = (PointerType)obj;
                return GetHashCodeFor(pointerType.ElementType) * hashCodeMultiplier * pointerMultiplier;
            }
 
            if (metadataType == MetadataType.RequiredModifier)
            {
                var requiredModifierType = (RequiredModifierType)obj;
                var hashCode = GetHashCodeFor(requiredModifierType.ElementType) * requiredModifierMultiplier;
                hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor(requiredModifierType.ModifierType);
                return hashCode;
            }
 
            if (metadataType == MetadataType.OptionalModifier)
            {
                var optionalModifierType = (OptionalModifierType)obj;
                var hashCode = GetHashCodeFor(optionalModifierType.ElementType) * optionalModifierMultiplier;
                hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor(optionalModifierType.ModifierType);
                return hashCode;
            }
 
            if (metadataType == MetadataType.Pinned)
            {
                var pinnedType = (PinnedType)obj;
                return GetHashCodeFor(pinnedType.ElementType) * hashCodeMultiplier * pinnedMultiplier;
            }
 
            if (metadataType == MetadataType.Sentinel)
            {
                var sentinelType = (SentinelType)obj;
                return GetHashCodeFor(sentinelType.ElementType) * hashCodeMultiplier * sentinelMultiplier;
            }
 
            if (metadataType == MetadataType.FunctionPointer)
            {
                var functionPointerType = (FunctionPointerType)obj;
                var hashCode = GetHashCodeFor(functionPointerType.ReturnType) * hashCodeMultiplier + functionPointerMultiplier;
                for (var i = 0; i < functionPointerType.Parameters.Count; i++)
                    hashCode = hashCode * hashCodeMultiplier + GetHashCodeFor(functionPointerType.Parameters[i].ParameterType);
                return hashCode;
            }
 
            return obj.Namespace.GetHashCode() * hashCodeMultiplier + obj.FullName.GetHashCode();
        }
    }
}