|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection.Runtime.TypeInfos;
namespace System.Reflection.Runtime.BindingFlagSupport
{
//=================================================================================================================
// This class encapsulates the minimum set of arcane desktop CLR policies needed to implement the Get*(BindingFlags) apis.
//
// In particular, it encapsulates behaviors such as what exactly determines the "visibility" of a property and event, and
// what determines whether and how they are overridden.
//=================================================================================================================
internal abstract class MemberPolicies<M> where M : MemberInfo
{
public MemberPolicies(int index)
{
Index = index;
}
//=================================================================================================================
// Subclasses for specific MemberInfo types must override these:
//=================================================================================================================
//
// Returns all of the directly declared members on the given Type.
//
public abstract IEnumerable<M> GetDeclaredMembers(Type type);
//
// Returns all of the directly declared members on the given TypeInfo whose name matches optionalNameFilter. If optionalNameFilter is null,
// returns all directly declared members.
//
public abstract IEnumerable<M> CoreGetDeclaredMembers(RuntimeTypeInfo type, NameFilter? optionalNameFilter, RuntimeTypeInfo reflectedType);
//
// Policy to decide whether a member is considered "virtual", "virtual new" and what its member visibility is.
// (For "visibility", we reuse the MethodAttributes enum since Reflection lacks an element-agnostic enum for this.
// Only the MemberAccessMask bits are set.)
//
public abstract void GetMemberAttributes(M member, out MethodAttributes visibility, out bool isStatic, out bool isVirtual, out bool isNewSlot);
//
// Policy to decide whether "derivedMember" is a virtual override of "baseMember." Used to implement MethodInfo.GetBaseDefinition(),
// parent chain traversal for discovering inherited custom attributes, and suppressing lookup results in the Type.Get*() api family.
//
// Does not consider explicit overrides (methodimpls.) Does not consider "overrides" of interface methods.
//
public abstract bool ImplicitlyOverrides(M baseMember, M derivedMember);
//
// Policy to decide how BindingFlags should be reinterpreted for a given member type.
// This is overridden for nested types which all match on any combination Instance | Static and are never inherited.
// It is also overridden for constructors which are never inherited.
//
public virtual BindingFlags ModifyBindingFlags(BindingFlags bindingFlags)
{
return bindingFlags;
}
//
// Policy to decide if BindingFlags is always interpreted as having set DeclaredOnly.
//
public abstract bool AlwaysTreatAsDeclaredOnly { get; }
//
// Policy to decide how or if members in more derived types hide same-named members in base types.
// Due to desktop compat concerns, the definitions are a bit more arbitrary than we'd like.
//
public abstract bool IsSuppressedByMoreDerivedMember(M member, M[] priorMembers, int startIndex, int endIndex);
//
// Policy to decide whether to throw an AmbiguousMatchException on an ambiguous Type.Get*() call.
// Does not apply to GetConstructor/GetMethod/GetProperty calls that have a non-null Type[] array passed to it.
//
// If method returns true, the Get() api will pick the member that's in the most derived type.
// If method returns false, the Get() api throws AmbiguousMatchException.
//
public abstract bool OkToIgnoreAmbiguity(M m1, M m2);
//
// Helper method for determining whether two methods are signature-compatible.
//
protected static bool AreNamesAndSignaturesEqual(MethodInfo method1, MethodInfo method2)
{
if (method1.Name != method2.Name)
return false;
ReadOnlySpan<ParameterInfo> p1 = method1.GetParametersAsSpan();
ReadOnlySpan<ParameterInfo> p2 = method2.GetParametersAsSpan();
if (p1.Length != p2.Length)
return false;
bool isGenericMethod1 = method1.IsGenericMethodDefinition;
bool isGenericMethod2 = method2.IsGenericMethodDefinition;
if (isGenericMethod1 != isGenericMethod2)
return false;
if (!isGenericMethod1)
{
for (int i = 0; i < p1.Length; i++)
{
Type parameterType1 = p1[i].ParameterType;
Type parameterType2 = p2[i].ParameterType;
if (!(parameterType1.Equals(parameterType2)))
{
return false;
}
}
}
else
{
if (method1.GetGenericArguments().Length != method2.GetGenericArguments().Length)
return false;
for (int i = 0; i < p1.Length; i++)
{
Type parameterType1 = p1[i].ParameterType;
Type parameterType2 = p2[i].ParameterType;
if (!GenericMethodAwareAreParameterTypesEqual(parameterType1, parameterType2))
{
return false;
}
}
}
return true;
}
//
// This helper compares the types of the corresponding parameters of two methods to see if one method is signature equivalent to the other.
// This is needed when comparing the signatures of two generic methods as Type.Equals() is not up to that job.
//
private static bool GenericMethodAwareAreParameterTypesEqual(Type t1, Type t2)
{
// Fast-path - if Reflection has already deemed them equivalent, we can trust its result.
if (t1.Equals(t2))
return true;
// If we got here, Reflection determined the types not equivalent. Most of the time, that's the result we want.
// There is however, one wrinkle. If the type is or embeds a generic method parameter type, Reflection will always report them
// non-equivalent, since generic parameter type comparison always compares both the position and the declaring method. For our purposes, though,
// we only want to consider the position.
// Fast-path: if the types don't embed any generic parameters, we can go ahead and use Reflection's result.
if (!(t1.ContainsGenericParameters && t2.ContainsGenericParameters))
return false;
if ((t1.IsArray && t2.IsArray) || (t1.IsByRef && t2.IsByRef) || (t1.IsPointer && t2.IsPointer))
{
if (t1.IsSZArray != t2.IsSZArray)
return false;
if (t1.IsArray && (t1.GetArrayRank() != t2.GetArrayRank()))
return false;
return GenericMethodAwareAreParameterTypesEqual(t1.GetElementType(), t2.GetElementType());
}
if (t1.IsConstructedGenericType && t2.IsConstructedGenericType)
{
// We can use regular old Equals() rather than recursing into GenericMethodAwareAreParameterTypesEqual() since the
// generic type definition will always be a plain old named type and won't embed any generic method parameters.
if (!(t1.GetGenericTypeDefinition().Equals(t2.GetGenericTypeDefinition())))
return false;
Type[] ga1 = t1.GenericTypeArguments;
Type[] ga2 = t2.GenericTypeArguments;
if (ga1.Length != ga2.Length)
return false;
for (int i = 0; i < ga1.Length; i++)
{
if (!GenericMethodAwareAreParameterTypesEqual(ga1[i], ga2[i]))
return false;
}
return true;
}
if (t1.IsGenericMethodParameter && t2.IsGenericMethodParameter)
{
// A generic method parameter. The DeclaringMethods will be different but we don't care about that - we can assume that
// the declaring method will be the method that declared the parameter's whose type we're testing. We only need to
// compare the positions.
return t1.GenericParameterPosition == t2.GenericParameterPosition;
}
// If we got here, either t1 and t2 are different flavors of types or they are both simple named types or both generic type parameters.
// Either way, we can trust Reflection's result here.
return false;
}
protected const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
//
// This returns a fixed value from 0 to MemberIndex.Count-1 with each possible type of M
// being assigned a unique index (see the MemberTypeIndex for possible values). This is useful
// for converting a type reference to M to an array index or switch case label.
//
public int Index { get; }
}
}
|