File: src\libraries\System.Private.CoreLib\src\System\Reflection\MethodInvokerCommon.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.Diagnostics;
using System.Runtime.CompilerServices;
using static System.Reflection.InvokerEmitUtil;
using static System.Reflection.MethodBase;
 
namespace System.Reflection
{
    internal static class MethodInvokerCommon
    {
        internal static void Initialize(
            RuntimeType[] argumentTypes,
            out InvokerStrategy strategy,
            out InvokerArgFlags[] invokerFlags,
            out bool needsByRefStrategy)
        {
            if (LocalAppContextSwitches.ForceInterpretedInvoke && !LocalAppContextSwitches.ForceEmitInvoke)
            {
                // Always use the native interpreted invoke.
                // Useful for testing, to avoid startup overhead of emit, or for calling a ctor on already initialized object.
                strategy = GetStrategyForUsingInterpreted();
            }
            else if (LocalAppContextSwitches.ForceEmitInvoke && !LocalAppContextSwitches.ForceInterpretedInvoke)
            {
                // Always use emit invoke (if IsDynamicCodeSupported == true); useful for testing.
                strategy = GetStrategyForUsingEmit();
            }
            else
            {
                strategy = default;
            }
 
            int argCount = argumentTypes.Length;
            invokerFlags = new InvokerArgFlags[argCount];
            needsByRefStrategy = false;
 
            for (int i = 0; i < argCount; i++)
            {
                RuntimeType type = argumentTypes[i];
                if (RuntimeTypeHandle.IsByRef(type))
                {
                    type = (RuntimeType)type.GetElementType()!;
                    invokerFlags[i] |= InvokerArgFlags.IsValueType_ByRef_Or_Pointer;
                    needsByRefStrategy = true;
                    if (type.IsNullableOfT)
                    {
                        invokerFlags[i] |= InvokerArgFlags.IsNullableOfT;
                    }
                }
 
                if (RuntimeTypeHandle.IsPointer(type))
                {
                    invokerFlags[i] |= InvokerArgFlags.IsValueType | InvokerArgFlags.IsValueType_ByRef_Or_Pointer;
                }
                else if (RuntimeTypeHandle.IsFunctionPointer(type))
                {
                    invokerFlags[i] |= InvokerArgFlags.IsValueType;
                }
                else if (type.IsActualValueType)
                {
                    invokerFlags[i] |= InvokerArgFlags.IsValueType | InvokerArgFlags.IsValueType_ByRef_Or_Pointer;
 
                    if (type.IsNullableOfT)
                    {
                        invokerFlags[i] |= InvokerArgFlags.IsNullableOfT;
                    }
                }
            }
        }
 
        internal static InvokerStrategy GetStrategyForUsingInterpreted()
        {
            // This causes the default strategy, which is interpreted, to always be used.
            return InvokerStrategy.StrategyDetermined_Obj4Args | InvokerStrategy.StrategyDetermined_ObjSpanArgs | InvokerStrategy.StrategyDetermined_RefArgs;
        }
 
        private static InvokerStrategy GetStrategyForUsingEmit()
        {
            // This causes the emit strategy, if supported, to be used on the first call as well as subsequent calls.
            return InvokerStrategy.HasBeenInvoked_Obj4Args | InvokerStrategy.HasBeenInvoked_ObjSpanArgs | InvokerStrategy.HasBeenInvoked_RefArgs;
        }
 
        /// <summary>
        /// Confirm member invocation has an instance and is of the correct type
        /// </summary>
        internal static void ValidateInvokeTarget(object? target, MethodBase method)
        {
            Debug.Assert(!method.IsStatic);
 
            if (target == null)
            {
                throw new TargetException(SR.RFLCT_Targ_StatMethReqTarg);
            }
 
            if (!method.DeclaringType!.IsInstanceOfType(target))
            {
                throw new TargetException(SR.Format(SR.RFLCT_Targ_ITargMismatch_WithType, method.DeclaringType, target.GetType()));
            }
        }
 
        internal static void DetermineStrategy_ObjSpanArgs(
            ref InvokerStrategy strategy,
            ref InvokeFunc_ObjSpanArgs?
            invokeFunc_ObjSpanArgs,
            MethodBase method,
            bool needsByRefStrategy,
            bool backwardsCompat)
        {
            if (needsByRefStrategy)
            {
                // If ByRefs are used, we can't use this strategy.
                strategy |= InvokerStrategy.StrategyDetermined_ObjSpanArgs;
            }
            else if (((strategy & InvokerStrategy.HasBeenInvoked_ObjSpanArgs) == 0) && !Debugger.IsAttached)
            {
                // The first time, ignoring race conditions, use the slow path, except for the case when running under a debugger.
                // This is a workaround for the debugger issues with understanding exceptions propagation over the slow path.
                strategy |= InvokerStrategy.HasBeenInvoked_ObjSpanArgs;
            }
            else
            {
                if (RuntimeFeature.IsDynamicCodeSupported)
                {
                    invokeFunc_ObjSpanArgs = CreateInvokeDelegate_ObjSpanArgs(method, backwardsCompat);
                }
 
                strategy |= InvokerStrategy.StrategyDetermined_ObjSpanArgs;
            }
        }
 
        internal static void DetermineStrategy_Obj4Args(
            ref InvokerStrategy strategy,
            ref InvokeFunc_Obj4Args? invokeFunc_Obj4Args,
            MethodBase method,
            bool needsByRefStrategy,
            bool backwardsCompat)
        {
            if (needsByRefStrategy)
            {
                // If ByRefs are used, we can't use this strategy.
                strategy |= InvokerStrategy.StrategyDetermined_Obj4Args;
            }
            else if (((strategy & InvokerStrategy.HasBeenInvoked_Obj4Args) == 0) && !Debugger.IsAttached)
            {
                // The first time, ignoring race conditions, use the slow path, except for the case when running under a debugger.
                // This is a workaround for the debugger issues with understanding exceptions propagation over the slow path.
                strategy |= InvokerStrategy.HasBeenInvoked_Obj4Args;
            }
            else
            {
                if (RuntimeFeature.IsDynamicCodeSupported)
                {
                    invokeFunc_Obj4Args = CreateInvokeDelegate_Obj4Args(method, backwardsCompat);
                }
 
                strategy |= InvokerStrategy.StrategyDetermined_Obj4Args;
            }
        }
 
        internal static void DetermineStrategy_RefArgs(
            ref InvokerStrategy strategy,
            ref InvokeFunc_RefArgs? invokeFunc_RefArgs,
            MethodBase method,
            bool backwardsCompat)
        {
            if (((strategy & InvokerStrategy.HasBeenInvoked_RefArgs) == 0) && !Debugger.IsAttached)
            {
                // The first time, ignoring race conditions, use the slow path, except for the case when running under a debugger.
                // This is a workaround for the debugger issues with understanding exceptions propagation over the slow path.
                strategy |= InvokerStrategy.HasBeenInvoked_RefArgs;
            }
            else
            {
                if (RuntimeFeature.IsDynamicCodeSupported)
                {
                    invokeFunc_RefArgs = CreateInvokeDelegate_RefArgs(method, backwardsCompat);
                }
 
                strategy |= InvokerStrategy.StrategyDetermined_RefArgs;
            }
        }
    }
}