Implement RuntimeHelpers.PrepareMethod/PrepareDelegate for CoreCLR (#16382)
authorJan Kotas <jkotas@microsoft.com>
Thu, 15 Feb 2018 06:19:16 +0000 (20:19 -1000)
committerGitHub <noreply@github.com>
Thu, 15 Feb 2018 06:19:16 +0000 (20:19 -1000)
* Implement RuntimeHelpers.PrepareMethod/PrepareDelegate for CoreCLR

CoreCLR implementation of this method triggers jiting of the given method only.
It does not walk a subset of callgraph to provide CER guarantees because of CERs
are not supported by CoreCLR.

Fixes #15522

src/mscorlib/Resources/Strings.resx
src/mscorlib/src/System/Runtime/CompilerServices/RuntimeHelpers.cs
src/vm/ecalllist.h
src/vm/reflectioninvocation.cpp
src/vm/reflectioninvocation.h

index b98553c..6448024 100644 (file)
   <data name="Arg_NullArgumentNullRef" xml:space="preserve">
     <value>The method was called with a null array argument.</value>
   </data>
-</root>
\ No newline at end of file
+  <data name="Argument_CannotPrepareAbstract" xml:space="preserve">
+    <value>Abstract methods cannot be prepared.</value>
+  </data>
+  <data name="Argument_InvalidGenericInstantiation" xml:space="preserve">
+    <value>The given generic instantiation was invalid.</value>
+  </data>
+</root>
index b340e4a..101f8c4 100644 (file)
@@ -86,18 +86,36 @@ namespace System.Runtime.CompilerServices
         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
         internal static extern void _CompileMethod(IRuntimeMethodInfo method);
 
-        public static void PrepareMethod(RuntimeMethodHandle method) { }
-        public static void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeHandle[] instantiation) { }
-        public static void PrepareContractedDelegate(Delegate d) { }
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        private static unsafe extern void _PrepareMethod(IRuntimeMethodInfo method, IntPtr* pInstantiation, int cInstantiation);
 
-        public static void PrepareDelegate(Delegate d)
+        public static void PrepareMethod(RuntimeMethodHandle method) 
         {
-            if (d == null)
+            unsafe
             {
-                throw new ArgumentNullException("d");
+                _PrepareMethod(method.GetMethodInfo(), null, 0);
             }
         }
 
+        public static void PrepareMethod(RuntimeMethodHandle method, RuntimeTypeHandle[] instantiation)
+        {
+            unsafe
+            {
+                int length;
+                IntPtr[] instantiationHandles = RuntimeTypeHandle.CopyRuntimeTypeHandles(instantiation, out length);
+                fixed (IntPtr* pInstantiation = instantiationHandles)
+                {
+                    _PrepareMethod(method.GetMethodInfo(), pInstantiation, length);
+                    GC.KeepAlive(instantiation);
+                }
+            }
+        }
+
+        public static void PrepareContractedDelegate(Delegate d) { }
+
+        [MethodImplAttribute(MethodImplOptions.InternalCall)]
+        public static extern void PrepareDelegate(Delegate d);
+
         [MethodImplAttribute(MethodImplOptions.InternalCall)]
         public static extern int GetHashCode(Object o);
 
index 86bc6a2..1259759 100644 (file)
@@ -1017,6 +1017,8 @@ FCFuncStart(gCompilerFuncs)
     FCFuncElement("_RunClassConstructor", ReflectionInvocation::RunClassConstructor)
     FCFuncElement("_RunModuleConstructor", ReflectionInvocation::RunModuleConstructor)
     QCFuncElement("_CompileMethod", ReflectionInvocation::CompileMethod)
+    FCFuncElement("_PrepareMethod", ReflectionInvocation::PrepareMethod)
+    FCFuncElement("PrepareDelegate", ReflectionInvocation::PrepareDelegate)
     FCFuncElement("ExecuteCodeWithGuaranteedCleanup", ReflectionInvocation::ExecuteCodeWithGuaranteedCleanup)
     FCFuncElement("GetHashCode", ObjectNative::GetHashCode)
     FCFuncElement("Equals", ObjectNative::Equals)
index 00556d8..12a3863 100644 (file)
@@ -1996,6 +1996,114 @@ FCIMPL1(void, ReflectionInvocation::RunModuleConstructor, ReflectModuleBaseObjec
 }
 FCIMPLEND
 
+static void PrepareMethodHelper(MethodDesc * pMD)
+{
+    CONTRACTL
+    {
+        THROWS;
+        GC_TRIGGERS;
+        MODE_ANY;
+    }
+    CONTRACTL_END;
+
+    GCX_PREEMP();
+
+    if (pMD->IsPointingToPrestub())
+        pMD->DoPrestub(NULL);
+
+    if (pMD->IsWrapperStub())
+    {
+        pMD = pMD->GetWrappedMethodDesc();
+        if (pMD->IsPointingToPrestub())
+            pMD->DoPrestub(NULL);
+    }
+}
+
+// This method triggers a given method to be jitted. CoreCLR implementation of this method triggers jiting of the given method only.
+// It does not walk a subset of callgraph to provide CER guarantees.
+FCIMPL3(void, ReflectionInvocation::PrepareMethod, ReflectMethodObject* pMethodUNSAFE, TypeHandle *pInstantiation, UINT32 cInstantiation)
+{
+    CONTRACTL {
+        FCALL_CHECK;
+        PRECONDITION(CheckPointer(pMethodUNSAFE, NULL_OK));
+        PRECONDITION(CheckPointer(pInstantiation, NULL_OK));
+    }
+    CONTRACTL_END;
+    
+    REFLECTMETHODREF refMethod = (REFLECTMETHODREF)ObjectToOBJECTREF(pMethodUNSAFE);
+    
+    HELPER_METHOD_FRAME_BEGIN_1(refMethod);
+
+    if (refMethod == NULL)
+        COMPlusThrow(kArgumentException, W("InvalidOperation_HandleIsNotInitialized"));
+
+    MethodDesc *pMD = refMethod->GetMethod();
+
+    if (pMD->IsAbstract())
+        COMPlusThrow(kArgumentException, W("Argument_CannotPrepareAbstract"));
+
+    MethodTable * pExactMT = pMD->GetMethodTable();
+    if (pInstantiation != NULL)
+    {
+        // We were handed an instantiation, check that the method expects it and the right number of types has been provided (the
+        // caller supplies one array containing the class instantiation immediately followed by the method instantiation).
+        if (cInstantiation != (pMD->GetNumGenericMethodArgs() + pMD->GetNumGenericClassArgs()))
+            COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
+
+        // Check we've got a reasonable looking instantiation.
+        if (!Generics::CheckInstantiation(Instantiation(pInstantiation, cInstantiation)))
+            COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
+        for (ULONG i = 0; i < cInstantiation; i++)
+            if (pInstantiation[i].ContainsGenericVariables())
+                COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
+
+        TypeHandle thExactType = ClassLoader::LoadGenericInstantiationThrowing(pMD->GetModule(),
+                                                                               pMD->GetMethodTable()->GetCl(),
+                                                                               Instantiation(pInstantiation, pMD->GetNumGenericClassArgs()));
+        pExactMT = thExactType.AsMethodTable();
+
+        pMD = MethodDesc::FindOrCreateAssociatedMethodDesc(pMD,
+                                                           pExactMT,
+                                                           FALSE,
+                                                           Instantiation(&pInstantiation[pMD->GetNumGenericClassArgs()], pMD->GetNumGenericMethodArgs()),
+                                                           FALSE);
+    }
+
+    if (pMD->ContainsGenericVariables())
+        COMPlusThrow(kArgumentException, W("Argument_InvalidGenericInstantiation"));
+
+    PrepareMethodHelper(pMD);
+
+    HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+// This method triggers target of a given method to be jitted. CoreCLR implementation of this method triggers jiting
+// of the given method only. It does not walk a subset of callgraph to provide CER guarantees.
+// In the case of a multi-cast delegate, we rely on the fact that each individual component
+// was prepared prior to the Combine.
+FCIMPL1(void, ReflectionInvocation::PrepareDelegate, Object* delegateUNSAFE)
+{
+    CONTRACTL {
+        FCALL_CHECK;
+        PRECONDITION(CheckPointer(delegateUNSAFE, NULL_OK));
+    }
+    CONTRACTL_END;
+    
+    if (delegateUNSAFE == NULL)
+        return;
+
+    OBJECTREF delegate = ObjectToOBJECTREF(delegateUNSAFE);
+    HELPER_METHOD_FRAME_BEGIN_1(delegate);
+
+    MethodDesc *pMD = COMDelegate::GetMethodDesc(delegate);
+
+    PrepareMethodHelper(pMD);
+
+    HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
 // This method checks to see if there is sufficient stack to execute the average Framework method.
 // If there is not, then it throws System.InsufficientExecutionStackException. The limit for each
 // thread is precomputed when the thread is created.
index 6a183b1..80b861f 100644 (file)
@@ -51,6 +51,8 @@ public:
 
     static FCDECL1(void, RunClassConstructor, ReflectClassBaseObject *pTypeUNSAFE);
     static FCDECL1(void, RunModuleConstructor, ReflectModuleBaseObject *pModuleUNSAFE);
+    static FCDECL3(void, PrepareMethod, ReflectMethodObject* pMethodUNSAFE, TypeHandle *pInstantiation, UINT32 cInstantiation);
+    static FCDECL1(void, PrepareDelegate, Object* delegateUNSAFE);
     static FCDECL1(void, PrepareContractedDelegate, Object* delegateUNSAFE);
     static FCDECL0(void, ProbeForSufficientStack);    
     static FCDECL0(void, EnsureSufficientExecutionStack);