File: src\System\Reflection\Associates.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\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.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
 
namespace System.Reflection
{
    internal static class Associates
    {
        [Flags]
        internal enum Attributes
        {
            ComposedOfAllVirtualMethods = 0x1,
            ComposedOfAllPrivateMethods = 0x2,
            ComposedOfNoPublicMembers = 0x4,
            ComposedOfNoStaticMembers = 0x8,
        }
 
        internal static bool IncludeAccessor(MethodInfo? associate, bool nonPublic)
        {
            if (associate is null)
                return false;
 
            if (nonPublic)
                return true;
 
            if (associate.IsPublic)
                return true;
 
            return false;
        }
 
        [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
            Justification = "Module.ResolveMethod is marked as RequiresUnreferencedCode because it relies on tokens" +
                            "which are not guaranteed to be stable across trimming. So if somebody hardcodes a token it could break." +
                            "The usage here is not like that as all these tokens come from existing metadata loaded from some IL" +
                            "and so trimming has no effect (the tokens are read AFTER trimming occurred).")]
        private static RuntimeMethodInfo? AssignAssociates(
            int tkMethod,
            RuntimeType declaredType,
            RuntimeType reflectedType)
        {
            if (MetadataToken.IsNullToken(tkMethod))
                return null;
 
            Debug.Assert(declaredType != null);
            Debug.Assert(reflectedType != null);
 
            bool isInherited = declaredType != reflectedType;
 
            scoped Span<IntPtr> genericArgumentHandles = default;
            RuntimeType[] genericArguments = declaredType.TypeHandle.GetInstantiationInternal();
            if (genericArguments != null)
            {
                genericArgumentHandles = genericArguments.Length <= 16 ? // arbitrary stackalloc limit
                    stackalloc IntPtr[genericArguments.Length] :
                    new IntPtr[genericArguments.Length];
                for (int i = 0; i < genericArguments.Length; i++)
                {
                    genericArgumentHandles[i] = genericArguments[i].TypeHandle.Value;
                }
            }
 
            RuntimeMethodHandleInternal associateMethodHandle = ModuleHandle.ResolveMethodHandleInternal(RuntimeTypeHandle.GetModule(declaredType), tkMethod, genericArgumentHandles, default);
            Debug.Assert(!associateMethodHandle.IsNullHandle(), "Failed to resolve associateRecord methodDef token");
 
            if (isInherited)
            {
                MethodAttributes methAttr = RuntimeMethodHandle.GetAttributes(associateMethodHandle);
 
                // ECMA MethodSemantics: "All methods for a given Property or Event shall have the same accessibility
                // (ie the MemberAccessMask subfield of their Flags row) and cannot be CompilerControlled  [CLS]"
                // Consequently, a property may be composed of public and private methods. If the declared type !=
                // the reflected type, the private methods should not be exposed. Note that this implies that the
                // identity of a property includes it's reflected type.
 
                if ((methAttr & MethodAttributes.MemberAccessMask) == MethodAttributes.Private)
                    return null;
 
                // Note this is the first time the property was encountered walking from the most derived class
                // towards the base class. It would seem to follow that any associated methods would not
                // be overridden -- but this is not necessarily true. A more derived class may have overridden a
                // virtual method associated with a property in a base class without associating the override with
                // the same or any property in the derived class.
                if ((methAttr & MethodAttributes.Virtual) != 0)
                {
                    bool declaringTypeIsClass =
                        (RuntimeTypeHandle.GetAttributes(declaredType) & TypeAttributes.ClassSemanticsMask) == TypeAttributes.Class;
 
                    // It makes no sense to search for a virtual override of a method declared on an interface.
                    if (declaringTypeIsClass)
                    {
                        int slot = RuntimeMethodHandle.GetSlot(associateMethodHandle);
 
                        // Find the override visible from the reflected type
                        associateMethodHandle = RuntimeTypeHandle.GetMethodAt(reflectedType, slot);
                    }
                }
            }
 
            RuntimeMethodInfo? associateMethod =
                RuntimeType.GetMethodBase(reflectedType, associateMethodHandle) as RuntimeMethodInfo;
 
            // suppose a property was mapped to a method not in the derivation hierarchy of the reflectedTypeHandle
            return associateMethod ?? reflectedType.Module.ResolveMethod(tkMethod, null, null) as RuntimeMethodInfo;
        }
 
        internal static void AssignAssociates(
            MetadataImport scope,
            int mdPropEvent,
            RuntimeType declaringType,
            RuntimeType reflectedType,
            out RuntimeMethodInfo? addOn,
            out RuntimeMethodInfo? removeOn,
            out RuntimeMethodInfo? fireOn,
            out RuntimeMethodInfo? getter,
            out RuntimeMethodInfo? setter,
            out MethodInfo[]? other,
            out bool composedOfAllPrivateMethods,
            out BindingFlags bindingFlags)
        {
            addOn = removeOn = fireOn = getter = setter = null;
 
            Attributes attributes =
                Attributes.ComposedOfAllPrivateMethods |
                Attributes.ComposedOfAllVirtualMethods |
                Attributes.ComposedOfNoPublicMembers |
                Attributes.ComposedOfNoStaticMembers;
 
            while (RuntimeTypeHandle.IsGenericVariable(reflectedType))
                reflectedType = (RuntimeType)reflectedType.BaseType!;
 
            bool isInherited = declaringType != reflectedType;
 
            List<MethodInfo>? otherList = null;
 
            scope.Enum(MetadataTokenType.MethodDef, mdPropEvent, out MetadataEnumResult associatesData);
 
            int cAssociates = associatesData.Length / 2;
 
            for (int i = 0; i < cAssociates; i++)
            {
                int methodDefToken = associatesData[i * 2];
                MethodSemanticsAttributes semantics = (MethodSemanticsAttributes)associatesData[i * 2 + 1];
 
                #region Assign each associate
                RuntimeMethodInfo? associateMethod =
                    AssignAssociates(methodDefToken, declaringType, reflectedType);
 
                if (associateMethod == null)
                    continue;
 
                MethodAttributes methAttr = associateMethod.Attributes;
                bool isPrivate = (methAttr & MethodAttributes.MemberAccessMask) == MethodAttributes.Private;
                bool isVirtual = (methAttr & MethodAttributes.Virtual) != 0;
 
                MethodAttributes visibility = methAttr & MethodAttributes.MemberAccessMask;
                bool isPublic = visibility == MethodAttributes.Public;
                bool isStatic = (methAttr & MethodAttributes.Static) != 0;
 
                if (isPublic)
                {
                    attributes &= ~Attributes.ComposedOfNoPublicMembers;
                    attributes &= ~Attributes.ComposedOfAllPrivateMethods;
                }
                else if (!isPrivate)
                {
                    attributes &= ~Attributes.ComposedOfAllPrivateMethods;
                }
 
                if (isStatic)
                    attributes &= ~Attributes.ComposedOfNoStaticMembers;
 
                if (!isVirtual)
                    attributes &= ~Attributes.ComposedOfAllVirtualMethods;
                #endregion
 
                if (semantics == MethodSemanticsAttributes.Setter)
                    setter = associateMethod;
                else if (semantics == MethodSemanticsAttributes.Getter)
                    getter = associateMethod;
                else if (semantics == MethodSemanticsAttributes.Fire)
                    fireOn = associateMethod;
                else if (semantics == MethodSemanticsAttributes.AddOn)
                    addOn = associateMethod;
                else if (semantics == MethodSemanticsAttributes.RemoveOn)
                    removeOn = associateMethod;
                else
                {
                    otherList ??= new List<MethodInfo>(cAssociates);
                    otherList.Add(associateMethod);
                }
            }
 
            bool isPseudoPublic = (attributes & Attributes.ComposedOfNoPublicMembers) == 0;
            bool isPseudoStatic = (attributes & Attributes.ComposedOfNoStaticMembers) == 0;
            bindingFlags = RuntimeType.FilterPreCalculate(isPseudoPublic, isInherited, isPseudoStatic);
 
            composedOfAllPrivateMethods = (attributes & Attributes.ComposedOfAllPrivateMethods) != 0;
 
            other = otherList?.ToArray();
        }
    }
}