diff --git a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp index 96cdf607b9fe6a..43350c0932d279 100644 --- a/src/coreclr/nativeaot/Runtime/GCHelpers.cpp +++ b/src/coreclr/nativeaot/Runtime/GCHelpers.cpp @@ -352,3 +352,23 @@ COOP_PINVOKE_HELPER(int64_t, RhGetTotalPauseDuration, ()) { return GCHeapUtilities::GetGCHeap()->GetTotalPauseDuration(); } + +COOP_PINVOKE_HELPER(void, RhRegisterForGCReporting, (GCFrameRegistration* pRegistration)) +{ + Thread* pThread = ThreadStore::GetCurrentThread(); + + ASSERT(pRegistration->m_pThread == NULL); + pRegistration->m_pThread = pThread; + + pThread->PushGCFrameRegistration(pRegistration); +} + +COOP_PINVOKE_HELPER(void, RhUnregisterForGCReporting, (GCFrameRegistration* pRegistration)) +{ + Thread* pThread = pRegistration->m_pThread; + if (pThread == NULL) + return; + + ASSERT(pThread == ThreadStore::GetCurrentThread()); + pThread->PopGCFrameRegistration(pRegistration); +} diff --git a/src/coreclr/nativeaot/Runtime/thread.cpp b/src/coreclr/nativeaot/Runtime/thread.cpp index 9256517277b2c6..be42de3d0463aa 100644 --- a/src/coreclr/nativeaot/Runtime/thread.cpp +++ b/src/coreclr/nativeaot/Runtime/thread.cpp @@ -251,9 +251,6 @@ void Thread::Construct() (offsetof(Thread, m_pTransitionFrame))); #endif // USE_PORTABLE_HELPERS - m_pThreadLocalModuleStatics = NULL; - m_numThreadLocalModuleStatics = 0; - // NOTE: We do not explicitly defer to the GC implementation to initialize the alloc_context. The // alloc_context will be initialized to 0 via the static initialization of tls_CurrentThread. If the // alloc_context ever needs different initialization, a matching change to the tls_CurrentThread @@ -281,12 +278,19 @@ void Thread::Construct() m_pThreadStressLog = StressLog::CreateThreadStressLog(this); #endif // STRESS_LOG - m_threadAbortException = NULL; + // Everything else should be initialized to 0 via the static initialization of tls_CurrentThread. + + ASSERT(m_pThreadLocalModuleStatics == NULL); + ASSERT(m_numThreadLocalModuleStatics == 0); + + ASSERT(m_pGCFrameRegistrations == NULL); + + ASSERT(m_threadAbortException == NULL); #ifdef FEATURE_SUSPEND_REDIRECTION - m_redirectionContextBuffer = NULL; + ASSERT(m_redirectionContextBuffer == NULL); #endif //FEATURE_SUSPEND_REDIRECTION - m_interruptedContext = NULL; + ASSERT(m_interruptedContext == NULL); } bool Thread::IsInitialized() @@ -370,6 +374,8 @@ void Thread::Destroy() delete[] m_redirectionContextBuffer; } #endif //FEATURE_SUSPEND_REDIRECTION + + ASSERT(m_pGCFrameRegistrations == NULL); } #ifdef HOST_WASM @@ -536,6 +542,17 @@ void Thread::GcScanRootsWorker(void * pfnEnumCallback, void * pvCallbackData, St RedhawkGCInterface::EnumGcRef(pExceptionObj, GCRK_Object, pfnEnumCallback, pvCallbackData); } + for (GCFrameRegistration* pCurGCFrame = m_pGCFrameRegistrations; pCurGCFrame != NULL; pCurGCFrame = pCurGCFrame->m_pNext) + { + ASSERT(pCurGCFrame->m_pThread == this); + + for (uint32_t i = 0; i < pCurGCFrame->m_numObjRefs; i++) + { + RedhawkGCInterface::EnumGcRef(dac_cast(pCurGCFrame->m_pObjRefs + i), + pCurGCFrame->m_MaybeInterior ? GCRK_Byref : GCRK_Object, pfnEnumCallback, pvCallbackData); + } + } + // Keep alive the ThreadAbortException that's stored in the target thread during thread abort PTR_RtuObjectRef pThreadAbortExceptionObj = dac_cast(&m_threadAbortException); RedhawkGCInterface::EnumGcRef(pThreadAbortExceptionObj, GCRK_Object, pfnEnumCallback, pvCallbackData); diff --git a/src/coreclr/nativeaot/Runtime/thread.h b/src/coreclr/nativeaot/Runtime/thread.h index a09ddd0ae02c0a..2c0881acf2fd16 100644 --- a/src/coreclr/nativeaot/Runtime/thread.h +++ b/src/coreclr/nativeaot/Runtime/thread.h @@ -66,6 +66,15 @@ struct ExInfo volatile void* m_notifyDebuggerSP; }; +struct GCFrameRegistration +{ + Thread* m_pThread; + GCFrameRegistration* m_pNext; + void** m_pObjRefs; + uint32_t m_numObjRefs; + int m_MaybeInterior; +}; + struct ThreadBuffer { uint8_t m_rgbAllocContextBuffer[SIZEOF_ALLOC_CONTEXT]; @@ -82,6 +91,7 @@ struct ThreadBuffer Object* m_threadAbortException; // ThreadAbortException instance -set only during thread abort PTR_PTR_VOID m_pThreadLocalModuleStatics; uint32_t m_numThreadLocalModuleStatics; + GCFrameRegistration* m_pGCFrameRegistrations; PTR_VOID m_pStackLow; PTR_VOID m_pStackHigh; PTR_UInt8 m_pTEB; // Pointer to OS TEB structure for this thread @@ -274,6 +284,9 @@ class Thread : private ThreadBuffer NATIVE_CONTEXT* GetInterruptedContext(); + void PushGCFrameRegistration(GCFrameRegistration* pRegistration); + void PopGCFrameRegistration(GCFrameRegistration* pRegistration); + #ifdef FEATURE_SUSPEND_REDIRECTION NATIVE_CONTEXT* EnsureRedirectionContext(); #endif //FEATURE_SUSPEND_REDIRECTION diff --git a/src/coreclr/nativeaot/Runtime/thread.inl b/src/coreclr/nativeaot/Runtime/thread.inl index 9c5ebe8343461b..65168a04f4cc63 100644 --- a/src/coreclr/nativeaot/Runtime/thread.inl +++ b/src/coreclr/nativeaot/Runtime/thread.inl @@ -41,3 +41,15 @@ inline PTR_VOID Thread::GetThreadStressLog() const { return m_pThreadStressLog; } + +inline void Thread::PushGCFrameRegistration(GCFrameRegistration* pRegistration) +{ + pRegistration->m_pNext = m_pGCFrameRegistrations; + m_pGCFrameRegistrations = pRegistration; +} + +inline void Thread::PopGCFrameRegistration(GCFrameRegistration* pRegistration) +{ + ASSERT(m_pGCFrameRegistrations == pRegistration); + m_pGCFrameRegistrations = pRegistration->m_pNext; +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs index ff03a2cfef0548..1ad76958f36489 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Augments/ReflectionAugments.cs @@ -167,6 +167,6 @@ public abstract object ActivatorCreateInstance( public abstract EnumInfo GetEnumInfo(Type type); - public abstract DelegateDynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type); + public abstract DynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs index 7942977ae12179..750a56ea13d562 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Reflection/Core/Execution/ExecutionEnvironment.cs @@ -98,7 +98,7 @@ public abstract class ExecutionEnvironment //============================================================================================== public abstract FieldAccessor CreateLiteralFieldAccessor(object value, RuntimeTypeHandle fieldTypeHandle); public abstract EnumInfo GetEnumInfo(RuntimeTypeHandle typeHandle); - public abstract IntPtr GetDynamicInvokeThunk(MethodInvoker invoker, out IntPtr genericDictionary); + public abstract IntPtr GetDynamicInvokeThunk(MethodInvoker invoker); //============================================================================================== // Non-public methods diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs index 6aaee122da8f0e..c6564dba60bcef 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/Augments/RuntimeAugments.cs @@ -364,24 +364,18 @@ public static int GetHighestStaticThreadStaticIndex(TypeManagerHandle typeManage public static object? CallDynamicInvokeMethod( object? thisPtr, IntPtr methodToCall, - IntPtr dynamicInvokeHelperMethod, - IntPtr dynamicInvokeHelperGenericDictionary, - MethodBase targetMethod, + DynamicInvokeInfo dynamicInvokeInfo, object?[]? parameters, BinderBundle binderBundle, - bool wrapInTargetInvocationException, - bool methodToCallIsThisCall) + bool wrapInTargetInvocationException) { object? result = InvokeUtils.CallDynamicInvokeMethod( thisPtr, methodToCall, - dynamicInvokeHelperMethod, - dynamicInvokeHelperGenericDictionary, - targetMethod, + dynamicInvokeInfo, parameters, binderBundle, - wrapInTargetInvocationException, - methodToCallIsThisCall); + wrapInTargetInvocationException); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; } @@ -712,7 +706,7 @@ public static bool CanPrimitiveWiden(RuntimeTypeHandle srcType, RuntimeTypeHandl public static object CheckArgument(object srcObject, RuntimeTypeHandle dstType, BinderBundle binderBundle) { - return InvokeUtils.CheckArgument(srcObject, dstType, binderBundle); + return InvokeUtils.CheckArgument(srcObject, dstType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, binderBundle); } // FieldInfo.SetValueDirect() has a completely different set of rules on how to coerce the argument from diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs index 3733e3c2f0bc35..a25590771a03b6 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/CompilerHelpers/ThrowHelpers.cs @@ -127,10 +127,5 @@ public static void ThrowArgumentOutOfRangeException() { throw new ArgumentOutOfRangeException(); } - - public static void ThrowInvokeNullRefReturned() - { - throw new NullReferenceException(SR.NullReference_InvokeNullRefReturned); - } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 3a5a4df522a942..3734a915120c9f 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -136,7 +136,7 @@ - + diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs index 9250223f57aaf0..6e2b078a371d08 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Delegate.cs @@ -276,11 +276,10 @@ internal bool IsDynamicDelegate() } else { - DelegateDynamicInvokeInfo invokeInfo = ReflectionAugments.ReflectionCoreCallbacks.GetDelegateDynamicInvokeInfo(GetType()); + DynamicInvokeInfo dynamicInvokeInfo = ReflectionAugments.ReflectionCoreCallbacks.GetDelegateDynamicInvokeInfo(GetType()); object? result = InvokeUtils.CallDynamicInvokeMethod(m_firstParameter, m_functionPointer, - invokeInfo.InvokeThunk, invokeInfo.GenericDictionary, invokeInfo.InvokeMethod, - args, binderBundle: null, wrapInTargetInvocationException: true); + dynamicInvokeInfo, args, binderBundle: null, wrapInTargetInvocationException: true); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs index 325b286e5d8e68..3e7d78f8dea3bd 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/InvokeUtils.cs @@ -1,23 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Runtime; +using System.Diagnostics; using System.Reflection; +using System.Runtime; using System.Runtime.CompilerServices; -using System.Diagnostics; - -using Internal.Reflection.Core.NonPortable; -using Internal.Runtime.Augments; -using Internal.Runtime.CompilerServices; - -using EETypeElementType = Internal.Runtime.EETypeElementType; -using Interlocked = System.Threading.Interlocked; +using System.Runtime.InteropServices; namespace System { - [System.Runtime.CompilerServices.ReflectionBlocked] - public static class InvokeUtils + internal static class InvokeUtils { // // Various reflection scenarios (Array.SetValue(), reflection Invoke, delegate DynamicInvoke and FieldInfo.Set()) perform @@ -37,14 +29,6 @@ public static class InvokeUtils // There is also another transform of T -> Nullable. This method acknowledges that rule but does not actually transform the T. // Rather, the transformation happens naturally when the caller unboxes the value to its final destination. // - // This method is targeted by the Delegate ILTransformer. - // - // - public static object? CheckArgument(object? srcObject, RuntimeTypeHandle dstType, BinderBundle? binderBundle) - { - EETypePtr dstEEType = dstType.ToEETypePtr(); - return CheckArgument(srcObject, dstEEType, CheckArgumentSemantics.DynamicInvoke, binderBundle, ref Unsafe.NullRef()); - } // This option tweaks the coercion rules to match classic inconsistencies. internal enum CheckArgumentSemantics @@ -55,11 +39,6 @@ internal enum CheckArgumentSemantics } internal static object? CheckArgument(object? srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, BinderBundle? binderBundle) - { - return CheckArgument(srcObject, dstEEType, semantics, binderBundle, ref Unsafe.NullRef()); - } - - internal static object? CheckArgument(object? srcObject, EETypePtr dstEEType, CheckArgumentSemantics semantics, BinderBundle? binderBundle, ref ArgSetupState argSetupState) { // Methods with ByRefLike types in signatures should be filtered out by the compiler Debug.Assert(!dstEEType.IsByRefLike); @@ -105,23 +84,12 @@ internal enum CheckArgumentSemantics throw exception; // Our normal coercion rules could not convert the passed in argument but we were supplied a custom binder. See if it can do it. - Type exactDstType; - if (Unsafe.IsNullRef(ref argSetupState)) - { - // We were called by someone other than DynamicInvokeParamHelperCore(). Those callers pass the correct dstEEType. - exactDstType = Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType))!; - } - else - { - // We were called by DynamicInvokeParamHelperCore(). He passes a dstEEType that enums folded to int and possibly other adjustments. A custom binder - // is app code however and needs the exact type. - exactDstType = GetExactTypeForCustomBinder(argSetupState); - } + Type exactDstType = Type.GetTypeFromHandle(new RuntimeTypeHandle(dstEEType))!; srcObject = binderBundle.ChangeType(srcObject, exactDstType); // For compat with desktop, the result of the binder call gets processed through the default rules again. - dstObject = CheckArgument(srcObject, dstEEType, semantics, binderBundle: null, ref Unsafe.NullRef()); + dstObject = CheckArgument(srcObject, dstEEType, semantics, binderBundle: null); return dstObject; } } @@ -290,380 +258,354 @@ private static InvalidCastException CreateChangeTypeInvalidCastException(EETypeP return new InvalidCastException(SR.InvalidCast_StoreArrayElement); } - // ----------------------------------------------- - // Infrastructure and logic for Dynamic Invocation - // ----------------------------------------------- - public enum DynamicInvokeParamType - { - In = 0, - Ref = 1 - } - - public enum DynamicInvokeParamLookupType - { - ValuetypeObjectReturned = 0, - IndexIntoObjectArrayReturned = 1, - } - - public struct ArgSetupState - { - public bool fComplete; - public object?[]? parameters; - public object[] nullableCopyBackObjects; - public int curIndex; - public MethodBase targetMethod; - public BinderBundle? binderBundle; - public object?[] customBinderProvidedParameters; - } - - private static object GetDefaultValue(MethodBase targetMethod, int argIndex) - { - ParameterInfo parameterInfo = targetMethod.GetParametersNoCopy()[argIndex]; - if (!parameterInfo.HasDefaultValue) - { - // If the parameter is optional, with no default value and we're asked for its default value, - // it means the caller specified Missing.Value as the value for the parameter. In this case the behavior - // is defined as passing in the Missing.Value, regardless of the parameter type. - // If Missing.Value is convertible to the parameter type, it will just work, otherwise we will fail - // due to type mismatch. - if (!parameterInfo.IsOptional) - throw new ArgumentException(SR.Arg_VarMissNull, "parameters"); - - return Missing.Value; - } - - return parameterInfo.DefaultValue; - } - - // This is only called if we have to invoke a custom binder to coerce a parameter type. It leverages s_targetMethodOrDelegate to retrieve - // the unaltered parameter type to pass to the binder. - private static Type GetExactTypeForCustomBinder(in ArgSetupState argSetupState) - { - // DynamicInvokeParamHelperCore() increments s_curIndex before calling us - that's why we have to subtract 1. - return argSetupState.targetMethod.GetParametersNoCopy()[argSetupState.curIndex - 1].ParameterType; - } - [DebuggerGuidedStepThroughAttribute] - internal static unsafe object CallDynamicInvokeMethod( + internal static unsafe object? CallDynamicInvokeMethod( object? thisPtr, IntPtr methodToCall, - IntPtr dynamicInvokeHelperMethod, - IntPtr dynamicInvokeHelperGenericDictionary, - MethodBase targetMethod, + DynamicInvokeInfo dynamicInvokeInfo, object?[]? parameters, BinderBundle? binderBundle, - bool wrapInTargetInvocationException, - bool methodToCallIsThisCall = true) + bool wrapInTargetInvocationException) { - ArgSetupState argSetupState = new ArgSetupState + int argCount = parameters?.Length ?? 0; + if (argCount != dynamicInvokeInfo.Arguments.Length) { - binderBundle = binderBundle, - targetMethod = targetMethod - }; + throw new TargetParameterCountException(SR.Arg_ParmCnt); + } + object? returnObject = null; + + ref byte thisArg = ref Unsafe.NullRef(); + if (!dynamicInvokeInfo.IsStatic) { - // If the passed in array is not an actual object[] instance, we need to copy it over to an actual object[] - // instance so that the rest of the code can safely create managed object references to individual elements. - if (parameters != null && EETypePtr.EETypePtrOf() != parameters.GetEETypePtr()) - { - argSetupState.parameters = new object[parameters.Length]; - Array.Copy(parameters, argSetupState.parameters, parameters.Length); - } - else - { - argSetupState.parameters = parameters; - } + // The caller is expected to validate this + Debug.Assert(thisPtr != null); + + // thisArg is a raw data byref for valuetype instance methods + thisArg = dynamicInvokeInfo.IsValueTypeInstanceMethod ? ref thisPtr.GetRawData() + : ref Unsafe.As(ref thisPtr); + } - object result; - try + ref byte ret = ref Unsafe.As(ref returnObject); + if ((dynamicInvokeInfo.ReturnTransform & DynamicInvokeTransform.AllocateBox) != 0) + { + returnObject = RuntimeImports.RhNewObject( + (dynamicInvokeInfo.ReturnTransform & DynamicInvokeTransform.Pointer) != 0 ? + EETypePtr.EETypePtrOf() : dynamicInvokeInfo.ReturnType); + ret = ref returnObject.GetRawData(); + } + + if (argCount == 0) + { + if (wrapInTargetInvocationException) { + try { - if (dynamicInvokeHelperGenericDictionary != IntPtr.Zero) - { - result = ((delegate*)dynamicInvokeHelperMethod) - (dynamicInvokeHelperGenericDictionary, thisPtr, methodToCall, ref argSetupState, methodToCallIsThisCall); - DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); - } - else - { - result = ((delegate*)dynamicInvokeHelperMethod) - (thisPtr, methodToCall, ref argSetupState, methodToCallIsThisCall); - DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); - } + ret = ref RawCalliHelper.Call(dynamicInvokeInfo.InvokeThunk, (void*)methodToCall, ref thisArg, ref ret, null); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + catch (Exception e) + { + throw new TargetInvocationException(e); } } - catch (Exception e) when (wrapInTargetInvocationException && argSetupState.fComplete) + else { - throw new TargetInvocationException(e); + ret = ref RawCalliHelper.Call(dynamicInvokeInfo.InvokeThunk, (void*)methodToCall, ref thisArg, ref ret, null); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } - finally + } + else if (argCount > MaxStackAllocArgCount) + { + ret = ref InvokeWithManyArguments(dynamicInvokeInfo, methodToCall, ref thisArg, ref ret, + parameters, binderBundle, wrapInTargetInvocationException); + } + else + { + StackAllocedArguments argStorage = default; + StackAllocatedByRefs byrefStorage = default; + + CheckArguments( + dynamicInvokeInfo, + ref argStorage._arg0!, + (ByReference*)&byrefStorage, + parameters, + binderBundle); + + if (wrapInTargetInvocationException) { - if (argSetupState.parameters != parameters) + try { - Array.Copy(argSetupState.parameters, parameters, parameters.Length); + ret = ref RawCalliHelper.Call(dynamicInvokeInfo.InvokeThunk, (void*)methodToCall, ref thisArg, ref ret, &byrefStorage); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); } - - if (argSetupState.fComplete) + catch (Exception e) { - // Nullable objects can't take advantage of the ability to update the boxed value on the heap directly, so perform - // an update of the parameters array now. - if (argSetupState.nullableCopyBackObjects != null) - { - for (int i = 0; i < argSetupState.nullableCopyBackObjects.Length; i++) - { - if (argSetupState.nullableCopyBackObjects[i] != null) - { - parameters[i] = DynamicInvokeBoxIntoNonNullable(argSetupState.nullableCopyBackObjects[i]); - } - } - } + throw new TargetInvocationException(e); } } + else + { + ret = ref RawCalliHelper.Call(dynamicInvokeInfo.InvokeThunk, (void*)methodToCall, ref thisArg, ref ret, &byrefStorage); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } - return result; - } - } - - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - internal static void DynamicInvokeArgSetupComplete(ref ArgSetupState argSetupState) - { - int parametersLength = argSetupState.parameters != null ? argSetupState.parameters.Length : 0; - - if (argSetupState.curIndex != parametersLength) - { - throw new System.Reflection.TargetParameterCountException(); + if (dynamicInvokeInfo.NeedsCopyBack) + { + CopyBack(dynamicInvokeInfo, (ByReference*)&byrefStorage, parameters); + } } - argSetupState.fComplete = true; - } - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - public static unsafe void DynamicInvokeArgSetupPtrComplete(IntPtr argSetupStatePtr) - { - // argSetupStatePtr is a pointer to a *pinned* ArgSetupState object - DynamicInvokeArgSetupComplete(ref Unsafe.As(ref *(byte*)argSetupStatePtr)); + return ((dynamicInvokeInfo.ReturnTransform & (DynamicInvokeTransform.Nullable | DynamicInvokeTransform.Pointer | DynamicInvokeTransform.ByRef)) != 0) ? + ReturnTranform(dynamicInvokeInfo, ref ret) : returnObject; } - private static void DynamicInvokeUnboxIntoActualNullable(object actualBoxedNullable, object boxedFillObject, EETypePtr nullableType) + private static unsafe ref byte InvokeWithManyArguments( + DynamicInvokeInfo dynamicInvokeInfo, IntPtr methodToCall, ref byte thisArg, ref byte ret, + object?[] parameters, BinderBundle binderBundle, bool wrapInTargetInvocationException) { - // get a byref to the data within the actual boxed nullable, and then call RhUnBox with the boxedFillObject as the boxed object, and nullableType as the unbox type, and unbox into the actualBoxedNullable - RuntimeImports.RhUnbox(boxedFillObject, ref actualBoxedNullable.GetRawData(), nullableType); - } + int argCount = dynamicInvokeInfo.Arguments.Length; - private static object DynamicInvokeBoxIntoNonNullable(object actualBoxedNullable) - { - // grab the pointer to data, box using the MethodTable of the actualBoxedNullable, and then return the boxed object - return RuntimeImports.RhBox(actualBoxedNullable.GetEETypePtr(), ref actualBoxedNullable.GetRawData()); - } + // We don't check a max stack size since we are invoking a method which + // naturally requires a stack size that is dependent on the arg count\size. + IntPtr* pStorage = stackalloc IntPtr[2 * argCount]; + NativeMemory.Clear(pStorage, (nuint)(2 * argCount) * (nuint)sizeof(IntPtr)); - [DebuggerStepThrough] - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - internal static ref IntPtr DynamicInvokeParamHelperIn(ref ArgSetupState argSetupState, RuntimeTypeHandle rth) - { - // - // Call DynamicInvokeParamHelperCore as an in parameter, and return a managed byref to the interesting bit. - // - // This function exactly matches DynamicInvokeParamHelperRef except for the value of the enum passed to DynamicInvokeParamHelperCore - // + ByReference* pByRefStorage = (ByReference*)(pStorage + argCount); - object obj = DynamicInvokeParamHelperCore(ref argSetupState, rth, out DynamicInvokeParamLookupType paramLookupType, out int index, DynamicInvokeParamType.In); + RuntimeImports.GCFrameRegistration regArgStorage = new(pStorage, (uint)argCount, areByRefs: false); + RuntimeImports.GCFrameRegistration regByRefStorage = new(pByRefStorage, (uint)argCount, areByRefs: true); - if (paramLookupType == DynamicInvokeParamLookupType.ValuetypeObjectReturned) + try { - return ref Unsafe.As(ref obj.GetRawData()); - } - else - { - return ref Unsafe.As(ref Unsafe.As(obj)[index]); - } - } - - [DebuggerStepThrough] - [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] - internal static ref IntPtr DynamicInvokeParamHelperRef(ref ArgSetupState argSetupState, RuntimeTypeHandle rth) - { - // - // Call DynamicInvokeParamHelperCore as a ref parameter, and return a managed byref to the interesting bit. As this can't actually be defined in C# there is an IL transform that fills this in. - // - // This function exactly matches DynamicInvokeParamHelperIn except for the value of the enum passed to DynamicInvokeParamHelperCore - // + RuntimeImports.RhRegisterForGCReporting(®ArgStorage); + RuntimeImports.RhRegisterForGCReporting(®ByRefStorage); - object obj = DynamicInvokeParamHelperCore(ref argSetupState, rth, out DynamicInvokeParamLookupType paramLookupType, out int index, DynamicInvokeParamType.Ref); + CheckArguments( + dynamicInvokeInfo, + ref Unsafe.As(ref *pStorage), + (ByReference*)pByRefStorage, + parameters, + binderBundle); - if (paramLookupType == DynamicInvokeParamLookupType.ValuetypeObjectReturned) - { - return ref Unsafe.As(ref obj.GetRawData()); - } - else - { - return ref Unsafe.As(ref Unsafe.As(obj)[index]); - } - } - - internal static object DynamicInvokeBoxedValuetypeReturn(out DynamicInvokeParamLookupType paramLookupType, object? boxedValuetype, object?[] parameters, int index, RuntimeTypeHandle type, DynamicInvokeParamType paramType, ref object[] nullableCopyBackObjects) - { - object? finalObjectToReturn = boxedValuetype; - EETypePtr eeType = type.ToEETypePtr(); - bool nullable = eeType.IsNullable; - - if (finalObjectToReturn == null || nullable || paramType == DynamicInvokeParamType.Ref) - { - finalObjectToReturn = RuntimeImports.RhNewObject(eeType); - if (boxedValuetype != null) + if (wrapInTargetInvocationException) { - DynamicInvokeUnboxIntoActualNullable(finalObjectToReturn, boxedValuetype, eeType); + try + { + ret = ref RawCalliHelper.Call(dynamicInvokeInfo.InvokeThunk, (void*)methodToCall, ref thisArg, ref ret, pByRefStorage); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } + catch (Exception e) + { + throw new TargetInvocationException(e); + } } - } - - if (nullable) - { - if (paramType == DynamicInvokeParamType.Ref) + else { - nullableCopyBackObjects ??= new object[parameters.Length]; + ret = ref RawCalliHelper.Call(dynamicInvokeInfo.InvokeThunk, (void*)methodToCall, ref thisArg, ref ret, pByRefStorage); + DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); + } - nullableCopyBackObjects[index] = finalObjectToReturn; - parameters[index] = null; + if (dynamicInvokeInfo.NeedsCopyBack) + { + CopyBack(dynamicInvokeInfo, pByRefStorage, parameters); } } - else + finally { - System.Diagnostics.Debug.Assert(finalObjectToReturn != null); - if (paramType == DynamicInvokeParamType.Ref) - parameters[index] = finalObjectToReturn; + RuntimeImports.RhUnregisterForGCReporting(®ByRefStorage); + RuntimeImports.RhUnregisterForGCReporting(®ArgStorage); } - paramLookupType = DynamicInvokeParamLookupType.ValuetypeObjectReturned; - return finalObjectToReturn; + return ref ret; } - internal static object DynamicInvokeUnmanagedPointerReturn(out DynamicInvokeParamLookupType paramLookupType, object boxedPointerType, int index, RuntimeTypeHandle type, DynamicInvokeParamType paramType) + private static object? GetCoercedDefaultValue(DynamicInvokeInfo dynamicInvokeInfo, int index, in ArgumentInfo argumentInfo) { - object finalObjectToReturn = boxedPointerType; + object? defaultValue = dynamicInvokeInfo.Method.GetParametersNoCopy()[index].DefaultValue; + if (defaultValue == DBNull.Value) + throw new ArgumentException(SR.Arg_VarMissNull, "parameters"); - Debug.Assert(finalObjectToReturn is IntPtr); - paramLookupType = DynamicInvokeParamLookupType.ValuetypeObjectReturned; - return finalObjectToReturn; - } + if (defaultValue != null && (argumentInfo.Transform & DynamicInvokeTransform.Nullable) != 0) + { + // In case if the parameter is nullable Enum type the ParameterInfo.DefaultValue returns a raw value which + // needs to be parsed to the Enum type, for more info: https://github.com/dotnet/runtime/issues/12924 + EETypePtr nullableType = argumentInfo.Type.NullableType; + if (nullableType.IsEnum) + { + defaultValue = Enum.ToObject(Type.GetTypeFromEETypePtr(nullableType), defaultValue); + } + } - public static unsafe object DynamicInvokeParamHelperCore(IntPtr argSetupState, RuntimeTypeHandle type, out DynamicInvokeParamLookupType paramLookupType, out int index, DynamicInvokeParamType paramType) - { - return DynamicInvokeParamHelperCore(ref Unsafe.AsRef((void*)argSetupState), type, out paramLookupType, out index, paramType); + return defaultValue; } - public static object DynamicInvokeParamHelperCore(ref ArgSetupState argSetupState, RuntimeTypeHandle type, out DynamicInvokeParamLookupType paramLookupType, out int index, DynamicInvokeParamType paramType) + private static unsafe void CheckArguments( + DynamicInvokeInfo dynamicInvokeInfo, + ref object copyOfParameters, + ByReference* byrefParameters, + object?[] parameters, + BinderBundle binderBundle) { - index = argSetupState.curIndex++; - int parametersLength = argSetupState.parameters != null ? argSetupState.parameters.Length : 0; - - if (index >= parametersLength) - throw new System.Reflection.TargetParameterCountException(); + for (int i = 0; i < parameters.Length; i++) + { + object? arg = parameters[i]; - Debug.Assert(argSetupState.parameters != null); - object? incomingParam = argSetupState.parameters[index]; - bool nullable = type.ToEETypePtr().IsNullable; + ref readonly ArgumentInfo argumentInfo = ref dynamicInvokeInfo.Arguments[i]; - // Handle default parameters - if ((incomingParam == System.Reflection.Missing.Value) && paramType == DynamicInvokeParamType.In) - { - incomingParam = GetDefaultValue(argSetupState.targetMethod, index); - if (incomingParam != null && nullable) + Again: + if (arg is null) { - // In case if the parameter is nullable Enum type the ParameterInfo.DefaultValue returns a raw value which - // needs to be parsed to the Enum type, for more info: https://github.com/dotnet/runtime/issues/12924 - EETypePtr nullableType = type.ToEETypePtr().NullableType; - if (nullableType.IsEnum) - { - incomingParam = Enum.ToObject(Type.GetTypeFromEETypePtr(nullableType), incomingParam); - } + // null is substituded by zero-initialized value for non-reference type + if ((argumentInfo.Transform & DynamicInvokeTransform.Reference) == 0) + arg = RuntimeImports.RhNewObject( + (argumentInfo.Transform & DynamicInvokeTransform.Pointer) != 0 ? + EETypePtr.EETypePtrOf() : argumentInfo.Type); } + else + { + // Check for Missing by comparing the type. It will save us from allocating the Missing instance + // unless it is needed. + if (arg.GetType() == typeof(Missing)) + { + // Missing is substited by metadata default value + arg = GetCoercedDefaultValue(dynamicInvokeInfo, i, argumentInfo); - // The default value is captured into the parameters array - argSetupState.parameters[index] = incomingParam; - } + // The metadata default value is written back into the parameters array + parameters[i] = arg; + if (arg is null) + goto Again; // Redo the argument handling to deal with null + } - RuntimeTypeHandle widenAndCompareType = type; - if (nullable) - { - widenAndCompareType = new RuntimeTypeHandle(type.ToEETypePtr().NullableType); - } + EETypePtr srcEEType = arg.GetEETypePtr(); + EETypePtr dstEEType = argumentInfo.Type; - if (widenAndCompareType.ToEETypePtr().IsPrimitive || type.ToEETypePtr().IsEnum) - { - // Nullable requires exact matching - if (incomingParam != null) - { - if (nullable || paramType == DynamicInvokeParamType.Ref) + // Quick check for exact match + if (srcEEType.RawValue != dstEEType.RawValue) { - if (widenAndCompareType.ToEETypePtr() != incomingParam.GetEETypePtr()) + if (!RuntimeImports.AreTypesAssignable(srcEEType, dstEEType)) { - if (argSetupState.binderBundle == null) - throw CreateChangeTypeArgumentException(incomingParam.GetEETypePtr(), type.ToEETypePtr()); - Type exactDstType = GetExactTypeForCustomBinder(argSetupState); - incomingParam = argSetupState.binderBundle.ChangeType(incomingParam, exactDstType); - if (incomingParam != null && widenAndCompareType.ToEETypePtr() != incomingParam.GetEETypePtr()) - throw CreateChangeTypeArgumentException(incomingParam.GetEETypePtr(), type.ToEETypePtr()); + // Slow path that supports type conversions. + arg = CheckArgument(arg, argumentInfo.Type, CheckArgumentSemantics.DynamicInvoke, binderBundle); } - } - else - { - if (widenAndCompareType.ToEETypePtr().ElementType != incomingParam.GetEETypePtr().ElementType) + + if ((argumentInfo.Transform & (DynamicInvokeTransform.ByRef | DynamicInvokeTransform.Nullable)) != 0) { - System.Diagnostics.Debug.Assert(paramType == DynamicInvokeParamType.In); - incomingParam = InvokeUtils.CheckArgument(incomingParam, widenAndCompareType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, argSetupState.binderBundle, ref argSetupState); + // Rebox the value to allow mutating the original box. This also takes care of + // T -> Nullable transformation as side-effect. + object box = Runtime.RuntimeImports.RhNewObject(dstEEType); + RuntimeImports.RhUnbox(arg, ref box.GetRawData(), dstEEType); + arg = box; } } } - return DynamicInvokeBoxedValuetypeReturn(out paramLookupType, incomingParam, argSetupState.parameters, index, type, paramType, ref argSetupState.nullableCopyBackObjects); + // We need to perform type safety validation against the incoming arguments, but we also need + // to be resilient against the possibility that some other thread (or even the binder itself!) + // may mutate the array after we've validated the arguments but before we've properly invoked + // the method. The solution is to copy the arguments to a different, not-user-visible buffer + // as we validate them. This separate array is also used to hold default values when 'null' + // is specified for value types, and also used to hold the results from conversions such as + // from Int16 to Int32. + + Unsafe.Add(ref copyOfParameters, i) = arg!; + + byrefParameters[i] = new ByReference(ref (argumentInfo.Transform & DynamicInvokeTransform.Reference) != 0 ? + ref Unsafe.As(ref Unsafe.Add(ref copyOfParameters, i)) : ref arg.GetRawData()); } - else if (type.ToEETypePtr().IsValueType) + } + + private static unsafe void CopyBack(DynamicInvokeInfo dynamicInvokeInfo, ByReference* byrefParameters, object?[] parameters) + { + ArgumentInfo[] arguments = dynamicInvokeInfo.Arguments; + + for (int i = 0; i < arguments.Length; i++) { - incomingParam = InvokeUtils.CheckArgument(incomingParam, type.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, argSetupState.binderBundle, ref argSetupState); - if (argSetupState.binderBundle == null) + ArgumentInfo argumentInfo = arguments[i]; + DynamicInvokeTransform transform = argumentInfo.Transform; + + if ((transform & DynamicInvokeTransform.ByRef) == 0) + continue; + + ref byte byref = ref byrefParameters[i].Value; + + object obj; + if ((transform & DynamicInvokeTransform.Pointer) != 0) { - System.Diagnostics.Debug.Assert(argSetupState.parameters[index] == null || object.ReferenceEquals(incomingParam, argSetupState.parameters[index])); + Type type = Type.GetTypeFromEETypePtr(argumentInfo.Type); + Debug.Assert(type.IsPointer); + obj = Pointer.Box((void*)Unsafe.As(ref byref), type); } - return DynamicInvokeBoxedValuetypeReturn(out paramLookupType, incomingParam, argSetupState.parameters, index, type, paramType, ref argSetupState.nullableCopyBackObjects); - } - else if (type.ToEETypePtr().IsPointer) - { - incomingParam = InvokeUtils.CheckArgument(incomingParam, type.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, argSetupState.binderBundle, ref argSetupState); - return DynamicInvokeUnmanagedPointerReturn(out paramLookupType, incomingParam, index, type, paramType); - } - else - { - incomingParam = InvokeUtils.CheckArgument(incomingParam, widenAndCompareType.ToEETypePtr(), InvokeUtils.CheckArgumentSemantics.DynamicInvoke, argSetupState.binderBundle, ref argSetupState); - paramLookupType = DynamicInvokeParamLookupType.IndexIntoObjectArrayReturned; - if (argSetupState.binderBundle == null) + else if ((transform & DynamicInvokeTransform.Nullable) != 0) { - System.Diagnostics.Debug.Assert(object.ReferenceEquals(incomingParam, argSetupState.parameters[index])); - return argSetupState.parameters; + obj = RuntimeImports.RhBox(argumentInfo.Type, ref byref); } else { - if (object.ReferenceEquals(incomingParam, argSetupState.parameters[index])) - { - return argSetupState.parameters; - } - else - { - // If we got here, the original argument object was superseded by invoking the custom binder. - - if (paramType == DynamicInvokeParamType.Ref) - { - argSetupState.parameters[index] = incomingParam; - return argSetupState.parameters; - } - else - { - // Since this not a by-ref parameter, we don't want to bash the original user-owned argument array but the rules of DynamicInvokeParamHelperCore() require - // that we return non-value types as the "index"th element in an array. Thus, create an on-demand throwaway array just for this purpose. - argSetupState.customBinderProvidedParameters ??= new object[argSetupState.parameters.Length]; - argSetupState.customBinderProvidedParameters[index] = incomingParam; - return argSetupState.customBinderProvidedParameters; - } - } + // This must be either object reference or we have allocated a value type box earlier + Debug.Assert((transform & (DynamicInvokeTransform.Reference | DynamicInvokeTransform.AllocateBox)) != 0); + obj = Unsafe.As(ref byref); } + parameters[i] = obj; + } + } + + private static unsafe object ReturnTranform(DynamicInvokeInfo dynamicInvokeInfo, ref byte byref) + { + if (Unsafe.IsNullRef(ref byref)) + { + Debug.Assert((dynamicInvokeInfo.ReturnTransform & DynamicInvokeTransform.ByRef) != 0); + throw new NullReferenceException(SR.NullReference_InvokeNullRefReturned); + } + + object obj; + if ((dynamicInvokeInfo.ReturnTransform & DynamicInvokeTransform.Pointer) != 0) + { + Type type = Type.GetTypeFromEETypePtr(dynamicInvokeInfo.ReturnType); + Debug.Assert(type.IsPointer); + obj = Pointer.Box((void*)Unsafe.As(ref byref), type); + } + else if ((dynamicInvokeInfo.ReturnTransform & DynamicInvokeTransform.Reference) != 0) + { + Debug.Assert((dynamicInvokeInfo.ReturnTransform & DynamicInvokeTransform.ByRef) != 0); + obj = Unsafe.As(ref byref); + } + else + { + Debug.Assert((dynamicInvokeInfo.ReturnTransform & (DynamicInvokeTransform.ByRef | DynamicInvokeTransform.Nullable)) != 0); + obj = RuntimeImports.RhBox(dynamicInvokeInfo.ReturnType, ref byref); } + return obj; + } + + private const int MaxStackAllocArgCount = 4; + + // Helper struct to avoid intermediate object[] allocation in calls to the native reflection stack. + // When argument count <= MaxStackAllocArgCount, define a local of type default(StackAllocatedByRefs) + // and pass it to CheckArguments(). + // For argument count > MaxStackAllocArgCount, do a stackalloc of void* pointers along with + // GCReportingRegistration to safely track references. + [StructLayout(LayoutKind.Sequential)] + private ref struct StackAllocedArguments + { + internal object? _arg0; +#pragma warning disable CA1823, CS0169, IDE0051 // accessed via 'CheckArguments' ref arithmetic + private object? _arg1; + private object? _arg2; + private object? _arg3; +#pragma warning restore CA1823, CS0169, IDE0051 + } + + // Helper struct to avoid intermediate IntPtr[] allocation and RegisterForGCReporting in calls to the native reflection stack. + [StructLayout(LayoutKind.Sequential)] + private ref struct StackAllocatedByRefs + { + internal ref byte _arg0; +#pragma warning disable CA1823, CS0169, IDE0051 // accessed via 'CheckArguments' ref arithmetic + private ref byte _arg1; + private ref byte _arg2; + private ref byte _arg3; +#pragma warning restore CA1823, CS0169, IDE0051 } } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DelegateDynamicInvokeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DelegateDynamicInvokeInfo.cs deleted file mode 100644 index 7fd5cadf5cbdfa..00000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DelegateDynamicInvokeInfo.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime; -using System.Runtime.CompilerServices; - -namespace System.Reflection -{ - [ReflectionBlocked] - public sealed class DelegateDynamicInvokeInfo - { - public DelegateDynamicInvokeInfo() - { - } - - internal MethodInfo InvokeMethod { get; init; } - internal IntPtr InvokeThunk { get; init; } - internal IntPtr GenericDictionary { get; init; } - } -} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs new file mode 100644 index 00000000000000..d88482e575fda4 --- /dev/null +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/DynamicInvokeInfo.cs @@ -0,0 +1,139 @@ +// 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; + +namespace System.Reflection +{ + [Flags] + internal enum DynamicInvokeTransform + { + ByRef = 0x0001, + Nullable = 0x0002, + Pointer = 0x0004, + AllocateBox = 0x0008, + Reference = 0x0010, + } + + internal readonly struct ArgumentInfo + { + internal ArgumentInfo(DynamicInvokeTransform transform, EETypePtr type) + { + Transform = transform; + Type = type; + } + + internal DynamicInvokeTransform Transform { get; } + internal EETypePtr Type { get; } + } + + // DynamicInvokeInfo caches information required for efficient argument validation and type coercion for reflection Invoke. + [ReflectionBlocked] + public class DynamicInvokeInfo + { + public DynamicInvokeInfo(MethodBase method, IntPtr invokeThunk) + { + Method = method; + InvokeThunk = invokeThunk; + + IsStatic = method.IsStatic; + + Type? declaringType = method.DeclaringType; + IsValueTypeInstanceMethod = declaringType?.IsValueType ?? false; + + ParameterInfo[] parameters = method.GetParametersNoCopy(); + ArgumentInfo[] arguments = (parameters.Length != 0) ? new ArgumentInfo[parameters.Length] : Array.Empty(); + for (int i = 0; i < parameters.Length; i++) + { + DynamicInvokeTransform transform = default; + + Type argumentType = parameters[i].ParameterType; + if (argumentType.IsByRef) + { + NeedsCopyBack = true; + transform |= DynamicInvokeTransform.ByRef; + argumentType = argumentType.GetElementType()!; + } + Debug.Assert(!argumentType.IsByRef); + + EETypePtr eeArgumentType = argumentType.GetEEType(); + + if (eeArgumentType.IsValueType) + { + Debug.Assert(argumentType.IsValueType); + + if (eeArgumentType.IsNullable) + transform |= DynamicInvokeTransform.Nullable; + } + else if (eeArgumentType.IsPointer) + { + Debug.Assert(argumentType.IsPointer); + + transform |= DynamicInvokeTransform.Pointer; + } + else + { + transform |= DynamicInvokeTransform.Reference; + } + + arguments[i] = new ArgumentInfo(transform, eeArgumentType); + } + Arguments = arguments; + + if (method is MethodInfo methodInfo) + { + DynamicInvokeTransform transform = default; + + Type returnType = methodInfo.ReturnType; + if (returnType.IsByRef) + { + transform |= DynamicInvokeTransform.ByRef; + returnType = returnType.GetElementType()!; + } + Debug.Assert(!returnType.IsByRef); + + EETypePtr eeReturnType = returnType.GetEEType(); + + if (eeReturnType.IsValueType) + { + Debug.Assert(returnType.IsValueType); + + if (returnType != typeof(void)) + { + if ((transform & DynamicInvokeTransform.ByRef) == 0) + transform |= DynamicInvokeTransform.AllocateBox; + + if (eeReturnType.IsNullable) + transform |= DynamicInvokeTransform.Nullable; + } + } + else if (eeReturnType.IsPointer) + { + Debug.Assert(returnType.IsPointer); + + transform |= DynamicInvokeTransform.Pointer; + if ((transform & DynamicInvokeTransform.ByRef) == 0) + transform |= DynamicInvokeTransform.AllocateBox; + } + else + { + transform |= DynamicInvokeTransform.Reference; + } + + ReturnTransform = transform; + ReturnType = eeReturnType; + } + } + + public MethodBase Method { get; } + public IntPtr InvokeThunk { get; } + + internal bool IsStatic { get; } + internal bool IsValueTypeInstanceMethod { get; } + internal bool NeedsCopyBack { get; } + internal DynamicInvokeTransform ReturnTransform { get; } + internal EETypePtr ReturnType { get; } + internal ArgumentInfo[] Arguments { get; } + } +} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs index da84727faf0a62..9de2c480274e37 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/General/ReflectionCoreCallbacksImplementation.cs @@ -417,25 +417,20 @@ public sealed override EnumInfo GetEnumInfo(Type type) return info; } - public sealed override DelegateDynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type) + public sealed override DynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type) { RuntimeTypeInfo runtimeType = type.CastToRuntimeTypeInfo(); - DelegateDynamicInvokeInfo? info = runtimeType.GenericCache as DelegateDynamicInvokeInfo; + DynamicInvokeInfo? info = runtimeType.GenericCache as DynamicInvokeInfo; if (info != null) return info; RuntimeMethodInfo invokeMethod = runtimeType.GetInvokeMethod(); MethodInvoker methodInvoker = invokeMethod.MethodInvoker; - IntPtr invokeThunk = ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetDynamicInvokeThunk(methodInvoker, out IntPtr genericDictionary); + IntPtr invokeThunk = ReflectionCoreExecution.ExecutionDomain.ExecutionEnvironment.GetDynamicInvokeThunk(methodInvoker); - info = new DelegateDynamicInvokeInfo() - { - InvokeMethod = invokeMethod, - InvokeThunk = invokeThunk, - GenericDictionary = genericDictionary - }; + info = new DynamicInvokeInfo(invokeMethod, invokeThunk); runtimeType.GenericCache = info; return info; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs index c75d371be48d77..c64b56818aa3b4 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Runtime/MethodInfos/RuntimeNamedMethodInfo.cs @@ -298,32 +298,7 @@ internal sealed override MethodInvoker GetUncachedMethodInvoker(RuntimeTypeInfo[ { MethodInvoker invoker = _common.GetUncachedMethodInvoker(methodArguments, exceptionPertainant, out Exception exception); if (invoker == null) - { - // If we have byref-like types in the signature, the reason we couldn't find an invoker - // is that. - bool hasByRefLikeParameter = false; - foreach (ParameterInfo parameter in GetParametersNoCopy()) - { - if (Unwrap(parameter.ParameterType).IsByRefLike) - { - hasByRefLikeParameter = true; - } - } - - if (hasByRefLikeParameter || Unwrap(ReturnType).IsByRefLike) - { - throw new NotSupportedException(SR.NotSupported_ByRefLike); - } - - static Type Unwrap(Type t) - { - while (t.HasElementType) - t = t.GetElementType()!; - return t; - } - throw exception; - } return invoker; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs index 35077dc025b3e3..48b799e20fbfe9 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/RuntimeImports.cs @@ -654,6 +654,32 @@ internal static IntPtr RhGetModuleSection(TypeManagerHandle module, ReadyToRunSe [RuntimeImport(RuntimeLibrary, "RhBulkMoveWithWriteBarrier")] internal static extern unsafe void RhBulkMoveWithWriteBarrier(ref byte dmem, ref byte smem, nuint size); + internal unsafe struct GCFrameRegistration + { + private nuint m_reserved1; + private nuint m_reserved2; + private void* m_pObjRefs; + private uint m_numObjRefs; + private int m_MaybeInterior; + + public GCFrameRegistration(void* allocation, uint elemCount, bool areByRefs = true) + { + m_reserved1 = 0; + m_reserved2 = 0; + m_pObjRefs = allocation; + m_numObjRefs = elemCount; + m_MaybeInterior = areByRefs ? 1 : 0; + } + } + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhRegisterForGCReporting")] + internal static extern unsafe void RhRegisterForGCReporting(GCFrameRegistration* pRegistration); + + [MethodImpl(MethodImplOptions.InternalCall)] + [RuntimeImport(RuntimeLibrary, "RhUnregisterForGCReporting")] + internal static extern unsafe void RhUnregisterForGCReporting(GCFrameRegistration* pRegistration); + // // ETW helpers. // diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs index 2d89aeb6fbfa20..d5674b4d05a0e8 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/TypeLoaderExports.cs @@ -381,5 +381,9 @@ public static T Call(System.IntPtr pfn, object arg1, IntPtr arg2) [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] public static T Call(IntPtr pfn, string[] arg0) => ((delegate*)pfn)(arg0); + + [MethodImplAttribute(MethodImplOptions.AggressiveInlining)] + public static ref byte Call(IntPtr pfn, void* arg1, ref byte arg2, ref byte arg3, void* arg4) + => ref ((delegate*)pfn)(arg1, ref arg2, ref arg3, arg4); } } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.Internal.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.Internal.cs index cad92a13fcc975..23d968e8608718 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.Internal.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Type.Internal.cs @@ -1,6 +1,7 @@ // 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; using Internal.Runtime.Augments; @@ -15,6 +16,13 @@ namespace System // public abstract partial class Type { + internal EETypePtr GetEEType() + { + RuntimeTypeHandle typeHandle = RuntimeAugments.Callbacks.GetTypeHandleIfAvailable(this); + Debug.Assert(!typeHandle.IsNull); + return typeHandle.ToEETypePtr(); + } + internal bool TryGetEEType(out EETypePtr eeType) { RuntimeTypeHandle typeHandle = RuntimeAugments.Callbacks.GetTypeHandleIfAvailable(this); diff --git a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs index 954eba2677fdae..474db052488311 100644 --- a/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs +++ b/src/coreclr/nativeaot/System.Private.DisabledReflection/src/Internal/Reflection/ReflectionCoreCallbacksImplementation.cs @@ -21,7 +21,7 @@ public override EnumInfo GetEnumInfo(Type type) isFlags: false); } - public override DelegateDynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type) + public override DynamicInvokeInfo GetDelegateDynamicInvokeInfo(Type type) => throw new NotSupportedException(SR.Reflection_Disabled); public override object ActivatorCreateInstance( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs index e8fd8bb74f6f2f..9505f6d4b8e601 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.MappingTables.cs @@ -399,81 +399,14 @@ public sealed override MethodInvoker TryGetMethodInvoker(RuntimeTypeHandle decla // Get the pointers necessary to call a dynamic method invocation function // - // This is either a function pointer to call, or a function pointer and template token. - private unsafe void GetDynamicMethodInvokeMethodInfo(NativeFormatModuleInfo module, uint cookie, RuntimeTypeHandle[] argHandles, - out IntPtr dynamicInvokeMethod, out IntPtr dynamicInvokeMethodGenericDictionary) + private static IntPtr GetDynamicMethodInvoke(NativeFormatModuleInfo module, uint cookie) { - if ((cookie & 1) == 1) - { - // If the dynamic invoke method is a generic method, we need to consult the DynamicInvokeTemplateData table to locate - // the matching template so that we can instantiate it. The DynamicInvokeTemplateData table starts with a single UINT - // with the RVA of the type that hosts all DynamicInvoke methods. The table then follows with list of [Token, FunctionPointer] - // pairs. The cookie parameter is an index into this table and points to a single pair. - byte* pBlobAsBytes; - uint cbBlob; - bool success = module.TryFindBlob((int)ReflectionMapBlob.DynamicInvokeTemplateData, out pBlobAsBytes, out cbBlob); - Debug.Assert(success && cbBlob > 4); - - byte* pNativeLayoutInfoBlob; - uint cbNativeLayoutInfoBlob; - success = module.TryFindBlob((int)ReflectionMapBlob.NativeLayoutInfo, out pNativeLayoutInfoBlob, out cbNativeLayoutInfoBlob); - Debug.Assert(success); - - RuntimeTypeHandle declaringTypeHandle; - // All methods referred from this blob are contained in the same type. The first UINT in the blob is a reloc to that MethodTable - if (RuntimeAugments.SupportsRelativePointers) - { - // 32bit relative relocs - declaringTypeHandle = RuntimeAugments.CreateRuntimeTypeHandle((IntPtr)(pBlobAsBytes + *(int*)pBlobAsBytes)); - } - else - { - declaringTypeHandle = RuntimeAugments.CreateRuntimeTypeHandle(*(IntPtr*)pBlobAsBytes); - } - - // The index points to two entries: the token of the dynamic invoke method and the function pointer to the canonical method - // Now have the type loader build or locate a dictionary for this method - uint index = cookie >> 1; - - MethodNameAndSignature nameAndSignature; - RuntimeSignature nameAndSigSignature; - - if (RuntimeAugments.SupportsRelativePointers) - { - nameAndSigSignature = RuntimeSignature.CreateFromNativeLayoutSignature(module.Handle, ((uint*)pBlobAsBytes)[index]); - } - else - { - nameAndSigSignature = RuntimeSignature.CreateFromNativeLayoutSignature(module.Handle, (uint)((IntPtr*)pBlobAsBytes)[index]); - } - - success = TypeLoaderEnvironment.Instance.TryGetMethodNameAndSignatureFromNativeLayoutSignature(nameAndSigSignature, out nameAndSignature); - Debug.Assert(success); - - success = TypeLoaderEnvironment.Instance.TryGetGenericMethodDictionaryForComponents(declaringTypeHandle, argHandles, nameAndSignature, out dynamicInvokeMethodGenericDictionary); - Debug.Assert(success); - - if (RuntimeAugments.SupportsRelativePointers) - { - // 32bit relative relocs - int* pRelPtr32 = &((int*)pBlobAsBytes)[index + 1]; - dynamicInvokeMethod = (IntPtr)((byte*)pRelPtr32 + *pRelPtr32); - } - else - { - dynamicInvokeMethod = ((IntPtr*)pBlobAsBytes)[index + 1]; - } - } - else - { - // Nongeneric DynamicInvoke method. This is used to DynamicInvoke methods that have parameters that - // cannot be shared (or if there are no parameters to begin with). - ExternalReferencesTable extRefs = default(ExternalReferencesTable); - extRefs.InitializeCommonFixupsTable(module); + // Nongeneric DynamicInvoke method. This is used to DynamicInvoke methods that have parameters that + // cannot be shared (or if there are no parameters to begin with). + ExternalReferencesTable extRefs = default(ExternalReferencesTable); + extRefs.InitializeCommonFixupsTable(module); - dynamicInvokeMethod = extRefs.GetFunctionPointerFromIndex(cookie >> 1); - dynamicInvokeMethodGenericDictionary = IntPtr.Zero; - } + return extRefs.GetFunctionPointerFromIndex(cookie); } #if FEATURE_UNIVERSAL_GENERICS @@ -491,35 +424,6 @@ private static IntPtr GetDynamicMethodInvokerThunk(MethodBase methodInfo) } #endif - private static RuntimeTypeHandle[] GetDynamicInvokeInstantiationArguments(MethodBase reflectionMethodBase) - { - // The DynamicInvoke method is a generic method with arguments that match the arguments of the target method. - // Prepare the list of arguments so that we can use it to instantiate the method. - - MethodParametersInfo methodParamsInfo = new MethodParametersInfo(reflectionMethodBase); - LowLevelList dynamicInvokeMethodGenArguments = methodParamsInfo.ParameterTypeHandles; - - // This is either a constructor ("returns" void) or an instance method - MethodInfo reflectionMethodInfo = reflectionMethodBase as MethodInfo; - - // Only use the return type if it's not void - if (reflectionMethodInfo != null && reflectionMethodInfo.ReturnType != typeof(void)) - dynamicInvokeMethodGenArguments.Add(methodParamsInfo.ReturnTypeHandle); - - for (int i = 0; i < dynamicInvokeMethodGenArguments.Count; i++) - { - // We can't instantiate over pointer types, so the DynamicInvoke method compensates for it already. - RuntimeTypeHandle type = dynamicInvokeMethodGenArguments[i]; - while (RuntimeAugments.IsUnmanagedPointerType(type)) - { - type = RuntimeAugments.GetRelatedParameterTypeHandle(type); - } - dynamicInvokeMethodGenArguments[i] = type; - } - - return dynamicInvokeMethodGenArguments.ToArray(); - } - private static RuntimeTypeHandle[] GetTypeSequence(ref ExternalReferencesTable extRefs, ref NativeParser parser) { uint count = parser.GetUnsigned(); @@ -565,7 +469,7 @@ private static IntPtr TryGetVirtualResolveData(NativeFormatModuleInfo module, /// Helper structure used for comparing signatures /// Requested canon form /// Constructed method invoke info, null on failure - private unsafe MethodInvokeInfo TryGetMethodInvokeInfo( + private static unsafe MethodInvokeInfo TryGetMethodInvokeInfo( RuntimeTypeHandle declaringTypeHandle, QMethodDefinition methodHandle, RuntimeTypeHandle[] genericMethodTypeArgumentHandles, @@ -609,7 +513,6 @@ private unsafe MethodInvokeInfo TryGetMethodInvokeInfo( #endif IntPtr dynamicInvokeMethod; - IntPtr dynamicInvokeMethodGenericDictionary; if ((methodInvokeMetadata.InvokeTableFlags & InvokeTableFlags.NeedsParameterInterpretation) != 0) { #if FEATURE_UNIVERSAL_GENERICS @@ -621,14 +524,9 @@ private unsafe MethodInvokeInfo TryGetMethodInvokeInfo( } else { - RuntimeTypeHandle[] dynInvokeMethodArgs = GetDynamicInvokeInstantiationArguments(methodInfo); - - GetDynamicMethodInvokeMethodInfo( + dynamicInvokeMethod = GetDynamicMethodInvoke( methodInvokeMetadata.MappingTableModule, - methodInvokeMetadata.DynamicInvokeCookie, - dynInvokeMethodArgs, - out dynamicInvokeMethod, - out dynamicInvokeMethodGenericDictionary); + methodInvokeMetadata.DynamicInvokeCookie); } IntPtr resolver = IntPtr.Zero; @@ -643,12 +541,9 @@ private unsafe MethodInvokeInfo TryGetMethodInvokeInfo( return null; } - var methodInvokeInfo = new MethodInvokeInfo + var methodInvokeInfo = new MethodInvokeInfo(methodInfo, dynamicInvokeMethod) { LdFtnResult = methodInvokeMetadata.MethodEntryPoint, - DynamicInvokeMethod = dynamicInvokeMethod, - DynamicInvokeGenericDictionary = dynamicInvokeMethodGenericDictionary, - MethodInfo = methodInfo, VirtualResolveData = resolver, }; return methodInvokeInfo; diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs index 52b1b0c801043a..f221221432f637 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/ExecutionEnvironmentImplementation.Runtime.cs @@ -163,11 +163,10 @@ public sealed override EnumInfo GetEnumInfo(RuntimeTypeHandle typeHandle) return null; } - public override IntPtr GetDynamicInvokeThunk(MethodInvoker invoker, out IntPtr genericDictionary) + public override IntPtr GetDynamicInvokeThunk(MethodInvoker invoker) { - MethodInvokeInfo invokeInfo = ((MethodInvokerWithMethodInvokeInfo)invoker).MethodInvokeInfo; - genericDictionary = invokeInfo.DynamicInvokeGenericDictionary; - return invokeInfo.DynamicInvokeMethod; + return ((MethodInvokerWithMethodInvokeInfo)invoker).MethodInvokeInfo.InvokeThunk + ; } } } diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokeInfo.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokeInfo.cs index 1c020b7c0aeffb..39ab0c2d459042 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokeInfo.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokeInfo.cs @@ -6,12 +6,14 @@ namespace Internal.Reflection.Execution { - internal sealed class MethodInvokeInfo + internal sealed class MethodInvokeInfo : DynamicInvokeInfo { + public MethodInvokeInfo(MethodBase method, IntPtr invokeThunk) + : base(method, invokeThunk) + { + } + public IntPtr LdFtnResult { get; set; } - public IntPtr DynamicInvokeMethod { get; set; } - public IntPtr DynamicInvokeGenericDictionary { get; set; } - public MethodBase MethodInfo { get; set; } public IntPtr VirtualResolveData { get; set; } } } diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs index 5a1ab72e7871eb..c73d7f6d496e56 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/InstanceMethodInvoker.cs @@ -33,13 +33,10 @@ public InstanceMethodInvoker(MethodInvokeInfo methodInvokeInfo, RuntimeTypeHandl object? result = RuntimeAugments.CallDynamicInvokeMethod( thisObject, MethodInvokeInfo.LdFtnResult, - MethodInvokeInfo.DynamicInvokeMethod, - MethodInvokeInfo.DynamicInvokeGenericDictionary, - MethodInvokeInfo.MethodInfo, + MethodInvokeInfo, arguments, binderBundle, - wrapInTargetInvocationException: wrapInTargetInvocationException, - methodToCallIsThisCall: true); + wrapInTargetInvocationException); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; } @@ -48,7 +45,7 @@ public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, o { if (isOpen) { - MethodInfo methodInfo = (MethodInfo)MethodInvokeInfo.MethodInfo; + MethodInfo methodInfo = (MethodInfo)MethodInvokeInfo.Method; short resolveType = OpenMethodResolver.OpenNonVirtualResolve; diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs index 44cc4ab7b0176d..e01bf48c5cc89d 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/StaticMethodInvoker.cs @@ -27,15 +27,12 @@ public StaticMethodInvoker(MethodInvokeInfo methodInvokeInfo) protected sealed override object? Invoke(object? thisObject, object?[]? arguments, BinderBundle binderBundle, bool wrapInTargetInvocationException) { object? result = RuntimeAugments.CallDynamicInvokeMethod( - thisObject, + null, // this pointer is ignored for static methods MethodInvokeInfo.LdFtnResult, - MethodInvokeInfo.DynamicInvokeMethod, - MethodInvokeInfo.DynamicInvokeGenericDictionary, - MethodInvokeInfo.MethodInfo, + MethodInvokeInfo, arguments, binderBundle, - wrapInTargetInvocationException: wrapInTargetInvocationException, - methodToCallIsThisCall: false); + wrapInTargetInvocationException); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; } diff --git a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs index 9634e693deb47d..996a8e4b7e2685 100644 --- a/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs +++ b/src/coreclr/nativeaot/System.Private.Reflection.Execution/src/Internal/Reflection/Execution/MethodInvokers/VirtualMethodInvoker.cs @@ -60,13 +60,10 @@ public sealed override Delegate CreateDelegate(RuntimeTypeHandle delegateType, o object? result = RuntimeAugments.CallDynamicInvokeMethod( thisObject, resolvedVirtual, - MethodInvokeInfo.DynamicInvokeMethod, - MethodInvokeInfo.DynamicInvokeGenericDictionary, - MethodInvokeInfo.MethodInfo, + MethodInvokeInfo, arguments, binderBundle, - wrapInTargetInvocationException: wrapInTargetInvocationException, - methodToCallIsThisCall: true); + wrapInTargetInvocationException); System.Diagnostics.DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return result; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs index 870095e4b9389f..2c677d51b052c0 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/SortableDependencyNode.cs @@ -73,7 +73,6 @@ protected enum ObjectNodeOrder ResourceIndexNode, TypeMetadataMapNode, ClassConstructorContextMap, - DynamicInvokeTemplateDataNode, ReflectionInvokeMapNode, DelegateMarshallingStubMapNode, StructMarshallingStubMapNode, diff --git a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs index e324885edee59a..12e255bb7228d2 100644 --- a/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs +++ b/src/coreclr/tools/Common/Internal/Runtime/MetadataBlob.cs @@ -7,16 +7,16 @@ internal enum ReflectionMapBlob { TypeMap = 1, ArrayMap = 2, - GenericInstanceMap = 3, // unused - GenericParameterMap = 4, // unused + // unused = 3, + // unused = 4, BlockReflectionTypeMap = 5, InvokeMap = 6, VirtualInvokeMap = 7, CommonFixupsTable = 8, FieldAccessMap = 9, CCtorContextMap = 10, - DiagGenericInstanceMap = 11, // unused - DiagGenericParameterMap = 12, // unused + // unused = 11, + // unused = 12, EmbeddedMetadata = 13, DefaultConstructorMap = 14, UnboxingAndInstantiatingStubMap = 15, @@ -28,7 +28,7 @@ internal enum ReflectionMapBlob // Reflection template types/methods blobs: TypeTemplateMap = 21, GenericMethodsTemplateMap = 22, - DynamicInvokeTemplateData = 23, + // unused = 23, BlobIdResourceIndex = 24, BlobIdResourceData = 25, BlobIdStackTraceEmbeddedMetadata = 26, diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Mangling.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Mangling.cs new file mode 100644 index 00000000000000..1c03c18928c7ee --- /dev/null +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Mangling.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Internal.TypeSystem; + +namespace Internal.IL.Stubs +{ + public partial class DynamicInvokeMethodThunk : IPrefixMangledSignature + { + MethodSignature IPrefixMangledSignature.BaseSignature + { + get + { + return _targetSignature; + } + } + + string IPrefixMangledSignature.Prefix + { + get + { + return "DynamicInvoke"; + } + } + } +} diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Sorting.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Sorting.cs index 7ae9157c025e73..3e67c728c743b8 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Sorting.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.Sorting.cs @@ -15,53 +15,8 @@ partial class DynamicInvokeMethodThunk protected override int CompareToImpl(MethodDesc other, TypeSystemComparer comparer) { - return CompareTo((DynamicInvokeMethodThunk)other); - } - - private int CompareTo(DynamicInvokeMethodThunk otherMethod) - { - int result = _targetSignature.Length - otherMethod._targetSignature.Length; - if (result != 0) - return result; - - DynamicInvokeMethodParameterKind thisReturnType = _targetSignature.ReturnType; - result = (int)thisReturnType - (int)otherMethod._targetSignature.ReturnType; - if (result != 0) - return result; - - result = _targetSignature.GetNumerOfReturnTypePointerIndirections() - otherMethod._targetSignature.GetNumerOfReturnTypePointerIndirections(); - if (result != 0) - return result; - - for (int i = 0; i < _targetSignature.Length; i++) - { - DynamicInvokeMethodParameterKind thisParamType = _targetSignature[i]; - result = (int)thisParamType - (int)otherMethod._targetSignature[i]; - if (result != 0) - return result; - - result = _targetSignature.GetNumberOfParameterPointerIndirections(i) - otherMethod._targetSignature.GetNumberOfParameterPointerIndirections(i); - if (result != 0) - return result; - } - - Debug.Assert(this == otherMethod); - return 0; - } - - partial class DynamicInvokeThunkGenericParameter - { - protected override int ClassCode => -234393261; - - protected override int CompareToImpl(TypeDesc other, TypeSystemComparer comparer) - { - var otherType = (DynamicInvokeThunkGenericParameter)other; - int result = Index - otherType.Index; - if (result != 0) - return result; - - return _owningMethod.CompareTo(otherType._owningMethod); - } + var otherMethod = (DynamicInvokeMethodThunk)other; + return comparer.Compare(_targetSignature, otherMethod._targetSignature); } } } diff --git a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs index 6d8c49851345ac..912bedede7eb14 100644 --- a/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs +++ b/src/coreclr/tools/Common/TypeSystem/IL/Stubs/DynamicInvokeMethodThunk.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Reflection.Emit; +using System.Reflection; using System.Text; using Internal.TypeSystem; @@ -12,155 +14,63 @@ namespace Internal.IL.Stubs { /// - /// Thunk to dynamically invoke a method using reflection. The method accepts an object[] of parameters - /// to target method, lays them out on the stack, and calls the target method. This thunk has heavy - /// dependencies on the general dynamic invocation infrastructure in System.InvokeUtils and gets called from there + /// Thunk to dynamically invoke a method using reflection. The method accepts an parameters as byrefs + /// lays them out on the stack, and calls the target method. This thunk has heavy dependencies + /// on the general dynamic invocation infrastructure in System.InvokeUtils and gets called from there /// at runtime. See comments in System.InvokeUtils for a more thorough explanation. /// - public partial class DynamicInvokeMethodThunk : ILStubMethod + public sealed partial class DynamicInvokeMethodThunk : ILStubMethod { private TypeDesc _owningType; - private DynamicInvokeMethodSignature _targetSignature; - - private TypeDesc[] _instantiation; + private MethodSignature _targetSignature; private MethodSignature _signature; - public DynamicInvokeMethodThunk(TypeDesc owningType, DynamicInvokeMethodSignature signature) + public DynamicInvokeMethodThunk(TypeDesc owningType, MethodSignature targetSignature) { _owningType = owningType; - _targetSignature = signature; + _targetSignature = targetSignature; } - internal static bool SupportsDynamicInvoke(TypeSystemContext context) - { - return context.SystemModule.GetType("System", "InvokeUtils", throwIfNotFound: false) != null; - } + // MethodSignature does not track information about the type of the this pointer. We will steal + // one of the unused upper bits to track whether the this pointer is a byref or an object. This makes + // the information passed around seamlesly, including name mangling. + private static MethodSignatureFlags MethodSignatureFlags_ValueTypeInstanceMethod => (MethodSignatureFlags)0x8000; - private static TypeDesc UnwrapByRef(TypeDesc type) + public static MethodSignature NormalizeSignature(MethodSignature sig, bool valueTypeInstanceMethod) { - if (type.IsByRef) - return ((ByRefType)type).ParameterType; - return type; - } - - private static bool ContainsFunctionPointer(TypeDesc type) - { - if (type.IsFunctionPointer) - return true; - else if (type.IsParameterizedType) - return ContainsFunctionPointer(((ParameterizedType)type).ParameterType); - else - return false; - } - - public static bool SupportsSignature(MethodSignature signature) - { - // ---------------------------------------------------------------- - // TODO: function pointer types are odd: https://github.com/dotnet/corert/issues/1929 - // ---------------------------------------------------------------- - - // ---------------------------------------------------------------- - // Methods that take or return ByRef-like types can't be reflection invoked - // - // TODO: CoreCLR allows invoking methods that take ByRef-like types by value when the argument has - // the default null value. It is a corner case that is unlikely to be exercised in practice. - // ---------------------------------------------------------------- - - TypeDesc unwrappedReturnType = UnwrapByRef(signature.ReturnType); - if (ContainsFunctionPointer(unwrappedReturnType)) - return false; - if (!unwrappedReturnType.IsSignatureVariable && unwrappedReturnType.IsByRefLike) - return false; - - for (int i = 0; i < signature.Length; i++) + MethodSignatureFlags flags = 0; + if (sig.IsStatic) { - TypeDesc unwrappedParameterType = UnwrapByRef(signature[i]); - if (ContainsFunctionPointer(unwrappedParameterType)) - return false; - if (!unwrappedParameterType.IsSignatureVariable && unwrappedParameterType.IsByRefLike) - return false; + flags |= MethodSignatureFlags.Static; } - - return true; - } - - public static TypeDesc[] GetThunkInstantiationForMethod(MethodDesc method) - { - MethodSignature sig = method.Signature; - - ParameterMetadata[] paramMetadata = null; - TypeDesc[] instantiation = new TypeDesc[sig.ReturnType.IsVoid ? sig.Length : sig.Length + 1]; - - for (int i = 0; i < sig.Length; i++) + else if (valueTypeInstanceMethod) { - TypeDesc parameterType = sig[i]; - if (parameterType.IsByRef) - { - // strip ByRefType off the parameter (the method already has ByRef in the signature) - parameterType = ((ByRefType)parameterType).ParameterType; - - // Strip off all the pointers. Pointers are not valid instantiation arguments and the thunk compensates for that - // by being specialized for the specific pointer depth. - while (parameterType.IsPointer) - parameterType = ((PointerType)parameterType).ParameterType; - } - else if (parameterType.IsPointer) - { - // Strip off all the pointers. Pointers are not valid instantiation arguments and the thunk compensates for that - // by being specialized for the specific pointer depth. - while (parameterType.IsPointer) - parameterType = ((PointerType)parameterType).ParameterType; - } - else if (parameterType.IsEnum) - { - // If the invoke method takes an enum as an input parameter and there is no default value for - // that parameter, we don't need to specialize on the exact enum type (we only need to specialize - // on the underlying integral type of the enum.) - if (paramMetadata == null) - paramMetadata = method.GetParameterMetadata(); - - bool hasDefaultValue = false; - foreach (var p in paramMetadata) - { - // Parameter metadata indexes are 1-based (0 is reserved for return "parameter") - if (p.Index == (i + 1) && p.HasDefault) - { - hasDefaultValue = true; - break; - } - } - - if (!hasDefaultValue) - parameterType = parameterType.UnderlyingType; - } - - instantiation[i] = parameterType; + flags |= MethodSignatureFlags_ValueTypeInstanceMethod; } - if (!sig.ReturnType.IsVoid) + TypeDesc[] parameters = new TypeDesc[sig.Length]; + for (int i = 0; i < sig.Length; i++) + parameters[i] = NormalizeType(sig[i]); + return new MethodSignature(flags, 0, NormalizeType(sig.ReturnType), parameters); + + static TypeDesc NormalizeType(TypeDesc type) { - TypeDesc returnType = sig.ReturnType; + Debug.Assert(!type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true)); - // strip ByRefType off the return type (the method already has ByRef in the signature) - if (returnType.IsByRef) - returnType = ((ByRefType)returnType).ParameterType; + if (type.IsByRef) + return type.Context.GetWellKnownType(WellKnownType.Byte).MakeByRefType(); - // If the invoke method return an object reference, we don't need to specialize on the - // exact type of the object reference, as the behavior is not different. - if ((returnType.IsDefType && !returnType.IsValueType) || returnType.IsArray) - { - returnType = method.Context.GetWellKnownType(WellKnownType.Object); - } + if (type.IsPointer || type.IsFunctionPointer) + return type.Context.GetWellKnownType(WellKnownType.Void).MakePointerType(); - // Strip off all the pointers. Pointers are not valid instantiation arguments and the thunk compensates for that - // by being specialized for the specific pointer depth. - while (returnType.IsPointer) - returnType = ((PointerType)returnType).ParameterType; + if (type.IsEnum) + return type.UnderlyingType; - instantiation[sig.Length] = returnType; - } + if (type.IsValueType) + return type; - return instantiation; + return type.Context.GetWellKnownType(WellKnownType.Object); + } } public override TypeSystemContext Context @@ -179,46 +89,25 @@ public override TypeDesc OwningType } } - private MetadataType InvokeUtilsType - { - get - { - return Context.SystemModule.GetKnownType("System", "InvokeUtils"); - } - } - - private MetadataType ArgSetupStateType - { - get - { - return InvokeUtilsType.GetNestedType("ArgSetupState"); - } - } - - public DynamicInvokeMethodSignature TargetSignature - { - get - { - return _targetSignature; - } - } - public override MethodSignature Signature { get { if (_signature == null) { + var ptr = Context.GetWellKnownType(WellKnownType.Void).MakePointerType(); + var byref = Context.GetWellKnownType(WellKnownType.Byte).MakeByRefType(); + _signature = new MethodSignature( MethodSignatureFlags.Static, - Instantiation.Length, - Context.GetWellKnownType(WellKnownType.Object), + 0, + byref, new TypeDesc[] { - Context.GetWellKnownType(WellKnownType.Object), // thisPtr - Context.GetWellKnownType(WellKnownType.IntPtr), // methodToCall - ArgSetupStateType.MakeByRefType(), // argSetupState - Context.GetWellKnownType(WellKnownType.Boolean), // targetIsThisCall + ptr, // fptr + byref, // thisptr + byref, // refbuf + ptr // arguments }); } @@ -226,467 +115,71 @@ public override MethodSignature Signature } } - public override Instantiation Instantiation - { - get - { - if (_instantiation == null) - { - TypeDesc[] instantiation = - new TypeDesc[_targetSignature.HasReturnValue ? _targetSignature.Length + 1 : _targetSignature.Length]; + public override string Name => "DynamicInvoke"; - for (int i = 0; i < _targetSignature.Length; i++) - instantiation[i] = new DynamicInvokeThunkGenericParameter(this, i); + public override string DiagnosticName => "DynamicInvoke"; - if (_targetSignature.HasReturnValue) - instantiation[_targetSignature.Length] = - new DynamicInvokeThunkGenericParameter(this, _targetSignature.Length); + public MethodSignature TargetSignature => _targetSignature; - Interlocked.CompareExchange(ref _instantiation, instantiation, null); - } + public override MethodIL EmitIL() + { + ILEmitter emitter = new ILEmitter(); + ILCodeStream codeStream = emitter.NewCodeStream(); - return new Instantiation(_instantiation); + // Handle instance methods. + if (!_targetSignature.IsStatic) + { + codeStream.EmitLdArg(1); + if ((_targetSignature.Flags & MethodSignatureFlags_ValueTypeInstanceMethod) == 0) + codeStream.Emit(ILOpcode.ldind_ref); } - } - public override string Name - { - get + // Push the arguments. + if (_targetSignature.Length != 0) { - StringBuilder sb = new StringBuilder("InvokeRet"); - - switch (_targetSignature.ReturnType) - { - case DynamicInvokeMethodParameterKind.None: - sb.Append('V'); - break; - case DynamicInvokeMethodParameterKind.Pointer: - sb.Append('P'); - for (int i = 0; i < _targetSignature.GetNumerOfReturnTypePointerIndirections() - 1; i++) - sb.Append('p'); - break; - case DynamicInvokeMethodParameterKind.Reference: - sb.Append("R"); - for (int i = 0; i < _targetSignature.GetNumerOfReturnTypePointerIndirections(); i++) - sb.Append('p'); - break; - case DynamicInvokeMethodParameterKind.Value: - sb.Append('O'); - break; - default: - Debug.Fail("Unreachable"); - break; - } - + var fieldByReferenceValueToken = emitter.NewToken( + Context.SystemModule.GetKnownType("System", "ByReference").GetKnownField("Value")); for (int i = 0; i < _targetSignature.Length; i++) { - switch (_targetSignature[i]) + codeStream.EmitLdArg(3); + if (i != 0) { - case DynamicInvokeMethodParameterKind.Pointer: - sb.Append('P'); - - for (int j = 0; j < _targetSignature.GetNumberOfParameterPointerIndirections(i) - 1; j++) - sb.Append('p'); - - break; - case DynamicInvokeMethodParameterKind.Reference: - sb.Append("R"); - - for (int j = 0; j < _targetSignature.GetNumberOfParameterPointerIndirections(i); j++) - sb.Append('p'); - break; - case DynamicInvokeMethodParameterKind.Value: - sb.Append("I"); - break; - default: - Debug.Fail("Unreachable"); - break; + codeStream.EmitLdc(i * Context.Target.PointerSize); + codeStream.Emit(ILOpcode.add); } - } - return sb.ToString(); - } - } + codeStream.Emit(ILOpcode.ldfld, fieldByReferenceValueToken); - public override string DiagnosticName - { - get - { - return Name; - } - } - - public override MethodIL EmitIL() - { - ILEmitter emitter = new ILEmitter(); - ILCodeStream argSetupStream = emitter.NewCodeStream(); - ILCodeStream thisCallSiteSetupStream = emitter.NewCodeStream(); - ILCodeStream staticCallSiteSetupStream = emitter.NewCodeStream(); - ILCodeStream returnCodeStream = emitter.NewCodeStream(); - - // This function will look like - // - // !For each parameter to the method - // !if (parameter is In Parameter) - // localX is TypeOfParameterX& - // ldarg.2 - // ldtoken TypeOfParameterX - // call DynamicInvokeParamHelperIn(ref ArgSetupState, RuntimeTypeHandle) - // stloc localX - // !else - // localX is TypeOfParameter - // ldarg.2 - // ldtoken TypeOfParameterX - // call DynamicInvokeParamHelperRef(ref ArgSetupState, RuntimeTypeHandle) - // stloc localX - - // ldarg.2 - // call DynamicInvokeArgSetupComplete(ref ArgSetupState) - - // *** Thiscall instruction stream starts here *** - - // ldarg.3 // Load targetIsThisCall - // brfalse Not_this_call - - // ldarg.0 // Load this pointer - // !For each parameter - // !if (parameter is In Parameter) - // ldloc localX - // ldobj TypeOfParameterX - // !else - // ldloc localX - // ldarg.1 - // calli ReturnType thiscall(TypeOfParameter1, ...) - // br Process_return - - // *** Static call instruction stream starts here *** - - // Not_this_call: - // !For each parameter - // !if (parameter is In Parameter) - // ldloc localX - // ldobj TypeOfParameterX - // !else - // ldloc localX - // ldarg.1 - // calli ReturnType (TypeOfParameter1, ...) - - // *** Return code stream starts here *** - - // Process_return: - // !if (ReturnType is Byref) - // dup - // brfalse ByRefNull - // ldobj ReturnType - // !if ((ReturnType == void) - // ldnull - // !elif (ReturnType is pointer) - // System.Reflection.Pointer.Box(ReturnType) - // !else - // box ReturnType - // ret - // - // !if (ReturnType is ByRef) - // ByRefNull: - // throw NullReferenceException - - ILCodeLabel lStaticCall = emitter.NewCodeLabel(); - ILCodeLabel lProcessReturn = emitter.NewCodeLabel(); - thisCallSiteSetupStream.EmitLdArg(3); // targetIsThisCall - thisCallSiteSetupStream.Emit(ILOpcode.brfalse, lStaticCall); - staticCallSiteSetupStream.EmitLabel(lStaticCall); - - thisCallSiteSetupStream.EmitLdArg(0); // thisPtr - - ILToken tokDynamicInvokeParamHelperRef = - emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeParamHelperRef", null)); - ILToken tokDynamicInvokeParamHelperIn = - emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeParamHelperIn", null)); - - TypeDesc[] targetMethodSignature = new TypeDesc[_targetSignature.Length]; - - for (int paramIndex = 0; paramIndex < _targetSignature.Length; paramIndex++) - { - TypeDesc paramType = Context.GetSignatureVariable(paramIndex, true); - DynamicInvokeMethodParameterKind paramKind = _targetSignature[paramIndex]; - - for (int i = 0; i < _targetSignature.GetNumberOfParameterPointerIndirections(paramIndex); i++) - paramType = paramType.MakePointerType(); - - ILToken tokParamType = emitter.NewToken(paramType); - ILLocalVariable local = emitter.NewLocal(paramType.MakeByRefType()); - - thisCallSiteSetupStream.EmitLdLoc(local); - staticCallSiteSetupStream.EmitLdLoc(local); - - argSetupStream.EmitLdArg(2); // argSetupState - argSetupStream.Emit(ILOpcode.ldtoken, tokParamType); - - if (paramKind == DynamicInvokeMethodParameterKind.Reference) - { - argSetupStream.Emit(ILOpcode.call, tokDynamicInvokeParamHelperRef); - - targetMethodSignature[paramIndex] = paramType.MakeByRefType(); - } - else - { - argSetupStream.Emit(ILOpcode.call, tokDynamicInvokeParamHelperIn); - - thisCallSiteSetupStream.EmitLdInd(paramType); - staticCallSiteSetupStream.EmitLdInd(paramType); - - targetMethodSignature[paramIndex] = paramType; + var parameterType = _targetSignature[i]; + if (!parameterType.IsByRef) + { + codeStream.EmitLdInd(parameterType); + } } - argSetupStream.EmitStLoc(local); } - argSetupStream.EmitLdArg(2); // argSetupState - argSetupStream.Emit(ILOpcode.call, emitter.NewToken(InvokeUtilsType.GetKnownMethod("DynamicInvokeArgSetupComplete", null))); - - thisCallSiteSetupStream.EmitLdArg(1); // methodToCall - staticCallSiteSetupStream.EmitLdArg(1); // methodToCall - - DynamicInvokeMethodParameterKind returnKind = _targetSignature.ReturnType; - TypeDesc returnType = returnKind != DynamicInvokeMethodParameterKind.None ? - Context.GetSignatureVariable(_targetSignature.Length, true) : - Context.GetWellKnownType(WellKnownType.Void); - - for (int i = 0; i < _targetSignature.GetNumerOfReturnTypePointerIndirections(); i++) - returnType = returnType.MakePointerType(); - - if (returnKind == DynamicInvokeMethodParameterKind.Reference) - returnType = returnType.MakeByRefType(); - - MethodSignature thisCallMethodSig = new MethodSignature(0, 0, returnType, targetMethodSignature); - thisCallSiteSetupStream.Emit(ILOpcode.calli, emitter.NewToken(thisCallMethodSig)); - thisCallSiteSetupStream.Emit(ILOpcode.br, lProcessReturn); - - MethodSignature staticCallMethodSig = new MethodSignature(MethodSignatureFlags.Static, 0, returnType, targetMethodSignature); - staticCallSiteSetupStream.Emit(ILOpcode.calli, emitter.NewToken(staticCallMethodSig)); - - returnCodeStream.EmitLabel(lProcessReturn); + codeStream.EmitLdArg(0); + codeStream.Emit(ILOpcode.calli, emitter.NewToken(_targetSignature)); - ILCodeLabel lByRefReturnNull = null; - - if (returnKind == DynamicInvokeMethodParameterKind.None) - { - returnCodeStream.Emit(ILOpcode.ldnull); - } - else + // Store the return value unless it is a byref + var returnType = _targetSignature.ReturnType; + if (!returnType.IsByRef) { - TypeDesc returnTypeForBoxing = returnType; - - if (returnType.IsByRef) + if (!returnType.IsVoid) { - // If this is a byref return, we need to dereference first - returnTypeForBoxing = ((ByRefType)returnType).ParameterType; - lByRefReturnNull = emitter.NewCodeLabel(); - returnCodeStream.Emit(ILOpcode.dup); - returnCodeStream.Emit(ILOpcode.brfalse, lByRefReturnNull); - returnCodeStream.EmitLdInd(returnTypeForBoxing); - } + var retVal = emitter.NewLocal(returnType); + codeStream.EmitStLoc(retVal); - if (returnTypeForBoxing.IsPointer) - { - // Pointers box differently - returnCodeStream.Emit(ILOpcode.ldtoken, emitter.NewToken(returnTypeForBoxing)); - MethodDesc getTypeFromHandleMethod = - Context.SystemModule.GetKnownType("System", "Type").GetKnownMethod("GetTypeFromHandle", null); - returnCodeStream.Emit(ILOpcode.call, emitter.NewToken(getTypeFromHandleMethod)); - - MethodDesc pointerBoxMethod = - Context.SystemModule.GetKnownType("System.Reflection", "Pointer").GetKnownMethod("Box", null); - returnCodeStream.Emit(ILOpcode.call, emitter.NewToken(pointerBoxMethod)); - } - else - { - ILToken tokReturnType = emitter.NewToken(returnTypeForBoxing); - returnCodeStream.Emit(ILOpcode.box, tokReturnType); + codeStream.EmitLdArg(2); + codeStream.EmitLdLoc(retVal); + codeStream.EmitStInd(returnType); } + codeStream.EmitLdArg(2); } - returnCodeStream.Emit(ILOpcode.ret); - - if (lByRefReturnNull != null) - { - returnCodeStream.EmitLabel(lByRefReturnNull); - MethodDesc nullReferencedExceptionHelper = Context.GetHelperEntryPoint("ThrowHelpers", "ThrowInvokeNullRefReturned"); - returnCodeStream.EmitCallThrowHelper(emitter, nullReferencedExceptionHelper); - } + codeStream.Emit(ILOpcode.ret); return emitter.Link(this); } - - private partial class DynamicInvokeThunkGenericParameter : GenericParameterDesc - { - private DynamicInvokeMethodThunk _owningMethod; - - public DynamicInvokeThunkGenericParameter(DynamicInvokeMethodThunk owningMethod, int index) - { - _owningMethod = owningMethod; - Index = index; - } - - public override TypeSystemContext Context - { - get - { - return _owningMethod.Context; - } - } - - public override int Index - { - get; - } - - public override GenericParameterKind Kind - { - get - { - return GenericParameterKind.Method; - } - } - } - } - - internal enum DynamicInvokeMethodParameterKind - { - None, - Value, - Reference, - Pointer, - } - - /// - /// Wraps a to reduce it's fidelity. - /// - public struct DynamicInvokeMethodSignature : IEquatable - { - private MethodSignature _signature; - - public TypeSystemContext Context => _signature.ReturnType.Context; - - public bool HasReturnValue - { - get - { - return !_signature.ReturnType.IsVoid; - } - } - - public int Length - { - get - { - return _signature.Length; - } - } - - internal DynamicInvokeMethodParameterKind this[int index] - { - get - { - TypeDesc type = _signature[index]; - - if (type.IsByRef) - return DynamicInvokeMethodParameterKind.Reference; - else if (type.IsPointer) - return DynamicInvokeMethodParameterKind.Pointer; - else - return DynamicInvokeMethodParameterKind.Value; - } - } - - public static int GetNumberOfIndirections(TypeDesc type) - { - // Strip byrefness off. This is to support "ref void**"-style signatures. - if (type.IsByRef) - type = ((ByRefType)type).ParameterType; - - int result = 0; - while (type.IsPointer) - { - result++; - type = ((PointerType)type).ParameterType; - } - - return result; - } - - public int GetNumberOfParameterPointerIndirections(int paramIndex) - { - return GetNumberOfIndirections(_signature[paramIndex]); - } - - public int GetNumerOfReturnTypePointerIndirections() - { - return GetNumberOfIndirections(_signature.ReturnType); - } - - internal DynamicInvokeMethodParameterKind ReturnType - { - get - { - TypeDesc type = _signature.ReturnType; - if (type.IsPointer) - return DynamicInvokeMethodParameterKind.Pointer; - else if (type.IsVoid) - return DynamicInvokeMethodParameterKind.None; - else if (type.IsByRef) - return DynamicInvokeMethodParameterKind.Reference; - else - return DynamicInvokeMethodParameterKind.Value; - } - } - - public DynamicInvokeMethodSignature(MethodSignature concreteSignature) - { - Debug.Assert(DynamicInvokeMethodThunk.SupportsSignature(concreteSignature)); - _signature = concreteSignature; - } - - public override bool Equals(object obj) - { - return obj is DynamicInvokeMethodSignature && Equals((DynamicInvokeMethodSignature)obj); - } - - public override int GetHashCode() - { - int hashCode = (int)this.ReturnType * 0x5498341 + 0x832424; - - for (int i = 0; i < Length; i++) - { - int value = (int)this[i] * 0x5498341 + 0x832424; - hashCode = hashCode * 31 + value; - } - - return hashCode; - } - - public bool Equals(DynamicInvokeMethodSignature other) - { - DynamicInvokeMethodParameterKind thisReturnKind = ReturnType; - if (thisReturnKind != other.ReturnType) - return false; - - if (GetNumerOfReturnTypePointerIndirections() != other.GetNumerOfReturnTypePointerIndirections()) - return false; - - if (Length != other.Length) - return false; - - for (int i = 0; i < Length; i++) - { - DynamicInvokeMethodParameterKind thisParamKind = this[i]; - if (thisParamKind != other[i]) - return false; - - if (GetNumberOfParameterPointerIndirections(i) != other.GetNumberOfParameterPointerIndirections(i)) - return false; - } - - return true; - } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.DynamicInvoke.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.DynamicInvoke.cs index 0ed3d107b3a396..f67306c264149b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.DynamicInvoke.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.DynamicInvoke.cs @@ -10,22 +10,23 @@ namespace ILCompiler { partial class CompilerTypeSystemContext { - private class DynamicInvokeThunkHashtable : LockFreeReaderHashtable + private class DynamicInvokeThunkHashtable : LockFreeReaderHashtable { - protected override bool CompareKeyToValue(DynamicInvokeMethodSignature key, DynamicInvokeMethodThunk value) => key.Equals(value.TargetSignature); + protected override bool CompareKeyToValue(MethodSignature key, DynamicInvokeMethodThunk value) => key.Equals(value.TargetSignature); protected override bool CompareValueToValue(DynamicInvokeMethodThunk value1, DynamicInvokeMethodThunk value2) => value1.TargetSignature.Equals(value2.TargetSignature) && value1.OwningType == value2.OwningType; - protected override int GetKeyHashCode(DynamicInvokeMethodSignature key) => key.GetHashCode(); + protected override int GetKeyHashCode(MethodSignature key) => key.GetHashCode(); protected override int GetValueHashCode(DynamicInvokeMethodThunk value) => value.TargetSignature.GetHashCode(); - protected override DynamicInvokeMethodThunk CreateValueFromKey(DynamicInvokeMethodSignature key) + protected override DynamicInvokeMethodThunk CreateValueFromKey(MethodSignature key) { return new DynamicInvokeMethodThunk(((CompilerTypeSystemContext)key.Context).GeneratedAssembly.GetGlobalModuleType(), key); } } DynamicInvokeThunkHashtable _dynamicInvokeThunks = new DynamicInvokeThunkHashtable(); - public MethodDesc GetDynamicInvokeThunk(DynamicInvokeMethodSignature signature) + public MethodDesc GetDynamicInvokeThunk(MethodSignature signature, bool valueTypeInstanceMethod) { - return _dynamicInvokeThunks.GetOrCreateValue(signature); + return _dynamicInvokeThunks.GetOrCreateValue( + DynamicInvokeMethodThunk.NormalizeSignature(signature, valueTypeInstanceMethod)); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs deleted file mode 100644 index 334b73adbddf34..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateDataNode.cs +++ /dev/null @@ -1,147 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using System.Collections.Generic; - -using Internal.Text; -using Internal.TypeSystem; - -namespace ILCompiler.DependencyAnalysis -{ - /// - /// Represents a map between method name / signature and CanonicalEntryPoint for the corresponding invoke stub. - /// The first entry is the containing type of the invoke stubs. - /// - internal sealed class DynamicInvokeTemplateDataNode : ObjectNode, ISymbolDefinitionNode - { - private ObjectAndOffsetSymbolNode _endSymbol; - private ExternalReferencesTableNode _externalReferences; - private Dictionary _methodToTemplateIndex; - - public DynamicInvokeTemplateDataNode(ExternalReferencesTableNode externalReferences) - { - _endSymbol = new ObjectAndOffsetSymbolNode(this, 0, "__dynamic_invoke_template_data_end", true); - _externalReferences = externalReferences; - } - - public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) - { - sb.Append(nameMangler.CompilationUnitPrefix).Append("__dynamic_invoke_template_data"); - } - - public ISymbolNode EndSymbol => _endSymbol; - public int Offset => 0; - public override ObjectNodeSection Section => _externalReferences.Section; - public override bool IsShareable => false; - public override bool StaticDependenciesAreComputed => true; - protected override string GetName(NodeFactory factory) => this.GetMangledName(factory.NameMangler); - public override bool ShouldSkipEmittingObjectNode(NodeFactory factory) => !factory.MetadataManager.GetDynamicInvokeTemplateMethods().GetEnumerator().MoveNext(); - - public int GetIdForMethod(MethodDesc dynamicInvokeMethod, NodeFactory factory) - { - // We should only see canonical or non-shared methods here - Debug.Assert(dynamicInvokeMethod.GetCanonMethodTarget(CanonicalFormKind.Specific) == dynamicInvokeMethod); - Debug.Assert(!dynamicInvokeMethod.IsCanonicalMethod(CanonicalFormKind.Universal)); - - if (_methodToTemplateIndex == null) - { - BuildMethodToIdMap(factory); - } - - return _methodToTemplateIndex[dynamicInvokeMethod]; - } - - private void BuildMethodToIdMap(NodeFactory factory) - { - // Get a sorted list of generated stubs - List methods = new List(factory.MetadataManager.GetDynamicInvokeTemplateMethods()); - - // Assign each stub an ID - var methodToTemplateIndex = new Dictionary(); - foreach (MethodDesc method in methods) - { - TypeDesc dynamicInvokeMethodContainingType = method.OwningType; - - int templateIndex = (2 * methodToTemplateIndex.Count) + 1; - // Add 1 to the index to account for the first blob entry being the containing MethodTable RVA - methodToTemplateIndex.Add(method, templateIndex); - } - - _methodToTemplateIndex = methodToTemplateIndex; - } - - internal static DependencyListEntry[] GetDependenciesDueToInvokeTemplatePresence(NodeFactory factory, MethodDesc method) - { - return new[] - { - new DependencyListEntry(factory.MethodEntrypoint(method), "Dynamic invoke stub"), - new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method)), "Dynamic invoke stub"), - new DependencyListEntry(factory.NecessaryTypeSymbol(method.OwningType), "Dynamic invoke stub containing type"), - new DependencyListEntry(factory.NativeLayout.TemplateMethodLayout(method), "Template"), - new DependencyListEntry(factory.NativeLayout.TemplateMethodEntry(method), "Template"), - }; - } - - public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) - { - if (relocsOnly) - return new ObjectData(Array.Empty(), Array.Empty(), 1, new ISymbolDefinitionNode[] { this }); - - // Ensure the native layout blob has been saved - factory.MetadataManager.NativeLayoutInfo.SaveNativeLayoutInfoWriter(factory); - - ObjectDataBuilder objData = new ObjectDataBuilder(factory, relocsOnly); - objData.RequireInitialPointerAlignment(); - objData.AddSymbol(this); - - if (_methodToTemplateIndex == null) - { - BuildMethodToIdMap(factory); - } - - TypeDesc containerType = null; - foreach (var method in _methodToTemplateIndex.Keys) - { - Debug.Assert(containerType == null || containerType == method.OwningType); - containerType = method.OwningType; -#if !DEBUG - break; -#endif - } - - if (factory.Target.SupportsRelativePointers) - objData.EmitReloc(factory.NecessaryTypeSymbol(containerType), RelocType.IMAGE_REL_BASED_RELPTR32); - else - objData.EmitPointerReloc(factory.NecessaryTypeSymbol(containerType)); - - List> sortedList = new List>(_methodToTemplateIndex); - sortedList.Sort((firstEntry, secondEntry) => firstEntry.Value.CompareTo(secondEntry.Value)); - - for (int i = 0; i < sortedList.Count; i++) - { - var nameAndSig = factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(sortedList[i].Key)); - - if (factory.Target.SupportsRelativePointers) - { - objData.EmitInt(nameAndSig.SavedVertex.VertexOffset); - objData.EmitReloc(factory.MethodEntrypoint(sortedList[i].Key), RelocType.IMAGE_REL_BASED_RELPTR32); - } - else - { - objData.EmitNaturalInt(nameAndSig.SavedVertex.VertexOffset); - objData.EmitPointerReloc(factory.MethodEntrypoint(sortedList[i].Key)); - } - } - - _endSymbol.SetSymbolOffset(objData.CountBytes); - objData.AddSymbol(_endSymbol); - - return objData.ToObjectData(); - } - - protected internal override int Phase => (int)ObjectNodePhase.Ordered; - public override int ClassCode => (int)ObjectNodeOrder.DynamicInvokeTemplateDataNode; - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateNode.cs deleted file mode 100644 index aebec11310545e..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DynamicInvokeTemplateNode.cs +++ /dev/null @@ -1,44 +0,0 @@ -// 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.Collections.Generic; - -using Internal.TypeSystem; -using ILCompiler.DependencyAnalysisFramework; - -namespace ILCompiler.DependencyAnalysis -{ - /// - /// Models dependencies of the dynamic invoke methods that aid in reflection invoking methods. - /// Dynamic invoke methods are shared method bodies that perform calling convention conversion - /// from object[] to the expected signature of the called method. - /// - internal class DynamicInvokeTemplateNode : DependencyNodeCore - { - public MethodDesc Method { get; } - - public DynamicInvokeTemplateNode(MethodDesc method) - { - Debug.Assert(method.IsSharedByGenericInstantiations); - Method = method; - } - - public override IEnumerable GetStaticDependencies(NodeFactory factory) - { - return DynamicInvokeTemplateDataNode.GetDependenciesDueToInvokeTemplatePresence(factory, Method); - } - - protected override string GetName(NodeFactory factory) - { - return "DynamicInvokeTemplate: " + factory.NameMangler.GetMangledMethodName(Method); - } - - public override bool InterestingForDynamicDependencyAnalysis => false; - public override bool HasDynamicDependencies => false; - public override bool HasConditionalStaticDependencies => false; - public override bool StaticDependenciesAreComputed => true; - public override IEnumerable GetConditionalStaticDependencies(NodeFactory context) => null; - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory context) => null; - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs index 2e5068110f6d26..47d8d1a0df43fc 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs @@ -289,11 +289,6 @@ private void CreateNodeCaches() return new TypeGVMEntriesNode(type); }); - _dynamicInvokeTemplates = new NodeCache(method => - { - return new DynamicInvokeTemplateNode(method); - }); - _reflectableMethods = new NodeCache(method => { return new ReflectableMethodNode(method); @@ -882,12 +877,6 @@ internal ObjectGetTypeFlowDependenciesNode ObjectGetTypeFlowDependencies(Metadat return _objectGetTypeFlowDependencies.GetOrAdd(type); } - private NodeCache _dynamicInvokeTemplates; - internal DynamicInvokeTemplateNode DynamicInvokeTemplate(MethodDesc method) - { - return _dynamicInvokeTemplates.GetOrAdd(method); - } - private NodeCache _shadowConcreteMethods; public IMethodNode ShadowConcreteMethod(MethodDesc method, bool isUnboxingStub = false) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs index 8939430acd0c6a..bc5454d4dae190 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReflectionInvokeMapNode.cs @@ -58,24 +58,36 @@ public static void AddDependenciesDueToReflectability(ref DependencyList depende if (factory.MetadataManager.HasReflectionInvokeStubForInvokableMethod(method)) { - MethodDesc canonInvokeStub = factory.MetadataManager.GetCanonicalReflectionInvokeStub(method); - if (canonInvokeStub.IsSharedByGenericInstantiations) + MethodDesc invokeStub = factory.MetadataManager.GetReflectionInvokeStub(method); + dependencies.Add(factory.MethodEntrypoint(invokeStub), "Reflection invoke"); + + var signature = method.Signature; + AddSignatureDependency(ref dependencies, factory, signature.ReturnType); + foreach (var parameterType in signature) + AddSignatureDependency(ref dependencies, factory, parameterType); + + static void AddSignatureDependency(ref DependencyList dependencies, NodeFactory factory, TypeDesc type) { - dependencies.Add(new DependencyListEntry(factory.DynamicInvokeTemplate(canonInvokeStub), "Reflection invoke")); + if (type.IsByRef) + type = ((ParameterizedType)type).ParameterType; + + // Skip tracking dependencies for primitive types. Assume that they are always present. + if (type.IsPrimitive || type.IsVoid) + return; + + dependencies.Add(factory.MaximallyConstructableType(type.ConvertToCanonForm(CanonicalFormKind.Specific)), "Reflection invoke"); } - else - dependencies.Add(new DependencyListEntry(factory.MethodEntrypoint(canonInvokeStub), "Reflection invoke")); } if (method.OwningType.IsValueType && !method.Signature.IsStatic) - dependencies.Add(new DependencyListEntry(factory.ExactCallableAddress(method, isUnboxingStub: true), "Reflection unboxing stub")); + dependencies.Add(factory.ExactCallableAddress(method, isUnboxingStub: true), "Reflection unboxing stub"); // If the method is defined in a different module than this one, a metadata token isn't known for performing the reference // Use a name/sig reference instead. if (!factory.MetadataManager.WillUseMetadataTokenToReferenceMethod(method)) { - dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition())), - "Non metadata-local method reference")); + dependencies.Add(factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method.GetTypicalMethodDefinition())), + "Non metadata-local method reference"); } if (method.HasInstantiation) @@ -84,8 +96,8 @@ public static void AddDependenciesDueToReflectability(ref DependencyList depende if (method.IsCanonicalMethod(CanonicalFormKind.Universal)) { - dependencies.Add(new DependencyListEntry(factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method)), - "UniversalCanon signature of method")); + dependencies.Add(factory.NativeLayout.PlacedSignatureVertex(factory.NativeLayout.MethodNameAndSignatureVertex(method)), + "UniversalCanon signature of method"); } else if (!method.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg() || method.IsAbstract) { @@ -189,17 +201,9 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false) if ((flags & InvokeTableFlags.NeedsParameterInterpretation) == 0) { - MethodDesc canonInvokeStubMethod = factory.MetadataManager.GetCanonicalReflectionInvokeStub(method); - if (canonInvokeStubMethod.IsSharedByGenericInstantiations) - { - vertex = writer.GetTuple(vertex, - writer.GetUnsignedConstant(((uint)factory.MetadataManager.DynamicInvokeTemplateData.GetIdForMethod(canonInvokeStubMethod, factory) << 1) | 1)); - } - else - { - vertex = writer.GetTuple(vertex, - writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(canonInvokeStubMethod)) << 1)); - } + MethodDesc invokeStub = factory.MetadataManager.GetReflectionInvokeStub(method); + vertex = writer.GetTuple(vertex, + writer.GetUnsignedConstant(_externalReferences.GetIndex(factory.MethodEntrypoint(invokeStub)))); } if ((flags & InvokeTableFlags.IsGenericMethod) != 0) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DynamicInvokeThunkGenerationPolicy.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DynamicInvokeThunkGenerationPolicy.cs index b67635a7731b50..f739bd7a68babb 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DynamicInvokeThunkGenerationPolicy.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DynamicInvokeThunkGenerationPolicy.cs @@ -33,14 +33,6 @@ public sealed class NoDynamicInvokeThunkGenerationPolicy : DynamicInvokeThunkGen /// public sealed class DefaultDynamicInvokeThunkGenerationPolicy : DynamicInvokeThunkGenerationPolicy { - public override bool HasStaticInvokeThunk(MethodDesc targetMethod) - { - // Place an upper limit on how many parameters a method can have to still get a static stub. - // From the past experience, methods taking 8000+ parameters get a stub that can hit various limitations - // in the codegen. - // Reflection invoke will still work, but will go through the calling convention converter. - - return targetMethod.Signature.Length <= 256; - } + public override bool HasStaticInvokeThunk(MethodDesc targetMethod) => true; } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs index 10e0f401258d2a..eff63e3867837b 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/GeneratingMetadataManager.cs @@ -210,13 +210,10 @@ record = transformed.GetTransformedTypeReference(definition); /// /// Gets a stub that can be used to reflection-invoke a method with a given signature. /// - public sealed override MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method) + public sealed override MethodDesc GetReflectionInvokeStub(MethodDesc method) { - // Get a generic method that can be used to invoke method with this shape. - var lookupSig = new DynamicInvokeMethodSignature(method.Signature); - MethodDesc thunk = _typeSystemContext.GetDynamicInvokeThunk(lookupSig); - - return InstantiateCanonicalDynamicInvokeMethodForMethod(thunk, method); + return _typeSystemContext.GetDynamicInvokeThunk(method.Signature, + !method.Signature.IsStatic && method.OwningType.IsValueType); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index c0e4920707fec3..67eb68090f3581 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -58,11 +58,9 @@ private readonly SortedSet _typeGVMEntries = new SortedSet(Comparer.Create((a, b) => TypeSystemComparer.Instance.Compare(a.AssociatedType, b.AssociatedType))); private readonly SortedSet _typesWithDelegateMarshalling = new SortedSet(TypeSystemComparer.Instance); private readonly SortedSet _typesWithStructMarshalling = new SortedSet(TypeSystemComparer.Instance); - private readonly SortedSet _dynamicInvokeTemplates = new SortedSet(TypeSystemComparer.Instance); private HashSet _templateMethodEntries = new HashSet(); internal NativeLayoutInfoNode NativeLayoutInfo { get; private set; } - internal DynamicInvokeTemplateDataNode DynamicInvokeTemplateData { get; private set; } public MetadataManager(CompilerTypeSystemContext typeSystemContext, MetadataBlockingPolicy blockingPolicy, ManifestResourceBlockingPolicy resourceBlockingPolicy, DynamicInvokeThunkGenerationPolicy dynamicInvokeThunkGenerationPolicy) @@ -106,9 +104,6 @@ public virtual void AddToReadyToRunHeader(ReadyToRunHeaderNode header, NodeFacto var cctorContextMapNode = new ClassConstructorContextMap(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.CCtorContextMap), cctorContextMapNode, cctorContextMapNode, cctorContextMapNode.EndSymbol); - DynamicInvokeTemplateData = new DynamicInvokeTemplateDataNode(commonFixupsTableNode); - header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.DynamicInvokeTemplateData), DynamicInvokeTemplateData, DynamicInvokeTemplateData, DynamicInvokeTemplateData.EndSymbol); - var invokeMapNode = new ReflectionInvokeMapNode(commonFixupsTableNode); header.Add(BlobIdToReadyToRunSection(ReflectionMapBlob.InvokeMap), invokeMapNode, invokeMapNode, invokeMapNode.EndSymbol); @@ -236,11 +231,6 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) _typesWithDelegateMarshalling.Add(delegateMarshallingDataNode.Type); } - if (obj is DynamicInvokeTemplateNode dynamicInvokeTemplate) - { - _dynamicInvokeTemplates.Add(dynamicInvokeTemplate.Method); - } - if (obj is NativeLayoutTemplateMethodSignatureVertexNode templateMethodEntry) { _templateMethodEntries.Add(templateMethodEntry); @@ -254,8 +244,7 @@ protected virtual void Graph_NewMarkedNode(DependencyNodeCore obj) /// public virtual bool IsReflectionInvokable(MethodDesc method) { - return Internal.IL.Stubs.DynamicInvokeMethodThunk.SupportsSignature(method.Signature) - && IsMethodSupportedInReflectionInvoke(method); + return IsMethodSupportedInReflectionInvoke(method); } public static bool IsMethodSupportedInReflectionInvoke(MethodDesc method) @@ -511,48 +500,7 @@ public bool HasReflectionInvokeStubForInvokableMethod(MethodDesc method) /// /// Gets a stub that can be used to reflection-invoke a method with a given signature. /// - public abstract MethodDesc GetCanonicalReflectionInvokeStub(MethodDesc method); - - /// - /// Compute the canonical instantiation of a dynamic invoke thunk needed to invoke a method - /// This algorithm is shared with the runtime, so if a thunk requires generic instantiation - /// to be used, it must match this algorithm, and cannot be different with different MetadataManagers - /// NOTE: This function may return null in cases where an exact instantiation does not exist. (Universal Generics) - /// - protected MethodDesc InstantiateCanonicalDynamicInvokeMethodForMethod(MethodDesc thunk, MethodDesc method) - { - if (thunk.Instantiation.Length == 0) - { - // nothing to instantiate - return thunk; - } - - MethodSignature sig = method.Signature; - TypeSystemContext context = method.Context; - - // - // Instantiate the generic thunk over the parameters and the return type of the target method - // - - TypeDesc[] instantiation = Internal.IL.Stubs.DynamicInvokeMethodThunk.GetThunkInstantiationForMethod(method); - Debug.Assert(thunk.Instantiation.Length == instantiation.Length); - - // Check if at least one of the instantiation arguments is a universal canonical type, and if so, we - // won't create a dynamic invoker instantiation. The arguments will be interpreted at runtime by the - // calling convention converter during the dynamic invocation - foreach (TypeDesc type in instantiation) - { - if (type.IsCanonicalSubtype(CanonicalFormKind.Universal)) - return null; - } - - // If the thunk ends up being shared code, return the canonical method body. - // The concrete dictionary for the thunk will be built at runtime and is not interesting for the compiler. - Instantiation canonInstantiation = context.ConvertInstantiationToCanonForm(new Instantiation(instantiation), CanonicalFormKind.Specific); - - MethodDesc instantiatedDynamicInvokeMethod = thunk.Context.GetInstantiatedMethod(thunk, canonInstantiation); - return instantiatedDynamicInvokeMethod; - } + public abstract MethodDesc GetReflectionInvokeStub(MethodDesc method); protected void EnsureMetadataGenerated(NodeFactory factory) { @@ -719,11 +667,6 @@ internal IEnumerable GetTypesWithConstructedEETypes() return _typesWithConstructedEETypesGenerated; } - internal IEnumerable GetDynamicInvokeTemplateMethods() - { - return _dynamicInvokeTemplates; - } - internal IEnumerable GetTemplateMethodEntries() { return _templateMethodEntries; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 230de4f8849551..302b49398ee3ed 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -37,6 +37,9 @@ IL\Stubs\DynamicInvokeMethodThunk.cs + + IL\Stubs\DynamicInvokeMethodThunk.Mangling.cs + IL\Stubs\DynamicInvokeMethodThunk.Sorting.cs @@ -370,7 +373,6 @@ - @@ -420,7 +422,6 @@ - diff --git a/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs b/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs index 98c3a87ae75d10..e2238ce7c687b8 100644 --- a/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs +++ b/src/libraries/System.Formats.Asn1/tests/Reader/ReadLength.cs @@ -27,7 +27,6 @@ private delegate Asn1Tag ReadTagAndLengthDelegate( [InlineData(4, 255, "0481FF")] [InlineData(2, 256, "02820100")] [InlineData(4, int.MaxValue, "04847FFFFFFF")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/72548", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static void MinimalPrimitiveLength(int tagValue, int length, string inputHex) { byte[] inputBytes = inputHex.HexToByteArray(); @@ -61,7 +60,6 @@ public static void ReadWithUnknownRuleSet(int invalidRuleSetValue) [InlineData("048201")] [InlineData("04830102")] [InlineData("0484010203")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/72548", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static void ReadWithInsufficientData(string inputHex) { byte[] inputData = inputHex.HexToByteArray(); @@ -97,7 +95,6 @@ public static void ReadWithInsufficientData(string inputHex) [InlineData("CER 5 byte spread", AsnEncodingRules.CER, "04850100000000")] [InlineData("DER 5 byte spread", AsnEncodingRules.DER, "04850100000000")] [InlineData("BER padded 5 byte spread", AsnEncodingRules.BER, "0486000100000000")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/72548", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static void InvalidLengths( string description, AsnEncodingRules rules, @@ -114,7 +111,6 @@ public static void InvalidLengths( [Theory] [InlineData(AsnEncodingRules.BER)] [InlineData(AsnEncodingRules.CER)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/72548", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static void IndefiniteLength(AsnEncodingRules ruleSet) { // SEQUENCE (indefinite) @@ -139,7 +135,6 @@ public static void IndefiniteLength(AsnEncodingRules ruleSet) [InlineData(0, "0483000000")] [InlineData(1, "048A00000000000000000001")] [InlineData(128, "049000000000000000000000000000000080")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/72548", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static void BerNonMinimalLength(int expectedLength, string inputHex) { byte[] inputData = inputHex.HexToByteArray(); @@ -161,7 +156,6 @@ public static void BerNonMinimalLength(int expectedLength, string inputHex) [InlineData(AsnEncodingRules.BER, 4, 0, 5, "0483000000" + "0500")] [InlineData(AsnEncodingRules.DER, 1, 1, 2, "0101" + "FF")] [InlineData(AsnEncodingRules.CER, 0x10, null, 2, "3080" + "0500" + "0000")] - [ActiveIssue("https://github.com/dotnet/runtime/issues/72548", typeof(PlatformDetection), nameof(PlatformDetection.IsNativeAot))] public static void ReadWithDataRemaining( AsnEncodingRules ruleSet, int tagValue,