| File: Linker\MethodReferenceComparer.cs | Web Access |
| Project: src\runtime\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/MethodReferenceComparer.cs internal sealed class MethodReferenceComparer : EqualityComparer<MethodReference> { // Initialized lazily for each thread [ThreadStatic] static List<MethodReference>? xComparisonStack; [ThreadStatic] static List<MethodReference>? yComparisonStack; public readonly ITryResolveMetadata _resolver; public MethodReferenceComparer(ITryResolveMetadata resolver) { _resolver = resolver; } public override bool Equals(MethodReference? x, MethodReference? y) { return AreEqual(x, y, _resolver); } public override int GetHashCode(MethodReference obj) { return GetHashCodeFor(obj); } public static bool AreEqual(MethodReference? x, MethodReference? y, ITryResolveMetadata resolver) { if (ReferenceEquals(x, y)) return true; if (x is null ^ y is null) return false; Debug.Assert(x is not null); Debug.Assert(y is not null); if (x.HasThis != y.HasThis) return false; #pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil if (x.HasParameters != y.HasParameters) return false; #pragma warning restore RS0030 if (x.HasGenericParameters != y.HasGenericParameters) return false; #pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil if (x.Parameters.Count != y.Parameters.Count) return false; #pragma warning restore RS0030 if (x.Name != y.Name) return false; if (!TypeReferenceEqualityComparer.AreEqual(x.DeclaringType, y.DeclaringType, resolver)) return false; var xGeneric = x as GenericInstanceMethod; var yGeneric = y as GenericInstanceMethod; if (xGeneric != null || yGeneric != null) { if (xGeneric == null || yGeneric == null) return false; if (xGeneric.GenericArguments.Count != yGeneric.GenericArguments.Count) return false; for (int i = 0; i < xGeneric.GenericArguments.Count; i++) if (!TypeReferenceEqualityComparer.AreEqual(xGeneric.GenericArguments[i], yGeneric.GenericArguments[i], resolver)) return false; } var xResolved = resolver.TryResolve(x); var yResolved = resolver.TryResolve(y); if (xResolved != yResolved) return false; if (xResolved == null) { // We couldn't resolve either method. In order for them to be equal, their parameter types _must_ match. But wait, there's a twist! // There exists a situation where we might get into a recursive state: parameter type comparison might lead to comparing the same // methods again if the parameter types are generic parameters whose owners are these methods. We guard against these by using a // thread static list of all our comparisons carried out in the stack so far, and if we're in progress of comparing them already, // we'll just say that they match. xComparisonStack ??= new List<MethodReference>(); yComparisonStack ??= new List<MethodReference>(); for (int i = 0; i < xComparisonStack.Count; i++) { if (xComparisonStack[i] == x && yComparisonStack[i] == y) return true; } xComparisonStack.Add(x); try { yComparisonStack.Add(y); try { #pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil for (int i = 0; i < x.Parameters.Count; i++) { if (!TypeReferenceEqualityComparer.AreEqual(x.Parameters[i].ParameterType, y.Parameters[i].ParameterType, resolver)) return false; } #pragma warning restore RS0030 } finally { yComparisonStack.RemoveAt(yComparisonStack.Count - 1); } } finally { xComparisonStack.RemoveAt(xComparisonStack.Count - 1); } } return true; } public static bool AreSignaturesEqual(MethodReference x, MethodReference y, ITryResolveMetadata resolver, TypeComparisonMode comparisonMode = TypeComparisonMode.Exact) { if (x.HasThis != y.HasThis) return false; #pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil if (x.Parameters.Count != y.Parameters.Count) return false; #pragma warning restore RS0030 if (x.GenericParameters.Count != y.GenericParameters.Count) return false; #pragma warning disable RS0030 // MethodReference.HasParameters is banned - this code is copied from Cecil for (var i = 0; i < x.Parameters.Count; i++) if (!TypeReferenceEqualityComparer.AreEqual(x.Parameters[i].ParameterType, y.Parameters[i].ParameterType, resolver, comparisonMode)) return false; #pragma warning restore RS0030 if (!TypeReferenceEqualityComparer.AreEqual(x.ReturnType, y.ReturnType, resolver, comparisonMode)) return false; return true; } public static int GetHashCodeFor(MethodReference obj) { // a very good prime number const int hashCodeMultiplier = 486187739; var genericInstanceMethod = obj as GenericInstanceMethod; if (genericInstanceMethod != null) { var hashCode = GetHashCodeFor(genericInstanceMethod.ElementMethod); for (var i = 0; i < genericInstanceMethod.GenericArguments.Count; i++) hashCode = hashCode * hashCodeMultiplier + TypeReferenceEqualityComparer.GetHashCodeFor(genericInstanceMethod.GenericArguments[i]); return hashCode; } return TypeReferenceEqualityComparer.GetHashCodeFor(obj.DeclaringType) * hashCodeMultiplier + obj.Name.GetHashCode(); } } }