| File: System\Reflection\Runtime\BindingFlagSupport\Shared.cs | Web Access |
| Project: src\runtime\src\coreclr\nativeaot\System.Private.CoreLib\src\System.Private.CoreLib.csproj (System.Private.CoreLib) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Reflection.Runtime.General; namespace System.Reflection.Runtime.BindingFlagSupport { internal static class Shared { // // This is similar to FilterApplyMethodBase from CoreClr with some important differences: // // - Does *not* filter on Public|NonPublic|Instance|Static|FlatternHierarchy. Caller is expected to have done that. // // - ArgumentTypes cannot be null. // // Used by Type.GetMethodImpl(), Type.GetConstructorImpl(), Type.InvokeMember() and Activator.CreateInstance(). Does some // preliminary weeding out of candidate methods based on the supplied calling convention and parameter list lengths. // // Candidates must pass this screen before we involve the binder. // public static bool QualifiesBasedOnParameterCount(this MethodBase methodBase, BindingFlags bindingFlags, CallingConventions callConv, Type?[] argumentTypes) { Debug.Assert(methodBase is not null); Debug.Assert(argumentTypes is not null); #if DEBUG bindingFlags &= ~(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.FlattenHierarchy | BindingFlags.IgnoreCase); #endif #region Check CallingConvention if ((callConv & CallingConventions.Any) == 0) { if ((callConv & CallingConventions.VarArgs) != 0 && (methodBase.CallingConvention & CallingConventions.VarArgs) == 0) return false; if ((callConv & CallingConventions.Standard) != 0 && (methodBase.CallingConvention & CallingConventions.Standard) == 0) return false; } #endregion #region ArgumentTypes ReadOnlySpan<ParameterInfo> parameterInfos = methodBase.GetParametersAsSpan(); if (argumentTypes.Length != parameterInfos.Length) { #region Invoke Member, Get\Set & Create Instance specific case // If the number of supplied arguments differs than the number in the signature AND // we are not filtering for a dynamic call -- InvokeMethod or CreateInstance -- filter out the method. if ((bindingFlags & (BindingFlags.InvokeMethod | BindingFlags.CreateInstance | BindingFlags.GetProperty | BindingFlags.SetProperty)) == 0) return false; bool testForParamArray = false; bool excessSuppliedArguments = argumentTypes.Length > parameterInfos.Length; if (excessSuppliedArguments) { // more supplied arguments than parameters, additional arguments could be vararg #region Varargs // If method is not vararg, additional arguments can not be passed as vararg if ((methodBase.CallingConvention & CallingConventions.VarArgs) == 0) { testForParamArray = true; } else { // If Binding flags did not include varargs we would have filtered this vararg method. // This Invariant established during callConv check. Debug.Assert((callConv & CallingConventions.VarArgs) != 0); } #endregion } else {// fewer supplied arguments than parameters, missing arguments could be optional #region OptionalParamBinding if ((bindingFlags & BindingFlags.OptionalParamBinding) == 0) { testForParamArray = true; } else { // From our existing code, our policy here is that if a parameterInfo // is optional then all subsequent parameterInfos shall be optional. // Thus, iff the first parameterInfo is not optional then this MethodInfo is no longer a canidate. if (!parameterInfos[argumentTypes.Length].IsOptional) testForParamArray = true; } #endregion } #region ParamArray if (testForParamArray) { if (parameterInfos.Length == 0) return false; // The last argument of the signature could be a param array. bool shortByMoreThanOneSuppliedArgument = argumentTypes.Length < parameterInfos.Length - 1; if (shortByMoreThanOneSuppliedArgument) return false; ParameterInfo lastParameter = parameterInfos[parameterInfos.Length - 1]; if (!lastParameter.ParameterType.IsArray) return false; if (!lastParameter.IsDefined(typeof(ParamArrayAttribute), false)) return false; } #endregion #endregion } else { #region Exact Binding if ((bindingFlags & BindingFlags.ExactBinding) != 0) { // Legacy behavior is to ignore ExactBinding when InvokeMember is specified. // Why filter by InvokeMember? If the answer is we leave this to the binder then why not leave // all the rest of this to the binder too? Further, what other semanitc would the binder // use for BindingFlags.ExactBinding besides this one? Further, why not include CreateInstance // in this if statement? That's just InvokeMethod with a constructor, right? if ((bindingFlags & (BindingFlags.InvokeMethod)) == 0) { for (int i = 0; i < parameterInfos.Length; i++) { // a null argument type implies a null arg which is always a perfect match if (argumentTypes[i] is not null && !argumentTypes[i].MatchesParameterTypeExactly(parameterInfos[i])) return false; } } } #endregion } #endregion return true; } // // If member is a virtual member that implicitly overrides a member in a base class, return the overridden member. // Otherwise, return null. // // - MethodImpls ignored. (I didn't say it made sense, this is just how the desktop api we're porting behaves.) // - Implemented interfaces ignores. (I didn't say it made sense, this is just how the desktop api we're porting behaves.) // public static M GetImplicitlyOverriddenBaseClassMember<M>(this M member, MemberPolicies<M> policies) where M : MemberInfo { bool isVirtual; bool isNewSlot; policies.GetMemberAttributes(member, out _, out _, out isVirtual, out isNewSlot); if (isNewSlot || !isVirtual) { return null; } string name = member.Name; Type type = member.DeclaringType!; for (; ; ) { Type? baseType = type.BaseType; if (baseType == null) { return null; } type = baseType; foreach (M candidate in policies.GetDeclaredMembers(type)) { if (candidate.Name != name) { continue; } bool isCandidateVirtual; policies.GetMemberAttributes(member, out _, out _, out isCandidateVirtual, out _); if (!isCandidateVirtual) { continue; } if (!policies.ImplicitlyOverrides(candidate, member)) { continue; } return candidate; } } } } }