File: src\System\Reflection\RuntimeMethodInfo.CoreCLR.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;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Security;
using System.Text;
using System.Threading;
using RuntimeTypeCache = System.RuntimeType.RuntimeTypeCache;
 
namespace System.Reflection
{
    internal sealed partial class RuntimeMethodInfo : MethodInfo, IRuntimeMethodInfo
    {
        #region Private Data Members
        private readonly IntPtr m_handle;
        private readonly RuntimeTypeCache m_reflectedTypeCache;
        private string? m_name;
        private string? m_toString;
        private ParameterInfo[]? m_parameters;
        private ParameterInfo? m_returnParameter;
        private readonly BindingFlags m_bindingFlags;
        private readonly MethodAttributes m_methodAttributes;
        private Signature? m_signature;
        private readonly RuntimeType m_declaringType;
        private readonly object? m_keepalive;
        private MethodBaseInvoker? m_invoker;
 
        internal InvocationFlags InvocationFlags
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                InvocationFlags flags = Invoker._invocationFlags;
                Debug.Assert((flags & InvocationFlags.Initialized) == InvocationFlags.Initialized);
                return flags;
            }
        }
 
        private MethodBaseInvoker Invoker
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                m_invoker ??= new MethodBaseInvoker(this);
                return m_invoker;
            }
        }
        #endregion
 
        #region Constructor
        internal RuntimeMethodInfo(
            RuntimeMethodHandleInternal handle, RuntimeType declaringType,
            RuntimeTypeCache reflectedTypeCache, MethodAttributes methodAttributes, BindingFlags bindingFlags, object? keepalive)
        {
            Debug.Assert(!handle.IsNullHandle());
            Debug.Assert(methodAttributes == RuntimeMethodHandle.GetAttributes(handle));
 
            m_bindingFlags = bindingFlags;
            m_declaringType = declaringType;
            m_keepalive = keepalive;
            m_handle = handle.Value;
            m_reflectedTypeCache = reflectedTypeCache;
            m_methodAttributes = methodAttributes;
        }
        #endregion
 
        #region Private Methods
        RuntimeMethodHandleInternal IRuntimeMethodInfo.Value => new RuntimeMethodHandleInternal(m_handle);
 
        private RuntimeType ReflectedTypeInternal => m_reflectedTypeCache.GetRuntimeType();
 
        private ParameterInfo[] FetchNonReturnParameters() =>
            m_parameters ??= RuntimeParameterInfo.GetParameters(this, this, Signature);
 
        private ParameterInfo FetchReturnParameter() =>
            m_returnParameter ??= RuntimeParameterInfo.GetReturnParameter(this, this, Signature);
        #endregion
 
        #region Internal Members
        internal override bool CacheEquals(object? o) =>
            o is RuntimeMethodInfo m && m.m_handle == m_handle;
 
        internal Signature Signature
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                [MethodImpl(MethodImplOptions.NoInlining)] // move lazy sig generation out of the hot path
                Signature LazyCreateSignature()
                {
                    Signature newSig = new Signature(this, m_declaringType);
                    Volatile.Write(ref m_signature, newSig);
                    return newSig;
                }
 
                return m_signature ?? LazyCreateSignature();
            }
        }
 
        internal BindingFlags BindingFlags => m_bindingFlags;
 
        internal RuntimeMethodInfo? GetParentDefinition()
        {
            if (!IsVirtual || m_declaringType.IsInterface)
                return null;
 
            RuntimeType? parent = (RuntimeType?)m_declaringType.BaseType;
 
            if (parent == null)
                return null;
 
            int slot = RuntimeMethodHandle.GetSlot(this);
 
            if (RuntimeTypeHandle.GetNumVirtuals(parent) <= slot)
                return null;
 
            return (RuntimeMethodInfo?)RuntimeType.GetMethodBase(parent, RuntimeTypeHandle.GetMethodAt(parent, slot));
        }
 
        // Unlike DeclaringType, this will return a valid type even for global methods
        internal RuntimeType GetDeclaringTypeInternal()
        {
            return m_declaringType;
        }
 
        internal sealed override int GenericParameterCount => RuntimeMethodHandle.GetGenericParameterCount(this);
        #endregion
 
        #region Object Overrides
        public override string ToString()
        {
            if (m_toString == null)
            {
                var sbName = new ValueStringBuilder(MethodNameBufferSize);
 
                sbName.Append(ReturnType.FormatTypeName());
                sbName.Append(' ');
                sbName.Append(Name);
 
                if (IsGenericMethod)
                    sbName.Append(RuntimeMethodHandle.ConstructInstantiation(this, TypeNameFormatFlags.FormatBasic));
 
                sbName.Append('(');
                AppendParameters(ref sbName, GetParameterTypes(), CallingConvention);
                sbName.Append(')');
 
                m_toString = sbName.ToString();
            }
 
            return m_toString;
        }
 
        // We cannot do simple object identity comparisons due to generic methods.
        // Equals and GetHashCode will be called in CerHashTable when RuntimeType+RuntimeTypeCache.GetGenericMethodInfo()
        // retrieve items from and insert items into s_methodInstantiations.
 
        public override int GetHashCode() =>
            HashCode.Combine(m_handle.GetHashCode(), m_declaringType.GetUnderlyingNativeHandle().GetHashCode());
 
        public override bool Equals(object? obj) =>
            obj is RuntimeMethodInfo m && m_handle == m.m_handle &&
            ReferenceEquals(m_declaringType, m.m_declaringType) &&
            ReferenceEquals(m_reflectedTypeCache.GetRuntimeType(), m.m_reflectedTypeCache.GetRuntimeType());
 
        #endregion
 
        #region ICustomAttributeProvider
        public override object[] GetCustomAttributes(bool inherit)
        {
            return CustomAttribute.GetCustomAttributes(this, (typeof(object) as RuntimeType)!, inherit);
        }
 
        public override object[] GetCustomAttributes(Type attributeType, bool inherit)
        {
            ArgumentNullException.ThrowIfNull(attributeType);
 
            if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType)
                throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType));
 
            return CustomAttribute.GetCustomAttributes(this, attributeRuntimeType, inherit);
        }
 
        public override bool IsDefined(Type attributeType, bool inherit)
        {
            ArgumentNullException.ThrowIfNull(attributeType);
 
            if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType)
                throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType));
 
            return CustomAttribute.IsDefined(this, attributeRuntimeType, inherit);
        }
 
        public override IList<CustomAttributeData> GetCustomAttributesData()
        {
            return RuntimeCustomAttributeData.GetCustomAttributesInternal(this);
        }
        #endregion
 
        #region MemberInfo Overrides
        public override string Name => m_name ??= RuntimeMethodHandle.GetName(this);
 
        public override Type? DeclaringType
        {
            get
            {
                if (m_reflectedTypeCache.IsGlobal)
                    return null;
 
                return m_declaringType;
            }
        }
 
        public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore<RuntimeMethodInfo>(other);
 
        public override Type? ReflectedType
        {
            get
            {
                if (m_reflectedTypeCache.IsGlobal)
                    return null;
 
                return m_reflectedTypeCache.GetRuntimeType();
            }
        }
 
        public override MemberTypes MemberType => MemberTypes.Method;
        public override int MetadataToken => RuntimeMethodHandle.GetMethodDef(this);
        public override Module Module => GetRuntimeModule();
        internal RuntimeType GetRuntimeType() { return m_declaringType; }
        internal RuntimeModule GetRuntimeModule() { return m_declaringType.GetRuntimeModule(); }
        internal RuntimeAssembly GetRuntimeAssembly() { return GetRuntimeModule().GetRuntimeAssembly(); }
 
        public override bool IsSecurityCritical => true;
        public override bool IsSecuritySafeCritical => false;
        public override bool IsSecurityTransparent => false;
        #endregion
 
        #region MethodBase Overrides
        internal override ReadOnlySpan<ParameterInfo> GetParametersAsSpan() =>
            FetchNonReturnParameters();
 
        public override ParameterInfo[] GetParameters()
        {
            ParameterInfo[] parameters = FetchNonReturnParameters();
 
            if (parameters.Length == 0)
                return parameters;
 
            ParameterInfo[] ret = new ParameterInfo[parameters.Length];
 
            Array.Copy(parameters, ret, parameters.Length);
 
            return ret;
        }
 
        public override MethodImplAttributes GetMethodImplementationFlags()
        {
            return RuntimeMethodHandle.GetImplAttributes(this);
        }
 
        public override RuntimeMethodHandle MethodHandle => new RuntimeMethodHandle(this);
 
        public override MethodAttributes Attributes => m_methodAttributes;
 
        public override CallingConventions CallingConvention => Signature.CallingConvention;
 
        internal RuntimeType[] ArgumentTypes => Signature.Arguments;
 
        [RequiresUnreferencedCode("Trimming may change method bodies. For example it can change some instructions, remove branches or local variables.")]
        public override MethodBody? GetMethodBody()
        {
            RuntimeMethodBody? mb = RuntimeMethodHandle.GetMethodBody(this, ReflectedTypeInternal);
            if (mb != null)
                mb._methodBase = this;
            return mb;
        }
 
        #endregion
 
        #region Invocation Logic
        [DebuggerStepThrough]
        [DebuggerHidden]
        internal void InvokePropertySetter(object? obj, BindingFlags invokeAttr, Binder? binder, object? parameter, CultureInfo? culture)
        {
            // ContainsStackPointers means that the struct (either the declaring type or the return type)
            // contains pointers that point to the stack. This is either a ByRef or a TypedReference. These structs cannot
            // be boxed and thus cannot be invoked through reflection which only deals with boxed value type objects.
            if ((InvocationFlags & (InvocationFlags.NoInvoke | InvocationFlags.ContainsStackPointers)) != 0)
            {
                ThrowNoInvokeException();
            }
 
            // check basic method consistency. This call will throw if there are problems in the target/method relationship
            if (!IsStatic)
            {
                MethodInvokerCommon.ValidateInvokeTarget(obj, this);
            }
 
            Signature sig = Signature;
            if (sig.Arguments.Length != 1)
            {
                throw new TargetParameterCountException(SR.Arg_ParmCnt);
            }
 
            Invoker.InvokePropertySetter(obj, invokeAttr, binder, parameter, culture);
        }
 
        #endregion
 
        #region MethodInfo Overrides
 
        public override Type ReturnType => Signature.ReturnType;
 
        public override ICustomAttributeProvider ReturnTypeCustomAttributes => ReturnParameter;
 
        public override ParameterInfo ReturnParameter => FetchReturnParameter();
 
        public override bool IsCollectible => RuntimeMethodHandle.GetIsCollectible(new RuntimeMethodHandleInternal(m_handle)) != Interop.BOOL.FALSE;
 
        public override MethodInfo GetBaseDefinition()
        {
            if (!IsVirtual || IsStatic || m_declaringType == null || m_declaringType.IsInterface)
                return this;
 
            int slot = RuntimeMethodHandle.GetSlot(this);
            RuntimeType declaringType = (RuntimeType)DeclaringType!;
            RuntimeType? baseDeclaringType = declaringType;
            RuntimeMethodHandleInternal baseMethodHandle = default;
 
            do
            {
                int cVtblSlots = RuntimeTypeHandle.GetNumVirtuals(declaringType);
 
                if (cVtblSlots <= slot)
                    break;
 
                baseMethodHandle = RuntimeTypeHandle.GetMethodAt(declaringType, slot);
                baseDeclaringType = declaringType;
 
                declaringType = (RuntimeType)declaringType.BaseType!;
            } while (declaringType != null);
 
            return (MethodInfo)RuntimeType.GetMethodBase(baseDeclaringType, baseMethodHandle)!;
        }
 
        public override Delegate CreateDelegate(Type delegateType)
        {
            // This API existed in v1/v1.1 and only expected to create closed
            // instance delegates. Constrain the call to BindToMethodInfo to
            // open delegates only for backwards compatibility. But we'll allow
            // relaxed signature checking and open static delegates because
            // there's no ambiguity there (the caller would have to explicitly
            // pass us a static method or a method with a non-exact signature
            // and the only change in behavior from v1.1 there is that we won't
            // fail the call).
            return CreateDelegateInternal(
                delegateType,
                null,
                DelegateBindingFlags.OpenDelegateOnly | DelegateBindingFlags.RelaxedSignature);
        }
 
        public override Delegate CreateDelegate(Type delegateType, object? target)
        {
            // This API is new in Whidbey and allows the full range of delegate
            // flexability (open or closed delegates binding to static or
            // instance methods with relaxed signature checking). The delegate
            // can also be closed over null. There's no ambiguity with all these
            // options since the caller is providing us a specific MethodInfo.
            return CreateDelegateInternal(
                delegateType,
                target,
                DelegateBindingFlags.RelaxedSignature);
        }
 
        private Delegate CreateDelegateInternal(Type delegateType, object? firstArgument, DelegateBindingFlags bindingFlags)
        {
            ArgumentNullException.ThrowIfNull(delegateType);
 
            RuntimeType rtType = delegateType as RuntimeType ??
                throw new ArgumentException(SR.Argument_MustBeRuntimeType, nameof(delegateType));
 
            if (!rtType.IsDelegate())
                throw new ArgumentException(SR.Arg_MustBeDelegate, nameof(delegateType));
 
            return Delegate.CreateDelegateInternal(rtType, this, firstArgument, bindingFlags) ??
                throw new ArgumentException(SR.Arg_DlgtTargMeth);
        }
 
        #endregion
 
        #region Generics
        [RequiresUnreferencedCode("If some of the generic arguments are annotated (either with DynamicallyAccessedMembersAttribute, or generic constraints), trimming can't validate that the requirements of those annotations are met.")]
        public override MethodInfo MakeGenericMethod(params Type[] methodInstantiation)
        {
            ArgumentNullException.ThrowIfNull(methodInstantiation);
 
            RuntimeType[] methodInstantionRuntimeType = new RuntimeType[methodInstantiation.Length];
 
            if (!IsGenericMethodDefinition)
                throw new InvalidOperationException(
                    SR.Format(SR.Arg_NotGenericMethodDefinition, this));
 
            for (int i = 0; i < methodInstantiation.Length; i++)
            {
                Type methodInstantiationElem = methodInstantiation[i];
                ArgumentNullException.ThrowIfNull(methodInstantiationElem, null);
 
                RuntimeType? rtMethodInstantiationElem = methodInstantiationElem as RuntimeType;
 
                if (rtMethodInstantiationElem == null)
                {
                    Type[] methodInstantiationCopy = new Type[methodInstantiation.Length];
                    for (int iCopy = 0; iCopy < methodInstantiation.Length; iCopy++)
                        methodInstantiationCopy[iCopy] = methodInstantiation[iCopy];
                    methodInstantiation = methodInstantiationCopy;
                    return Emit.MethodBuilderInstantiation.MakeGenericMethod(this, methodInstantiation);
                }
 
                methodInstantionRuntimeType[i] = rtMethodInstantiationElem;
            }
 
            RuntimeType[] genericParameters = GetGenericArgumentsInternal();
 
            RuntimeType.SanityCheckGenericArguments(methodInstantionRuntimeType, genericParameters);
 
            MethodInfo? ret;
 
            try
            {
                ret = RuntimeType.GetMethodBase(ReflectedTypeInternal,
                    RuntimeMethodHandle.GetStubIfNeeded(new RuntimeMethodHandleInternal(m_handle), m_declaringType, methodInstantionRuntimeType)) as MethodInfo;
            }
            catch (VerificationException e)
            {
                RuntimeType.ValidateGenericArguments(this, methodInstantionRuntimeType, e);
                throw;
            }
 
            return ret!;
        }
 
        internal RuntimeType[] GetGenericArgumentsInternal() =>
            RuntimeMethodHandle.GetMethodInstantiationInternal(this);
 
        public override Type[] GetGenericArguments() =>
            RuntimeMethodHandle.GetMethodInstantiationPublic(this) ?? Type.EmptyTypes;
 
        public override MethodInfo GetGenericMethodDefinition()
        {
            if (!IsGenericMethod)
                throw new InvalidOperationException();
 
            return (RuntimeType.GetMethodBase(m_declaringType, RuntimeMethodHandle.StripMethodInstantiation(this)) as MethodInfo)!;
        }
 
        public override bool IsGenericMethod => RuntimeMethodHandle.HasMethodInstantiation(this);
 
        public override bool IsGenericMethodDefinition => RuntimeMethodHandle.IsGenericMethodDefinition(this);
 
        public override bool ContainsGenericParameters
        {
            get
            {
                if (DeclaringType != null && DeclaringType.ContainsGenericParameters)
                    return true;
 
                if (!IsGenericMethod)
                    return false;
 
                Type[] pis = GetGenericArguments();
                for (int i = 0; i < pis.Length; i++)
                {
                    if (pis[i].ContainsGenericParameters)
                        return true;
                }
 
                return false;
            }
        }
        #endregion
    }
}