summaryrefslogtreecommitdiff
path: root/src/vm/genmeth.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/genmeth.cpp')
-rw-r--r--src/vm/genmeth.cpp1789
1 files changed, 1789 insertions, 0 deletions
diff --git a/src/vm/genmeth.cpp b/src/vm/genmeth.cpp
new file mode 100644
index 0000000000..c50f806c60
--- /dev/null
+++ b/src/vm/genmeth.cpp
@@ -0,0 +1,1789 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+// File: genmeth.cpp
+//
+// Most functionality for generic methods is put here
+//
+
+
+
+#include "common.h"
+#include "method.hpp"
+#include "field.h"
+#include "eeconfig.h"
+#include "perfcounters.h"
+#include "crst.h"
+#include "generics.h"
+#include "genericdict.h"
+#include "instmethhash.h"
+#include "typestring.h"
+#include "typedesc.h"
+#include "comdelegate.h"
+
+// Instantiated generic methods
+//
+// Method descriptors for instantiated generic methods are allocated on demand and inserted
+// into the InstMethodHashTable for the LoaderModule of the descriptor. (See ceeload.h for more
+// information about loader modules).
+//
+// For non-shared instantiations, entering the prestub for such a method descriptor causes the method to
+// be JIT-compiled, specialized to that instantiation.
+//
+// For shared instantiations, entering the prestub generates a piece of stub code that passes the
+// method descriptor as an extra argument and then jumps to code shared between compatible
+// instantiations. This code has its own method descriptor whose instantiation is *canonical*
+// (with reference-type type parameters replaced by Object).
+//
+// Thus for example the shared method descriptor for m<object> is different to the
+// exact-instantiation method descriptor for m<object>.
+//
+// Complete example:
+//
+// class C<T> { public void m<S>(S x, T y) { ... } }
+//
+// Suppose that code sharing is turned on.
+//
+// Upon compiling calls to C<string>.m<string>, C<string>.m<Type>, C<Type>.m<string> and C<Type>.m<Type>
+
+// Given a generic method descriptor and an instantiation, create a new instantiated method
+// descriptor and chain it into the list attached to the generic method descriptor
+//
+// pMT is the owner method table. If looking for a shared MD this should be
+// the MT for the shared class.
+//
+// pGenericMD is the generic method descriptor (owner may be instantiated)
+// pWrappedMD is the corresponding shared md for use when creating stubs
+// nGenericMethodArgs/genericMethodArgs is the instantiation
+// getWrappedCode=TRUE if you want a shared instantiated md whose code expects an extra argument. In this
+// case pWrappedMD should be NULL.
+//
+// The result is put in ppMD
+//
+// If getWrappedCode. In thise case the genericMethodArgs
+// should be the normalized representative genericMethodArgs (see typehandle.h)
+//
+
+
+// Helper method that creates a method-desc off a template method desc
+static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
+ MethodTable *pMT,
+ MethodDesc *pTemplateMD,
+ DWORD classification,
+ BOOL fNativeCodeSlot,
+ BOOL fComPlusCallInfo,
+ AllocMemTracker *pamTracker)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ PRECONDITION(CheckPointer(pAllocator));
+ PRECONDITION(CheckPointer(pMT));
+ PRECONDITION(CheckPointer(pTemplateMD));
+ PRECONDITION(pTemplateMD->IsRestored());
+ PRECONDITION(pMT->IsRestored_NoLogging());
+ PRECONDITION(pTemplateMD->GetMethodTable()->GetCanonicalMethodTable() == pMT->GetCanonicalMethodTable());
+ }
+ CONTRACTL_END
+
+ mdMethodDef token = pTemplateMD->GetMemberDef();
+
+ // Create a singleton chunk for the method desc
+ MethodDescChunk *pChunk =
+ MethodDescChunk::CreateChunk(pAllocator->GetHighFrequencyHeap(),
+ 1,
+ classification,
+ TRUE /* fNonVtableSlot*/,
+ fNativeCodeSlot,
+ fComPlusCallInfo,
+ pMT,
+ pamTracker);
+
+ // Now initialize the MDesc at the single method descriptor in
+ // the new chunk
+ MethodDesc *pMD = pChunk->GetFirstMethodDesc();
+
+ //We copy over the flags one by one. This is fragile w.r.t. adding
+ // new flags, but other techniques are also fragile. <NICE>We should move
+ // to using constructors on MethodDesc</NICE>
+ if (pTemplateMD->IsStatic())
+ {
+ pMD->SetStatic();
+ }
+ if (pTemplateMD->IsNotInline())
+ {
+ pMD->SetNotInline(true);
+ }
+ if (pTemplateMD->IsSynchronized())
+ {
+ pMD->SetSynchronized();
+ }
+ if (pTemplateMD->RequiresLinktimeCheck())
+ {
+ pMD->SetRequiresLinktimeCheck();
+ }
+ if (pTemplateMD->RequiresInheritanceCheck())
+ {
+ pMD->SetRequiresInheritanceCheck();
+ }
+ if (pTemplateMD->ParentRequiresInheritanceCheck())
+ {
+ pMD->SetParentRequiresInheritanceCheck();
+ }
+ if (pTemplateMD->IsInterceptedForDeclSecurity())
+ {
+ pMD->SetInterceptedForDeclSecurity();
+ }
+ if (pTemplateMD->IsInterceptedForDeclSecurityCASDemandsOnly())
+ {
+ pMD->SetInterceptedForDeclSecurityCASDemandsOnly();
+ }
+ if (pTemplateMD->HasCriticalTransparentInfo())
+ {
+ pMD->SetCriticalTransparentInfo(pTemplateMD->IsCritical(), pTemplateMD->IsTreatAsSafe());
+ }
+ if (pTemplateMD->RequiresLinkTimeCheckHostProtectionOnly())
+ {
+ pMD->SetRequiresLinkTimeCheckHostProtectionOnly();
+ }
+
+ pMD->SetMemberDef(token);
+ pMD->SetSlot(pTemplateMD->GetSlot());
+
+#ifdef _DEBUG
+ pMD->m_pszDebugMethodName = pTemplateMD->m_pszDebugMethodName;
+ //<NICE> more info here</NICE>
+ pMD->m_pszDebugMethodSignature = "<generic method signature>";
+ pMD->m_pszDebugClassName = "<generic method class name>";
+ pMD->m_pszDebugMethodName = "<generic method name>";
+ pMD->m_pDebugMethodTable.SetValue(pMT);
+#endif // _DEBUG
+
+ return pMD;
+}
+
+//
+// The following methods map between tightly bound boxing and unboxing MethodDesc.
+// We always layout boxing and unboxing MethodDescs next to each other in same
+// MethodDescChunk. It allows us to avoid brute-force iteration over all methods
+// on the type to perform the mapping.
+//
+
+//
+// Find matching tightly-bound methoddesc
+//
+static MethodDesc * FindTightlyBoundWrappedMethodDesc(MethodDesc * pMD)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END
+
+ if (pMD->IsUnboxingStub() && pMD->GetClassification() == mcInstantiated)
+ pMD = pMD->AsInstantiatedMethodDesc()->IMD_GetWrappedMethodDesc();
+
+ // Find matching MethodDesc in the MethodTable
+ if (!pMD->IsTightlyBoundToMethodTable())
+ pMD = pMD->GetCanonicalMethodTable()->GetParallelMethodDesc(pMD);
+ _ASSERTE(pMD->IsTightlyBoundToMethodTable());
+
+ // Real MethodDesc immediately follows unboxing stub
+ if (pMD->IsUnboxingStub())
+ pMD = MethodTable::IntroducedMethodIterator::GetNext(pMD);
+ _ASSERTE(!pMD->IsUnboxingStub());
+
+ return pMD;
+}
+
+//
+// Find matching tightly-bound unboxing stub if there is one
+//
+static MethodDesc * FindTightlyBoundUnboxingStub(MethodDesc * pMD)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END
+
+ // Find matching MethodDesc in the MethodTable
+ if (!pMD->IsTightlyBoundToMethodTable())
+ pMD = pMD->GetCanonicalMethodTable()->GetParallelMethodDesc(pMD);
+ _ASSERTE(pMD->IsTightlyBoundToMethodTable());
+
+ // We are done if we have unboxing stub already
+ if (pMD->IsUnboxingStub())
+ return pMD;
+
+ //
+ // Unboxing stub immediately precedes real methoddesc
+ //
+ MethodDesc * pCurMD = pMD->GetMethodDescChunk()->GetFirstMethodDesc();
+
+ if (pCurMD == pMD)
+ return NULL;
+
+ for (;;)
+ {
+ MethodDesc * pNextMD = MethodTable::IntroducedMethodIterator::GetNext(pCurMD);
+ if (pNextMD == pMD)
+ break;
+ pCurMD = pNextMD;
+ }
+
+ return pCurMD->IsUnboxingStub() ? pCurMD : NULL;
+}
+
+#ifdef _DEBUG
+//
+// Alternative brute-force implementation of FindTightlyBoundWrappedMethodDesc for debug-only check.
+//
+// Please note that this does not do the same up-front checks as the non-debug version to
+// see whether or not the input pMD is even an unboxing stub in the first place.
+//
+static MethodDesc * FindTightlyBoundWrappedMethodDesc_DEBUG(MethodDesc * pMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END
+
+ mdMethodDef methodDef = pMD->GetMemberDef();
+ Module *pModule = pMD->GetModule();
+
+ MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
+ it.MoveToEnd();
+ for (; it.IsValid(); it.Prev()) {
+ if (!it.IsVirtual()) {
+ // Get the MethodDesc for current method
+ MethodDesc* pCurMethod = it.GetMethodDesc();
+
+ if (pCurMethod && !pCurMethod->IsUnboxingStub()) {
+ if ((pCurMethod->GetMemberDef() == methodDef) &&
+ (pCurMethod->GetModule() == pModule))
+ {
+ return pCurMethod;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+//
+// Alternative brute-force implementation of FindTightlyBoundUnboxingStub for debug-only check
+//
+// Please note that this does not do the same up-front checks as the non-debug version to
+// see whether or not the input pMD even qualifies to have a corresponding unboxing stub.
+//
+static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(pMD));
+ }
+ CONTRACTL_END
+
+ mdMethodDef methodDef = pMD->GetMemberDef();
+ Module *pModule = pMD->GetModule();
+
+ MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
+ it.MoveToEnd();
+ for (; it.IsValid(); it.Prev()) {
+ if (it.IsVirtual()) {
+ MethodDesc* pCurMethod = it.GetMethodDesc();
+ if (pCurMethod && pCurMethod->IsUnboxingStub()) {
+ if ((pCurMethod->GetMemberDef() == methodDef) &&
+ (pCurMethod->GetModule() == pModule)) {
+ return pCurMethod;
+ }
+ }
+ }
+ }
+ return NULL;
+}
+#endif // _DEBUG
+
+/* static */
+InstantiatedMethodDesc *
+InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT,
+ MethodDesc* pGenericMDescInRepMT,
+ MethodDesc* pWrappedMD,
+ Instantiation methodInst,
+ BOOL getWrappedCode)
+{
+ CONTRACT(InstantiatedMethodDesc*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ PRECONDITION(CheckPointer(pExactMT));
+ PRECONDITION(CheckPointer(pGenericMDescInRepMT));
+ PRECONDITION(pGenericMDescInRepMT->IsRestored());
+ PRECONDITION(pWrappedMD == NULL || pWrappedMD->IsRestored());
+ PRECONDITION(methodInst.IsEmpty() || pGenericMDescInRepMT->IsGenericMethodDefinition());
+ PRECONDITION(methodInst.GetNumArgs() == pGenericMDescInRepMT->GetNumGenericMethodArgs());
+ POSTCONDITION(CheckPointer(RETVAL));
+ POSTCONDITION(RETVAL->IsRestored());
+ POSTCONDITION(getWrappedCode == RETVAL->IsSharedByGenericInstantiations());
+ POSTCONDITION(methodInst.IsEmpty() || RETVAL->HasMethodInstantiation());
+ }
+ CONTRACT_END;
+
+ // All instantiated method descs live off the RepMT for the
+ // instantiated class they live in.
+ INDEBUG(MethodTable * pCanonMT = pExactMT->GetCanonicalMethodTable();)
+
+ _ASSERTE(pGenericMDescInRepMT->GetMethodTable() == pCanonMT);
+
+ if (getWrappedCode)
+ {
+ _ASSERTE(pWrappedMD == NULL);
+ _ASSERTE(pExactMT->IsCanonicalMethodTable());
+ _ASSERTE(pCanonMT == pExactMT);
+ _ASSERTE(pExactMT->IsSharedByGenericInstantiations() || ClassLoader::IsSharableInstantiation(methodInst));
+
+ }
+
+ InstantiatedMethodDesc *pNewMD;
+ //@todo : move this into the domain
+ Module * pExactMDLoaderModule = ClassLoader::ComputeLoaderModule(pExactMT, pGenericMDescInRepMT->GetMemberDef(), methodInst);
+
+ LoaderAllocator * pAllocator = pExactMDLoaderModule->GetLoaderAllocator();
+
+ // Create LoaderAllocator to LoaderAllocator links for members of the instantiations of this method
+ pAllocator->EnsureInstantiation(pExactMT->GetLoaderModule(), pExactMT->GetInstantiation());
+ pAllocator->EnsureInstantiation(pGenericMDescInRepMT->GetLoaderModule(), methodInst);
+
+ {
+ // Acquire crst to prevent tripping up other threads searching in the same hashtable
+ CrstHolder ch(&pExactMDLoaderModule->m_InstMethodHashTableCrst);
+
+ // Check whether another thread beat us to it!
+ pNewMD = FindLoadedInstantiatedMethodDesc(pExactMT,
+ pGenericMDescInRepMT->GetMemberDef(),
+ methodInst,
+ getWrappedCode);
+
+ // Crst goes out of scope here
+ // We don't need to hold the crst while we build the MethodDesc, but we reacquire it later
+ }
+
+#ifdef FEATURE_PREJIT
+ // This section is the search for an instantiation in the various NGEN images
+ // where we may have precompiled the instantiation.
+ // Never use dyn link zap items during ngen time. We will independently decide later
+ // whether we want to store the item into ngen image or not.
+ if ((pNewMD == NULL) && !IsCompilationProcess())
+ {
+ // We need to know which domain the item must live in (DomainNeutral or AppDomain)
+ // <TODO>We can't use pDomain because at NGEN
+ // time this may not be accurate - this must be cleaned up as part of getting
+ // rid of GetLoaderModule() altogether.... </TODO>
+ BaseDomain * pRequiredDomain = BaseDomain::ComputeBaseDomain(
+ pExactMT->GetDomain(),
+ pExactMT->GetInstantiation(),
+ methodInst);
+
+ // Next look in each ngen'ed image in turn
+ AppDomain::AssemblyIterator assemblyIterator = GetAppDomain()->IterateAssembliesEx((AssemblyIterationFlags)(
+ kIncludeLoaded |
+ (pExactMT->IsIntrospectionOnly() ? kIncludeIntrospection : kIncludeExecution)));
+ CollectibleAssemblyHolder<DomainAssembly *> pDomainAssembly;
+ while ((pNewMD == NULL) && assemblyIterator.Next(pDomainAssembly.This()))
+ {
+ // Make sure the domain of the NGEN'd images associated with the assembly matches...
+ // No need to check this when NGEN'ing
+ CollectibleAssemblyHolder<Assembly *> pAssembly = pDomainAssembly->GetLoadedAssembly();
+ if (GetAppDomain()->IsCompilationDomain() || (pAssembly->GetDomain() == pRequiredDomain))
+ {
+ DomainAssembly::ModuleIterator i = pDomainAssembly->IterateModules(kModIterIncludeLoaded);
+ while ((pNewMD == NULL) && i.Next())
+ {
+ Module * pModule = i.GetLoadedModule();
+ if (!pModule->HasNativeImage())
+ continue;
+ _ASSERTE(!pModule->IsCollectible());
+
+ // We don't need to track references to normal (non-collectible) assemblies
+ pNewMD = (InstantiatedMethodDesc *)pModule->GetInstMethodHashTable()->FindMethodDesc(
+ TypeHandle(pExactMT),
+ pGenericMDescInRepMT->GetMemberDef(),
+ FALSE /* not forceBoxedEntryPoint */,
+ methodInst,
+ getWrappedCode);
+ if (pNewMD == NULL)
+ continue;
+#ifdef _DEBUG
+#ifndef DACCESS_COMPILE
+ if (LoggingOn(LF_CLASSLOADER, LL_INFO10000))
+ {
+ StackSString methodName;
+ pNewMD->CheckRestore();
+ TypeString::AppendMethodDebug(methodName, pNewMD);
+ LOG((LF_CLASSLOADER, LL_INFO10000, "Found method %S in non-preferred zap module %S\n", methodName.GetUnicode(), pModule->GetPath().GetUnicode()));
+ }
+#endif //!DACCESS_COMPILE
+#endif //_DEBUG
+ }
+ }
+ else
+ {
+ LOG((LF_CLASSLOADER, LL_INFO10000, "Skipping assembly %S due to domain mismatch when searching for prejitted instantiation\n",
+ pAssembly->GetDebugName()));
+ }
+ }
+ }
+#endif // FEATURE_PREJIT
+
+ if (pNewMD != NULL)
+ {
+ pNewMD->CheckRestore();
+ }
+ else
+ {
+ TypeHandle *pInstOrPerInstInfo = NULL;
+ DictionaryLayout *pDL = NULL;
+ DWORD infoSize = 0;
+ IBCLoggerAwareAllocMemTracker amt;
+
+ if (!methodInst.IsEmpty())
+ {
+ if (pWrappedMD)
+ {
+ if (pWrappedMD->IsSharedByGenericMethodInstantiations())
+ {
+ pDL = pWrappedMD->AsInstantiatedMethodDesc()->m_pDictLayout;
+ }
+ }
+ else if (getWrappedCode)
+ {
+ // 4 seems like a good number
+ pDL = DictionaryLayout::Allocate(4, pAllocator, &amt);
+#ifdef _DEBUG
+ {
+ SString name;
+ TypeString::AppendMethodDebug(name, pGenericMDescInRepMT);
+ LOG((LF_JIT, LL_INFO1000, "GENERICS: Created new dictionary layout for dictionary of size %d for %S\n",
+ DictionaryLayout::GetFirstDictionaryBucketSize(pGenericMDescInRepMT->GetNumGenericMethodArgs(), pDL), name.GetUnicode()));
+ }
+#endif // _DEBUG
+ }
+
+ // Allocate space for the instantiation and dictionary
+ infoSize = DictionaryLayout::GetFirstDictionaryBucketSize(methodInst.GetNumArgs(), pDL);
+ pInstOrPerInstInfo = (TypeHandle *) (void*) amt.Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(infoSize)));
+ for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
+ pInstOrPerInstInfo[i] = methodInst[i];
+ }
+
+ BOOL forComInterop = FALSE;
+#ifdef FEATURE_COMINTEROP
+ if (pExactMT->IsProjectedFromWinRT())
+ {
+ forComInterop = (pExactMT->IsInterface() || (pExactMT->IsDelegate() && COMDelegate::IsDelegateInvokeMethod(pGenericMDescInRepMT)));
+ }
+ else
+ {
+ // redirected interfaces and delegates also support interop
+ forComInterop = (pExactMT->IsWinRTRedirectedInterface(TypeHandle::Interop_ManagedToNative) ||
+ (pExactMT->IsWinRTRedirectedDelegate() && COMDelegate::IsDelegateInvokeMethod(pGenericMDescInRepMT)));
+ }
+#endif // FEATURE_COMINTEROP
+
+ // Create a new singleton chunk for the new instantiated method descriptor
+ // Notice that we've passed in the method table pointer; this gets
+ // used in some of the subsequent setup methods for method descs.
+ //
+ pNewMD = (InstantiatedMethodDesc*) (CreateMethodDesc(pAllocator,
+ pExactMT,
+ pGenericMDescInRepMT,
+ mcInstantiated,
+ !pWrappedMD, // This is pesimistic estimate for fNativeCodeSlot
+ forComInterop,
+ &amt));
+
+ // Initialize the MD the way it needs to be
+ if (pWrappedMD)
+ {
+ pNewMD->SetupWrapperStubWithInstantiations(pWrappedMD, methodInst.GetNumArgs(), pInstOrPerInstInfo);
+ _ASSERTE(pNewMD->IsInstantiatingStub());
+ }
+ else if (getWrappedCode)
+ {
+ pNewMD->SetupSharedMethodInstantiation(methodInst.GetNumArgs(), pInstOrPerInstInfo, pDL);
+ _ASSERTE(!pNewMD->IsInstantiatingStub());
+ }
+ else
+ {
+ pNewMD->SetupUnsharedMethodInstantiation(methodInst.GetNumArgs(), pInstOrPerInstInfo);
+ }
+
+ // Check that whichever field holds the inst. got setup correctly
+ _ASSERTE((PVOID)pNewMD->GetMethodInstantiation().GetRawArgs() == (PVOID)pInstOrPerInstInfo);
+
+ pNewMD->SetTemporaryEntryPoint(pAllocator, &amt);
+
+ {
+ // The canonical instantiation is exempt from constraint checks. It's used as the basis
+ // for all other reference instantiations so we can't not load it. The Canon type is
+ // not visible to users so it can't be abused.
+
+ BOOL fExempt =
+ TypeHandle::IsCanonicalSubtypeInstantiation(methodInst) ||
+ TypeHandle::IsCanonicalSubtypeInstantiation(pNewMD->GetClassInstantiation());
+
+ if (!fExempt)
+ {
+ pNewMD->SatisfiesMethodConstraints(TypeHandle(pExactMT), TRUE);
+ }
+ }
+
+ // OK, now we have a candidate MethodDesc.
+ {
+ CrstHolder ch(&pExactMDLoaderModule->m_InstMethodHashTableCrst);
+
+ // We checked before, but make sure again that another thread didn't beat us to it!
+ InstantiatedMethodDesc *pOldMD = FindLoadedInstantiatedMethodDesc(pExactMT,
+ pGenericMDescInRepMT->GetMemberDef(),
+ methodInst,
+ getWrappedCode);
+
+ if (pOldMD == NULL)
+ {
+ // No one else got there first, our MethodDesc wins.
+ amt.SuppressRelease();
+
+#ifdef _DEBUG
+ SString name(SString::Utf8);
+ TypeString::AppendMethodDebug(name, pNewMD);
+ StackScratchBuffer buff;
+ const char* pDebugNameUTF8 = name.GetUTF8(buff);
+ const char* verb = "Created";
+ if (pWrappedMD)
+ LOG((LF_CLASSLOADER, LL_INFO1000,
+ "GENERICS: %s instantiating-stub method desc %s with dictionary size %d\n",
+ verb, pDebugNameUTF8, infoSize));
+ else
+ LOG((LF_CLASSLOADER, LL_INFO1000,
+ "GENERICS: %s instantiated method desc %s\n",
+ verb, pDebugNameUTF8));
+
+ S_SIZE_T safeLen = S_SIZE_T(strlen(pDebugNameUTF8))+S_SIZE_T(1);
+ if(safeLen.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW);
+
+ size_t len = safeLen.Value();
+ pNewMD->m_pszDebugMethodName = (char*) (void*)pAllocator->GetLowFrequencyHeap()->AllocMem(safeLen);
+ _ASSERTE(pNewMD->m_pszDebugMethodName);
+ strcpy_s((char *) pNewMD->m_pszDebugMethodName, len, pDebugNameUTF8);
+ pNewMD->m_pszDebugClassName = pExactMT->GetDebugClassName();
+ pNewMD->m_pszDebugMethodSignature = (LPUTF8)pNewMD->m_pszDebugMethodName;
+#endif // _DEBUG
+
+ // Generic methods can't be varargs. code:MethodTableBuilder::ValidateMethods should have checked it.
+ _ASSERTE(!pNewMD->IsVarArg());
+
+ // Verify that we are not creating redundant MethodDescs
+ _ASSERTE(!pNewMD->IsTightlyBoundToMethodTable());
+
+ // The method desc is fully set up; now add to the table
+ InstMethodHashTable* pTable = pExactMDLoaderModule->GetInstMethodHashTable();
+ pTable->InsertMethodDesc(pNewMD);
+ }
+ else
+ pNewMD = pOldMD;
+ // CrstHolder goes out of scope here
+ }
+
+ }
+
+ RETURN pNewMD;
+}
+
+// Calling this method is equivalent to
+// FindOrCreateAssociatedMethodDesc(pCanonicalMD, pExactMT, FALSE, Instantiation(), FALSE, TRUE)
+// except that it also creates InstantiatedMethodDescs based on shared class methods. This is
+// convenient for interop where, unlike ordinary managed methods, marshaling stubs for say Foo<string>
+// and Foo<object> look very different and need separate representation.
+InstantiatedMethodDesc*
+InstantiatedMethodDesc::FindOrCreateExactClassMethod(MethodTable *pExactMT,
+ MethodDesc *pCanonicalMD)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(!pExactMT->IsSharedByGenericInstantiations());
+ PRECONDITION(pCanonicalMD->IsSharedByGenericInstantiations());
+ }
+ CONTRACTL_END;
+
+ InstantiatedMethodDesc *pInstMD = FindLoadedInstantiatedMethodDesc(pExactMT,
+ pCanonicalMD->GetMemberDef(),
+ Instantiation(),
+ FALSE);
+
+ if (pInstMD == NULL)
+ {
+ // create a new MD if not found
+ pInstMD = NewInstantiatedMethodDesc(pExactMT,
+ pCanonicalMD,
+ pCanonicalMD,
+ Instantiation(),
+ FALSE);
+ }
+
+ return pInstMD;
+}
+
+// N.B. it is not guarantee that the returned InstantiatedMethodDesc is restored.
+// It is the caller's responsibility to call CheckRestore on the returned value.
+/* static */
+InstantiatedMethodDesc*
+InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(MethodTable *pExactOrRepMT,
+ mdMethodDef methodDef,
+ Instantiation methodInst,
+ BOOL getWrappedCode)
+{
+ CONTRACT(InstantiatedMethodDesc *)
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ PRECONDITION(CheckPointer(pExactOrRepMT));
+
+ // All wrapped method descriptors (except BoxedEntryPointStubs, which don't use this path) are
+ // canonical and exhibit some kind of code sharing.
+ PRECONDITION(!getWrappedCode || pExactOrRepMT->IsCanonicalMethodTable());
+ PRECONDITION(!getWrappedCode || pExactOrRepMT->IsSharedByGenericInstantiations() || ClassLoader::IsSharableInstantiation(methodInst));
+
+ // Unboxing stubs are dealt with separately in FindOrCreateAssociatedMethodDesc. This should
+ // probably be streamlined...
+ POSTCONDITION(!RETVAL || !RETVAL->IsUnboxingStub());
+
+ // All wrapped method descriptors (except BoxedEntryPointStubs, which don't use this path) take an inst arg.
+ // The only ones that don't should have been found in the type's meth table.
+ POSTCONDITION(!getWrappedCode || !RETVAL || !RETVAL->IsRestored() || RETVAL->RequiresInstArg());
+ }
+ CONTRACT_END
+
+
+ // First look in the table for the runtime loader module in case someone created it before any
+ // zap modules got loaded
+ Module *pLoaderModule = ClassLoader::ComputeLoaderModule(pExactOrRepMT, methodDef, methodInst);
+
+ InstMethodHashTable* pTable = pLoaderModule->GetInstMethodHashTable();
+ MethodDesc *resultMD = pTable->FindMethodDesc(TypeHandle(pExactOrRepMT),
+ methodDef,
+ FALSE /* not forceBoxedEntryPoint */,
+ methodInst,
+ getWrappedCode);
+
+ if (resultMD != NULL)
+ RETURN((InstantiatedMethodDesc*) resultMD);
+
+#ifdef FEATURE_PREJIT
+ // Next look in the preferred zap module
+ Module *pPreferredZapModule = Module::ComputePreferredZapModule(pExactOrRepMT->GetModule(),
+ pExactOrRepMT->GetInstantiation(),
+ methodInst);
+ if (pPreferredZapModule->HasNativeImage())
+ {
+ resultMD = pPreferredZapModule->GetInstMethodHashTable()->FindMethodDesc(TypeHandle(pExactOrRepMT),
+ methodDef,
+ FALSE /* not forceBoxedEntryPoint */,
+ methodInst,
+ getWrappedCode);
+
+ if (resultMD != NULL)
+ RETURN((InstantiatedMethodDesc*) resultMD);
+ }
+#endif // FEATURE_PREJIT
+
+ RETURN(NULL);
+}
+
+
+// Given a method descriptor, find (or create) an instantiated
+// method descriptor or BoxedEntryPointStub associated with that method
+// and a particular instantiation of any generic method arguments. Also check
+// the method instantiation is valid.
+//
+// This routine also works for non-generic methods - it will be fast
+// in most cases. In this case nothing in particular
+// occurs except for static methods in shared generic classes, where an
+// instantiating stub is needed.
+//
+// The generic parameters provided are only those for the generic method.
+// pExactMT should be used to specify any class parameters.
+//
+// Unboxing stubs
+// --------------
+//
+// These are required to provide callable addresses with a uniform calling convention
+// for all methods on a boxed value class object. There are a wide range of possible
+// methods:
+// 1 virtual, non-generic instance methods
+// 2 non-virtual, non-generic instance methods
+// 3 virtual, generic instance methods
+// 4 non-virtual, generic instance methods
+// There is no substantial difference between case 3 and case 4: the only times
+// when BoxedEntryPointStubs are used for non-virtual methods are when calling a delegate or
+// making a reflection call.
+//
+// The only substantial difference between 1 and 2 is that the stubs are stored in
+// different places - we are forced to create the BoxedEntryPointStubs for (1) at class
+// creation time (they form part of the vtable and dispatch maps). Hence these
+// stubs are "owned" by method tables. We store all other stubs in the AssociatedMethTable.
+//
+// Unboxing stubs and generics
+// ---------------------------
+//
+// Generics code sharing complicates matters. The typical cases are where the struct
+// is in a shared-codegenerics struct such as
+//
+// struct Pair<string,object>
+//
+// which shares code with other types such as Pair<object,object>. All the code that ends up
+// being run for all the methods in such a struct takes an instantiation parameter, i.e.
+// is RequiresInstArg(), a non-uniform calling convention. We obviously can't give these out as
+// targets of delegate calls. Hence we have to wrap this shared code in various stubs in
+// order to get the right type context parameter provided to the shared code.
+//
+// Unboxing stubs on shared-code generic structs, e.g. Pair<object,string>,
+// acquire the class-portion of their type context from the "this" pointer.
+//
+// Thus there are two flavours of BoxedEntryPointStubs:
+//
+// - Methods that are not themselves generic:
+//
+// These wrap possibly-shared code (hence allowInstParam == TRUE).
+//
+// These directly call the possible-shared code for the instance method. This code
+// can have the following calling conventions:
+// - RequiresMethodTableInstArg() (if pMT->SharedByGenericInstantiations())
+// - Uniform (if !pMT->SharedByGenericInstantiations())
+//
+// Thus if the code they are
+//
+// - Methods that are themselves generic:
+//
+// These wrap unshared code (hence allowInstParam == FALSE):
+//
+// These are always invoked by slow paths (how often do you use a generic method in a struct?),
+// such as JIT_VirtualFunctionPointer or a reflection call. These paths eventually
+// use FindOrCreateAssociatedMethodDesc to piece together the exact instantiation provided by the "this"
+// pointer with the exact instantiation provided by the wrapped method pointer.
+//
+// These call a stub for the instance method which provides the instantiation
+// context, possibly in turn calling further shared code. This stub will
+// always be !RequiresInstArg()
+//
+// If the method being called is aMethod calls via BoxedEntryPointStubs
+//
+// Remotable methods
+// -----------------
+//
+// Remoting has high requirements for method descs passed to it (i.e. the method desc that represents the client "view" of the
+// method to be called on the real server object). Since the identity of the method call is serialized and passed on the wire before
+// be resolved into the real target method on the server remoting needs to be able to extract exact instantiation information from
+// its inputs (a method desc and a this pointer).
+//
+// To that end generic methods should always be passed via an instantiating stub (i.e. set allowInstParam to FALSE when calling
+// FindOrCreateAssociatedMethodDesc).
+//
+// There's a more subtle problem though. If the client method call is via a non-generic method on a generic interface we won't have
+// enough information to serialize the call. That's because such methods don't have instantiated method descs by default (these are
+// weighty structures and most of the runtime would never use the extra information). The this pointer doesn't help provide the
+// additional information in this case (consider the case of a class that implements both IFoo<String> and IFoo<Random>).
+//
+// So instead we create instantiated interface method descs on demand (i.e. during stub-based interface dispatch). Setting the
+// forceRemotableMethod predicate to TRUE below will ensure this (it's a no-op for methods that don't match this pattern, so can be
+// freely set to true for all calls intended to produce a remotable ready method). This characteristic of a methoddesc that is fully
+// descriptive of the method and class used is also necessary in certain places in reflection. In particular, it is known to be needed
+// for the Delegate.CreateDelegate logic.
+//
+// allowCreate may be set to FALSE to enforce that the method searched
+// should already be in existence - thus preventing creation and GCs during
+// inappropriate times.
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+/* static */
+MethodDesc*
+MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
+ MethodTable *pExactMT,
+ BOOL forceBoxedEntryPoint,
+ Instantiation methodInst,
+ BOOL allowInstParam,
+ BOOL forceRemotableMethod,
+ BOOL allowCreate,
+ ClassLoadLevel level)
+{
+ CONTRACT(MethodDesc*)
+ {
+ THROWS;
+ if (allowCreate) { GC_TRIGGERS; } else { GC_NOTRIGGER; }
+ INJECT_FAULT(COMPlusThrowOM(););
+
+ PRECONDITION(CheckPointer(pDefMD));
+ PRECONDITION(CheckPointer(pExactMT));
+ PRECONDITION(pDefMD->IsRestored_NoLogging());
+ PRECONDITION(pExactMT->IsRestored_NoLogging());
+
+ // If the method descriptor belongs to a generic type then
+ // the input exact type must be an instantiation of that type.
+ // DISABLED PRECONDITION - too strict - the classes may be in
+ // a subtype relation to each other.
+ //
+ // PRECONDITION(!pDefMD->HasClassInstantiation() || pDefMD->GetMethodTable()->HasSameTypeDefAs(pExactMT));
+
+ // You may only request an BoxedEntryPointStub for an instance method on a value type
+ PRECONDITION(!forceBoxedEntryPoint || pExactMT->IsValueType());
+ PRECONDITION(!forceBoxedEntryPoint || !pDefMD->IsStatic());
+
+ // For remotable methods we better not be allowing instantiation parameters.
+ PRECONDITION(!forceRemotableMethod || !allowInstParam);
+
+ POSTCONDITION(((RETVAL == NULL) && !allowCreate) || CheckPointer(RETVAL));
+ POSTCONDITION(((RETVAL == NULL) && !allowCreate) || RETVAL->IsRestored());
+ POSTCONDITION(((RETVAL == NULL) && !allowCreate) || forceBoxedEntryPoint || !RETVAL->IsUnboxingStub());
+ POSTCONDITION(((RETVAL == NULL) && !allowCreate) || allowInstParam || !RETVAL->RequiresInstArg());
+ }
+ CONTRACT_END;
+
+ // Quick exit for the common cases where the result is the same as the primary MD we are given
+ if (!pDefMD->HasClassOrMethodInstantiation() &&
+ methodInst.IsEmpty() &&
+ !forceBoxedEntryPoint &&
+ !pDefMD->IsUnboxingStub())
+ {
+ // Make sure that pDefMD->GetMethodTable() and pExactMT are related types even
+ // if we took the fast path.
+ _ASSERTE(pDefMD->IsArray() || pDefMD->GetExactDeclaringType(pExactMT) != NULL);
+
+ RETURN pDefMD;
+ }
+
+ // Get the version of the method desc. for the instantiated shared class, e.g.
+ // e.g. if pDefMD == List<T>.m()
+ // pExactMT = List<string>
+ // then pMDescInCanonMT = List<object>.m()
+ // or
+ // e.g. if pDefMD == List<T>.m<U>()
+ // pExactMT = List<string>
+ // then pMDescInCanonMT = List<object>.m<U>()
+
+ MethodDesc * pMDescInCanonMT = pDefMD;
+
+ // Some callers pass a pExactMT that is a subtype of a parent type of pDefMD.
+ // Find the actual exact parent of pDefMD.
+ pExactMT = pDefMD->GetExactDeclaringType(pExactMT);
+ _ASSERTE(pExactMT != NULL);
+
+ if (pDefMD->HasClassOrMethodInstantiation() || !methodInst.IsEmpty())
+ {
+ // General checks related to generics: arity (if any) must match and generic method
+ // instantiation (if any) must be well-formed.
+ if (pDefMD->GetNumGenericMethodArgs() != methodInst.GetNumArgs() ||
+ !Generics::CheckInstantiation(methodInst))
+ {
+ COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+
+ pMDescInCanonMT = pExactMT->GetCanonicalMethodTable()->GetParallelMethodDesc(pDefMD);
+
+ if (!allowCreate && (!pMDescInCanonMT->IsRestored() ||
+ !pMDescInCanonMT->GetMethodTable()->IsFullyLoaded()))
+
+ {
+ RETURN(NULL);
+ }
+
+ pMDescInCanonMT->CheckRestore(level);
+ }
+
+ // This case covers nearly all "normal" (i.e. non-associate) MethodDescs. Just return
+ // the MethodDesc in the canonical method table.
+ //
+ // Also, it will be taken for methods which acquire their type context from the "this" parameter
+ // - we don't need instantiating stubs for these.
+ if ( methodInst.IsEmpty()
+ && (allowInstParam || !pMDescInCanonMT->RequiresInstArg())
+ && (forceBoxedEntryPoint == pMDescInCanonMT->IsUnboxingStub())
+ && (!forceRemotableMethod || !pMDescInCanonMT->IsInterface()
+ || !pMDescInCanonMT->GetMethodTable()->IsSharedByGenericInstantiations()) )
+ {
+ RETURN(pMDescInCanonMT);
+ }
+
+ // Unboxing stubs
+ else if (forceBoxedEntryPoint)
+ {
+
+ // This assert isn't quite right, for example the repro from NDPWhidbey 18737
+ // fires it, because we fetch an BoxedEntryPointStub for a virtual method on a struct
+ // when the uninstantiated MethodDesc for the generic virtual method actually
+ // qualifies as an BoxedEntryPointStub... Hence we weaken the assert a little.
+ //
+ // _ASSERTE(!pDefMD->IsUnboxingStub());
+ // _ASSERTE(pDefMD->IsGenericMethodDefinition() || !pDefMD->IsUnboxingStub());
+
+ //Unboxing stubs for non-generic methods and generic methods are
+ // subtly different... For non-generic methods we can look in the
+ // shared vtable, and then go to the hash table only if needed.
+ // Furthermore even if we have to go to hash table we still base
+ // the BoxedEntryPointStub on an unerlying _shared_ method descriptor.
+ //
+ // For generic methods we must build an BoxedEntryPointStub that calls an
+ // underlying instantiating stub. The underlying instantiating stub
+ // will be an _exact_ method descriptor.
+ MethodDesc *pResultMD;
+ if (methodInst.IsEmpty())
+ {
+ // First search for the unboxing MD in the shared vtable for the value type
+ pResultMD = FindTightlyBoundUnboxingStub(pMDescInCanonMT);
+
+ // Verify that we get the same result by alternative method. There is a possibility
+ // that there is no associated unboxing stub, and FindTightlyBoundUnboxingStub takes
+ // this into account but the _DEBUG version does not, so only use it if the method
+ // returned is actually different.
+ _ASSERTE(pResultMD == pMDescInCanonMT ||
+ pResultMD == FindTightlyBoundUnboxingStub_DEBUG(pMDescInCanonMT));
+
+ if (pResultMD != NULL)
+ {
+ _ASSERTE(pResultMD->IsRestored() && pResultMD->GetMethodTable()->IsFullyLoaded());
+ g_IBCLogger.LogMethodDescAccess(pResultMD);
+ RETURN(pResultMD);
+ }
+
+ MethodTable *pRepMT = pMDescInCanonMT->GetMethodTable();
+ mdMethodDef methodDef = pDefMD->GetMemberDef();
+
+ Module *pLoaderModule = ClassLoader::ComputeLoaderModule(pRepMT, methodDef, methodInst);
+ LoaderAllocator* pAllocator=pLoaderModule->GetLoaderAllocator();
+
+ InstMethodHashTable* pTable = pLoaderModule->GetInstMethodHashTable();
+ // If we didn't find it there then go to the hash table
+ pResultMD = pTable->FindMethodDesc(TypeHandle(pRepMT),
+ methodDef,
+ TRUE /* forceBoxedEntryPoint */,
+ Instantiation(),
+ FALSE /* no inst param */);
+
+ // If we didn't find it then create it...
+ if (!pResultMD)
+ {
+ // !allowCreate ==> GC_NOTRIGGER ==> no entering Crst
+ if (!allowCreate)
+ {
+ RETURN(NULL);
+ }
+
+ CrstHolder ch(&pLoaderModule->m_InstMethodHashTableCrst);
+
+ // Check whether another thread beat us to it!
+ pResultMD = pTable->FindMethodDesc(TypeHandle(pRepMT),
+ methodDef,
+ TRUE,
+ Instantiation(),
+ FALSE);
+ if (pResultMD == NULL)
+ {
+ IBCLoggerAwareAllocMemTracker amt;
+
+ pResultMD = CreateMethodDesc(pAllocator,
+ pRepMT,
+ pMDescInCanonMT,
+ mcInstantiated,
+ FALSE /* fNativeCodeSlot */,
+ FALSE /* fComPlusCallInfo */,
+ &amt);
+
+ // Indicate that this is a stub method which takes a BOXed this pointer.
+ // An BoxedEntryPointStub may still be an InstantiatedMethodDesc
+ pResultMD->SetIsUnboxingStub();
+ pResultMD->AsInstantiatedMethodDesc()->SetupWrapperStubWithInstantiations(pMDescInCanonMT, NULL, NULL);
+
+ pResultMD->SetTemporaryEntryPoint(pAllocator, &amt);
+
+ amt.SuppressRelease();
+
+ // Verify that we are not creating redundant MethodDescs
+ _ASSERTE(!pResultMD->IsTightlyBoundToMethodTable());
+
+ // Add it to the table
+ pTable->InsertMethodDesc(pResultMD);
+ }
+
+ // CrstHolder goes out of scope here
+ }
+
+ }
+ else
+ {
+ mdMethodDef methodDef = pDefMD->GetMemberDef();
+
+ Module *pLoaderModule = ClassLoader::ComputeLoaderModule(pExactMT, methodDef, methodInst);
+ LoaderAllocator* pAllocator = pLoaderModule->GetLoaderAllocator();
+
+ InstMethodHashTable* pTable = pLoaderModule->GetInstMethodHashTable();
+ // First check the hash table...
+ pResultMD = pTable->FindMethodDesc(TypeHandle(pExactMT),
+ methodDef,
+ TRUE, /* forceBoxedEntryPoint */
+ methodInst,
+ FALSE /* no inst param */);
+
+ if (!pResultMD)
+ {
+ // !allowCreate ==> GC_NOTRIGGER ==> no entering Crst
+ if (!allowCreate)
+ {
+ RETURN(NULL);
+ }
+
+ // Enter the critical section *after* we've found or created the non-unboxing instantiating stub (else we'd have a race)
+ CrstHolder ch(&pLoaderModule->m_InstMethodHashTableCrst);
+
+ // Check whether another thread beat us to it!
+ pResultMD = pTable->FindMethodDesc(TypeHandle(pExactMT),
+ methodDef,
+ TRUE, /* forceBoxedEntryPoint */
+ methodInst,
+ FALSE /* no inst param */);
+
+ if (pResultMD == NULL)
+ {
+ // Recursively get the non-unboxing instantiating stub. Thus we chain an unboxing
+ // stub with an instantiating stub.
+ MethodDesc* pNonUnboxingStub=
+ MethodDesc::FindOrCreateAssociatedMethodDesc(pDefMD,
+ pExactMT,
+ FALSE /* not Unboxing */,
+ methodInst,
+ FALSE);
+
+ _ASSERTE(pNonUnboxingStub->GetClassification() == mcInstantiated);
+ _ASSERTE(!pNonUnboxingStub->RequiresInstArg());
+ _ASSERTE(!pNonUnboxingStub->IsUnboxingStub());
+
+ IBCLoggerAwareAllocMemTracker amt;
+
+ _ASSERTE(pDefMD->GetClassification() == mcInstantiated);
+
+ pResultMD = CreateMethodDesc(pAllocator,
+ pExactMT,
+ pNonUnboxingStub,
+ mcInstantiated,
+ FALSE /* fNativeCodeSlot */,
+ FALSE /* fComPlusCallInfo */,
+ &amt);
+
+ pResultMD->SetIsUnboxingStub();
+ pResultMD->AsInstantiatedMethodDesc()->SetupWrapperStubWithInstantiations(pNonUnboxingStub,
+ pNonUnboxingStub->GetNumGenericMethodArgs(),
+ (TypeHandle *)pNonUnboxingStub->GetMethodInstantiation().GetRawArgs());
+
+ pResultMD->SetTemporaryEntryPoint(pAllocator, &amt);
+
+ amt.SuppressRelease();
+
+ // Verify that we are not creating redundant MethodDescs
+ _ASSERTE(!pResultMD->IsTightlyBoundToMethodTable());
+
+ pTable->InsertMethodDesc(pResultMD);
+ }
+
+ // CrstHolder goes out of scope here
+ }
+ }
+ _ASSERTE(pResultMD);
+
+ if (!allowCreate && (!pResultMD->IsRestored() || !pResultMD->GetMethodTable()->IsFullyLoaded()))
+ {
+ RETURN(NULL);
+ }
+
+ pResultMD->CheckRestore(level);
+ _ASSERTE(pResultMD->IsUnboxingStub());
+ _ASSERTE(!pResultMD->IsInstantiatingStub());
+ RETURN(pResultMD);
+ }
+
+
+ // Now all generic method instantiations and static/shared-struct-instance-method wrappers...
+ else
+ {
+ _ASSERTE(!forceBoxedEntryPoint);
+
+ mdMethodDef methodDef = pDefMD->GetMemberDef();
+ Module *pModule = pDefMD->GetModule();
+
+ // Some unboxed entry points are attached to canonical method tables. This is because
+ // we have to fill in vtables and/or dispatch maps at load time,
+ // and boxed entry points are created to do this. (note vtables and dispatch maps
+ // are only created for canonical instantiations). These boxed entry points
+ // in turn refer to unboxed entry points.
+
+ if (// Check if we're looking for something at the canonical instantiation
+ (allowInstParam || pExactMT->IsCanonicalMethodTable()) &&
+ // Only value types have BoxedEntryPointStubs in the canonical method table
+ pExactMT->IsValueType() &&
+ // The only generic methods whose BoxedEntryPointStubs are in the canonical method table
+ // are those open MethodDescs at the "typical" isntantiation, e.g.
+ // VC<int>.m<T>
+ // <NICE> This is probably actually not needed </NICE>
+ ClassLoader::IsTypicalInstantiation(pModule, methodDef, methodInst)
+
+ )
+ {
+ MethodDesc * pResultMD = FindTightlyBoundWrappedMethodDesc(pMDescInCanonMT);
+
+ // Verify that we get the same result by alternative method. There is a possibility
+ // that this is not an unboxing stub, and FindTightlyBoundWrappedMethodDesc takes
+ // this into account but the _DEBUG version does not, so only use it if the method
+ // returned is actually different.
+ _ASSERTE(pResultMD == pMDescInCanonMT ||
+ pResultMD == FindTightlyBoundWrappedMethodDesc_DEBUG(pMDescInCanonMT));
+
+ if (pResultMD != NULL)
+ {
+ _ASSERTE(pResultMD->IsRestored() && pResultMD->GetMethodTable()->IsFullyLoaded());
+
+ g_IBCLogger.LogMethodDescAccess(pResultMD);
+
+ if (allowInstParam || !pResultMD->RequiresInstArg())
+ {
+ RETURN(pResultMD);
+ }
+ }
+ }
+
+ // Are either the generic type arguments or the generic method arguments shared?
+ BOOL sharedInst =
+ pExactMT->GetCanonicalMethodTable()->IsSharedByGenericInstantiations()
+ || ClassLoader::IsSharableInstantiation(methodInst);
+
+ // Is it the "typical" instantiation in the correct type that does not require wrapper?
+ if (!sharedInst &&
+ pExactMT == pMDescInCanonMT->GetMethodTable() &&
+ ClassLoader::IsTypicalInstantiation(pModule, methodDef, methodInst))
+ {
+ _ASSERTE(!pMDescInCanonMT->IsUnboxingStub());
+ RETURN(pMDescInCanonMT);
+ }
+
+ // OK, so we now know the thing we're looking for can only be found in the MethodDesc table.
+
+ // If getWrappedCode == true, we are looking for a wrapped MethodDesc
+
+ BOOL getWrappedCode = allowInstParam && sharedInst;
+ BOOL getWrappedThenStub = !allowInstParam && sharedInst;
+
+ CQuickBytes qbRepInst;
+ TypeHandle *repInst = NULL;
+ if (getWrappedCode || getWrappedThenStub)
+ {
+ // Canonicalize the type arguments.
+ DWORD cbAllocaSize = 0;
+ if (!ClrSafeInt<DWORD>::multiply(methodInst.GetNumArgs(), sizeof(TypeHandle), cbAllocaSize))
+ ThrowHR(COR_E_OVERFLOW);
+
+ repInst = reinterpret_cast<TypeHandle *>(qbRepInst.AllocThrows(cbAllocaSize));
+
+ for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
+ {
+ repInst[i] = ClassLoader::CanonicalizeGenericArg(methodInst[i]);
+ }
+ }
+
+ // <NICE> These paths can probably be merged together more nicely, and the lookup-lock-lookup pattern made much
+ // more obvious </NICE>
+ InstantiatedMethodDesc *pInstMD;
+ if (getWrappedCode)
+ {
+ // Get the underlying shared code using the canonical instantiations
+ pInstMD =
+ InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT->GetCanonicalMethodTable(),
+ methodDef,
+ Instantiation(repInst, methodInst.GetNumArgs()),
+ TRUE);
+
+ // No - so create one.
+ if (pInstMD == NULL)
+ {
+ if (!allowCreate)
+ {
+ RETURN(NULL);
+ }
+
+ pInstMD = InstantiatedMethodDesc::NewInstantiatedMethodDesc(pExactMT->GetCanonicalMethodTable(),
+ pMDescInCanonMT,
+ NULL,
+ Instantiation(repInst, methodInst.GetNumArgs()),
+ TRUE);
+ }
+ }
+ else if (getWrappedThenStub)
+ {
+ // See if we've already got the instantiated method desc for this one.
+ pInstMD =
+ InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT,
+ methodDef,
+ methodInst,
+ FALSE);
+
+ // No - so create one. Go fetch the shared one first
+ if (pInstMD == NULL)
+ {
+ if (!allowCreate)
+ {
+ RETURN(NULL);
+ }
+
+ // This always returns the shared code. Repeat the original call except with
+ // approximate params and allowInstParam=true
+ MethodDesc* pWrappedMD = FindOrCreateAssociatedMethodDesc(pDefMD,
+ pExactMT->GetCanonicalMethodTable(),
+ FALSE,
+ Instantiation(repInst, methodInst.GetNumArgs()),
+ TRUE);
+
+ _ASSERTE(pWrappedMD->IsSharedByGenericInstantiations());
+ _ASSERTE(!methodInst.IsEmpty() || !pWrappedMD->IsSharedByGenericMethodInstantiations());
+
+ pInstMD = InstantiatedMethodDesc::NewInstantiatedMethodDesc(pExactMT,
+ pMDescInCanonMT,
+ pWrappedMD,
+ methodInst,
+ FALSE);
+ }
+ }
+ else
+ {
+ // See if we've already got the instantiated method desc for this one.
+ // If looking for shared code use the representative inst.
+ pInstMD =
+ InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT,
+ methodDef,
+ methodInst,
+ FALSE);
+
+ // No - so create one.
+ if (pInstMD == NULL)
+ {
+ if (!allowCreate)
+ {
+ RETURN(NULL);
+ }
+
+ pInstMD = InstantiatedMethodDesc::NewInstantiatedMethodDesc(pExactMT,
+ pMDescInCanonMT,
+ NULL,
+ methodInst,
+ FALSE);
+ }
+ }
+ _ASSERTE(pInstMD);
+
+ if (!allowCreate && (!pInstMD->IsRestored() || !pInstMD->GetMethodTable()->IsFullyLoaded()))
+ {
+ RETURN(NULL);
+ }
+
+ pInstMD->CheckRestore(level);
+
+ RETURN(pInstMD);
+ }
+}
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+// Normalize the methoddesc for reflection
+/*static*/ MethodDesc* MethodDesc::FindOrCreateAssociatedMethodDescForReflection(
+ MethodDesc *pMethod,
+ TypeHandle instType,
+ Instantiation methodInst)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS; // Because allowCreate is TRUE
+ PRECONDITION(CheckPointer(pMethod));
+ }
+ CONTRACTL_END;
+
+ MethodDesc *pInstMD = pMethod;
+
+ // no stubs for TypeDesc
+ if (instType.IsTypeDesc())
+ return pInstMD;
+
+ MethodTable* pMT = instType.AsMethodTable();
+
+ if (!methodInst.IsEmpty())
+ {
+ // method.BindGenericParameters() was called and we need to retrieve an instantiating stub
+
+ // pMethod is not necessarily a generic method definition, ResolveMethod could pass in an
+ // instantiated generic method.
+ _ASSERTE(pMethod->HasMethodInstantiation());
+
+ if (methodInst.GetNumArgs() != pMethod->GetNumGenericMethodArgs())
+ COMPlusThrow(kArgumentException);
+
+ // we base the creation of an unboxing stub on whether the original method was one already
+ // that keeps the reflection logic the same for value types
+ pInstMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
+ pMethod,
+ pMT,
+ pMethod->IsUnboxingStub(),
+ methodInst,
+ FALSE, /* no allowInstParam */
+ TRUE /* force remotable method (i.e. inst wrappers for non-generic methods on generic interfaces) */);
+ }
+ else if ( !pMethod->HasMethodInstantiation() &&
+ ( instType.IsValueType() ||
+ ( instType.HasInstantiation() &&
+ !instType.IsGenericTypeDefinition() &&
+ ( instType.IsInterface() || pMethod->IsStatic() ) ) ) )
+ {
+ //
+ // Called at MethodInfos cache creation
+ // the method is either a normal method or a generic method definition
+ // Also called at MethodBase.GetMethodBaseFromHandle
+ // the method is either a normal method, a generic method definition, or an instantiated generic method
+ // Needs an instantiating stub if
+ // - non generic static method on a generic class
+ // - non generic instance method on a struct
+ // - non generic method on a generic interface
+ //
+
+ // we base the creation of an unboxing stub on whether the original method was one already
+ // that keeps the reflection logic the same for value types
+
+ // we need unboxing stubs for virtual methods on value types unless the method is generic
+ BOOL fNeedUnboxingStub = pMethod->IsUnboxingStub() ||
+ ( instType.IsValueType() && pMethod->IsVirtual() );
+
+ pInstMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
+ pMethod, /* the original MD */
+ pMT, /* the method table */
+ fNeedUnboxingStub, /* create boxing stub */
+ Instantiation(), /* no generic instantiation */
+ FALSE, /* no allowInstParam */
+ TRUE /* force remotable method (i.e. inst wrappers for non-generic methods on generic interfaces) */);
+ }
+
+ return pInstMD;
+}
+
+// Given a typical method desc (i.e. instantiated at formal type
+// parameters if it is a generic method or lives in a generic class),
+// instantiate any type parameters at <__Canon>
+//
+// NOTE: If allowCreate is FALSE, typically you must also set ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE()
+// allowCreate may be set to FALSE to enforce that the method searched
+// should already be in existence - thus preventing creation and GCs during
+// inappropriate times.
+//
+MethodDesc * MethodDesc::FindOrCreateTypicalSharedInstantiation(BOOL allowCreate /* = TRUE */)
+{
+ CONTRACT(MethodDesc*)
+ {
+ THROWS;
+ GC_TRIGGERS;
+ PRECONDITION(IsTypicalMethodDefinition());
+ POSTCONDITION(CheckPointer(RETVAL));
+ POSTCONDITION(RETVAL->IsTypicalSharedInstantiation());
+ }
+ CONTRACT_END
+
+ MethodDesc *pMD = this;
+ MethodTable *pMT = pMD->GetMethodTable();
+
+ // First instantiate the declaring type at <__Canon,...,__Canon>
+ DWORD nGenericClassArgs = pMT->GetNumGenericArgs();
+ DWORD dwAllocSize = 0;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(TypeHandle), nGenericClassArgs, dwAllocSize))
+ ThrowHR(COR_E_OVERFLOW);
+
+ CQuickBytes qbGenericClassArgs;
+ TypeHandle* pGenericClassArgs = reinterpret_cast<TypeHandle*>(qbGenericClassArgs.AllocThrows(dwAllocSize));
+
+ for (DWORD i = 0; i < nGenericClassArgs; i++)
+ {
+ pGenericClassArgs[i] = TypeHandle(g_pCanonMethodTableClass);
+ }
+
+ pMT = ClassLoader::LoadGenericInstantiationThrowing(pMT->GetModule(),
+ pMT->GetCl(),
+ Instantiation(pGenericClassArgs, nGenericClassArgs),
+ allowCreate ? ClassLoader::LoadTypes : ClassLoader::DontLoadTypes
+ ).GetMethodTable();
+
+ if (pMT == NULL)
+ {
+ _ASSERTE(!allowCreate);
+ return NULL;
+ }
+
+ // Now instantiate the method at <__Canon,...,__Canon>, creating the shared code.
+ // This will not create an instantiating stub just yet.
+ DWORD nGenericMethodArgs = pMD->GetNumGenericMethodArgs();
+ CQuickBytes qbGenericMethodArgs;
+ TypeHandle *genericMethodArgs = NULL;
+
+ // The rest of this method instantiates a generic method
+ // Instantiate at "__Canon" if a NULL "genericMethodArgs" is given
+ if (nGenericMethodArgs)
+ {
+ dwAllocSize = 0;
+ if (!ClrSafeInt<DWORD>::multiply(sizeof(TypeHandle), nGenericMethodArgs, dwAllocSize))
+ ThrowHR(COR_E_OVERFLOW);
+
+ genericMethodArgs = reinterpret_cast<TypeHandle*>(qbGenericMethodArgs.AllocThrows(dwAllocSize));
+
+ for (DWORD i =0; i < nGenericMethodArgs; i++)
+ genericMethodArgs[i] = TypeHandle(g_pCanonMethodTableClass);
+ }
+
+ RETURN(MethodDesc::FindOrCreateAssociatedMethodDesc(pMD,
+ pMT,
+ FALSE, /* don't get unboxing entry point */
+ Instantiation(genericMethodArgs, nGenericMethodArgs),
+ TRUE,
+ FALSE,
+ allowCreate));
+}
+
+//@GENERICSVER: Set the typical (ie. formal) instantiation
+void InstantiatedMethodDesc::SetupGenericMethodDefinition(IMDInternalImport *pIMDII,
+ LoaderAllocator* pAllocator,
+ AllocMemTracker *pamTracker,
+ Module *pModule,
+ mdMethodDef tok)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ INJECT_FAULT(COMPlusThrowOM(););
+ PRECONDITION(CheckPointer(pModule));
+ PRECONDITION(CheckPointer(pIMDII));
+ }
+ CONTRACTL_END;
+
+ // The first field is never used
+ m_wFlags2 = GenericMethodDefinition | (m_wFlags2 & ~KindMask);
+
+ //@GENERICSVER: allocate space for and initialize the typical instantiation
+ //we share the typical instantiation among all instantiations by placing it in the generic method desc
+ LOG((LF_JIT, LL_INFO10000, "GENERICSVER: Initializing typical method instantiation with type handles\n"));
+ mdGenericParam tkTyPar;
+ HENUMInternalHolder hEnumTyPars(pIMDII);
+ hEnumTyPars.EnumInit(mdtGenericParam, tok);
+
+ // Initialize the typical instantiation
+ DWORD numTyPars = hEnumTyPars.EnumGetCount();
+ if (!FitsIn<WORD>(numTyPars))
+ {
+ LPCSTR szMethodName;
+ if (FAILED(pIMDII->GetNameOfMethodDef(tok, &szMethodName)))
+ {
+ szMethodName = "Invalid MethodDef record";
+ }
+ pModule->GetAssembly()->ThrowTypeLoadException(szMethodName, IDS_CLASSLOAD_TOOMANYGENERICARGS);
+ }
+ m_wNumGenericArgs = static_cast<WORD>(numTyPars);
+ _ASSERTE(m_wNumGenericArgs > 0);
+
+ S_SIZE_T dwAllocSize = S_SIZE_T(numTyPars) * S_SIZE_T(sizeof(TypeHandle));
+
+ // the memory allocated for m_pMethInst will be freed if the declaring type fails to load
+ m_pPerInstInfo = (Dictionary *) pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(dwAllocSize));
+
+ TypeHandle * pInstDest = (TypeHandle *)m_pPerInstInfo;
+ for(unsigned int i = 0; i < numTyPars; i++)
+ {
+ hEnumTyPars.EnumNext(&tkTyPar);
+
+ // code:Module.m_GenericParamToDescMap maps generic parameter RIDs to TypeVarTypeDesc
+ // instances so that we do not leak by allocating them all over again, if the declaring
+ // type repeatedly fails to load.
+ TypeVarTypeDesc *pTypeVarTypeDesc = pModule->LookupGenericParam(tkTyPar);
+ if (pTypeVarTypeDesc == NULL)
+ {
+ // Do NOT use pamTracker for this memory as we need it stay allocated even if the load fails.
+ void *mem = (void *)pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(TypeVarTypeDesc)));
+ pTypeVarTypeDesc = new (mem) TypeVarTypeDesc(pModule, tok, i, tkTyPar);
+
+ // No race here - the row in GenericParam table is owned exclusively by this method and we
+ // are holding a lock preventing other threads from loading the declaring type and setting
+ // up this method desc.
+ pModule->StoreGenericParamThrowing(tkTyPar, pTypeVarTypeDesc);
+ }
+ pInstDest[i] = TypeHandle(pTypeVarTypeDesc);
+ }
+ LOG((LF_JIT, LL_INFO10000, "GENERICSVER: Initialized typical method instantiation with %d type handles\n",numTyPars));
+}
+
+void InstantiatedMethodDesc::SetupWrapperStubWithInstantiations(MethodDesc* wrappedMD,DWORD numGenericArgs, TypeHandle *pInst)
+{
+ WRAPPER_NO_CONTRACT;
+
+ //_ASSERTE(sharedMD->IMD_IsSharedByGenericMethodInstantiations());
+
+ m_pWrappedMethodDesc.SetValue(wrappedMD);
+ m_wFlags2 = WrapperStubWithInstantiations | (m_wFlags2 & ~KindMask);
+ m_pPerInstInfo = (Dictionary*)pInst;
+
+ _ASSERTE(FitsIn<WORD>(numGenericArgs));
+ m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
+
+ _ASSERTE(IMD_IsWrapperStubWithInstantiations());
+ _ASSERTE(((MethodDesc *) this)->IsInstantiatingStub() || ((MethodDesc *) this)->IsUnboxingStub());
+}
+
+
+// Set the instantiation in the per-inst section (this is actually a dictionary)
+void InstantiatedMethodDesc::SetupSharedMethodInstantiation(DWORD numGenericArgs, TypeHandle *pPerInstInfo, DictionaryLayout *pDL)
+{
+ WRAPPER_NO_CONTRACT;
+
+ _ASSERTE(numGenericArgs != 0);
+ // Initially the dictionary layout is empty
+ m_wFlags2 = SharedMethodInstantiation | (m_wFlags2 & ~KindMask);
+ m_pPerInstInfo = (Dictionary *)pPerInstInfo;
+
+ _ASSERTE(FitsIn<WORD>(numGenericArgs));
+ m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
+
+ m_pDictLayout = pDL;
+
+
+ _ASSERTE(IMD_IsSharedByGenericMethodInstantiations());
+}
+
+// Set the instantiation in the per-inst section (this is actually a dictionary)
+void InstantiatedMethodDesc::SetupUnsharedMethodInstantiation(DWORD numGenericArgs, TypeHandle *pInst)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // The first field is never used
+ m_wFlags2 = UnsharedMethodInstantiation | (m_wFlags2 & ~KindMask);
+ m_pPerInstInfo = (Dictionary *)pInst;
+
+ _ASSERTE(FitsIn<WORD>(numGenericArgs));
+ m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
+
+ _ASSERTE(!IsUnboxingStub());
+ _ASSERTE(!IsInstantiatingStub());
+ _ASSERTE(!IMD_IsWrapperStubWithInstantiations());
+ _ASSERTE(!IMD_IsSharedByGenericMethodInstantiations());
+ _ASSERTE(!IMD_IsGenericMethodDefinition());
+}
+
+
+// A type variable is bounded to some depth iff it
+// has no chain of type variable bounds of that depth.
+// We use this is a simple test for circularity among class and method type parameter constraints:
+// the constraints on a set of n variables are well-founded iff every variable is bounded by n.
+// The test is cheap for the common case that few, if any, constraints are variables.
+BOOL Bounded(TypeVarTypeDesc *tyvar, DWORD depth) {
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(CheckPointer(tyvar));
+ } CONTRACTL_END;
+
+ if (depth == 0)
+ {
+ return FALSE;
+ }
+
+ DWORD numConstraints;
+ TypeHandle *constraints = tyvar->GetConstraints(&numConstraints, CLASS_DEPENDENCIES_LOADED);
+ for (unsigned i = 0; i < numConstraints; i++)
+ {
+ TypeHandle constraint = constraints[i];
+ if (constraint.IsGenericVariable())
+ {
+ TypeVarTypeDesc* constraintVar = (TypeVarTypeDesc*) constraint.AsTypeDesc();
+ //only consider bounds between same sort of variables (VAR or MVAR)
+ if (tyvar->GetInternalCorElementType() == constraintVar->GetInternalCorElementType())
+ {
+ if (!Bounded(constraintVar, depth - 1))
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void MethodDesc::LoadConstraintsForTypicalMethodDefinition(BOOL *pfHasCircularClassConstraints, BOOL *pfHasCircularMethodConstraints, ClassLoadLevel level/* = CLASS_LOADED*/)
+{
+ CONTRACTL {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ PRECONDITION(IsTypicalMethodDefinition());
+ PRECONDITION(CheckPointer(pfHasCircularClassConstraints));
+ PRECONDITION(CheckPointer(pfHasCircularMethodConstraints));
+ } CONTRACTL_END;
+
+ *pfHasCircularClassConstraints = FALSE;
+ *pfHasCircularMethodConstraints = FALSE;
+
+ // Force a load of the constraints on the type parameters
+ Instantiation classInst = GetClassInstantiation();
+ for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
+ {
+ TypeVarTypeDesc* tyvar = classInst[i].AsGenericVariable();
+ _ASSERTE(tyvar != NULL);
+ tyvar->LoadConstraints(level);
+ }
+
+ Instantiation methodInst = GetMethodInstantiation();
+ for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
+ {
+ TypeVarTypeDesc* tyvar = methodInst[i].AsGenericVariable();
+ _ASSERTE(tyvar != NULL);
+ tyvar->LoadConstraints(level);
+
+ VOID DoAccessibilityCheckForConstraints(MethodTable *pAskingMT, TypeVarTypeDesc *pTyVar, UINT resIDWhy);
+ DoAccessibilityCheckForConstraints(GetMethodTable(), tyvar, E_ACCESSDENIED);
+ }
+
+ // reject circular class constraints
+ for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
+ {
+ TypeVarTypeDesc* tyvar = classInst[i].AsGenericVariable();
+ _ASSERTE(tyvar != NULL);
+ if(!Bounded(tyvar, classInst.GetNumArgs()))
+ {
+ *pfHasCircularClassConstraints = TRUE;
+ }
+ }
+
+ // reject circular method constraints
+ for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
+ {
+ TypeVarTypeDesc* tyvar = methodInst[i].AsGenericVariable();
+ _ASSERTE(tyvar != NULL);
+ if(!Bounded(tyvar, methodInst.GetNumArgs()))
+ {
+ *pfHasCircularMethodConstraints = TRUE;
+ }
+ }
+
+ return;
+}
+
+
+#ifdef FEATURE_PREJIT
+
+void MethodDesc::PrepopulateDictionary(DataImage * image, BOOL nonExpansive)
+{
+ STANDARD_VM_CONTRACT;
+
+ // Note the strong similarity to MethodTable::PrepopulateDictionary
+ if (GetMethodDictionary())
+ {
+ LOG((LF_JIT, LL_INFO10000, "GENERICS: Prepopulating dictionary for MD %s\n", this));
+ GetMethodDictionary()->PrepopulateDictionary(this, NULL, nonExpansive);
+ }
+}
+
+#endif // FEATURE_PREJIT
+
+#ifndef DACCESS_COMPILE
+
+BOOL MethodDesc::SatisfiesMethodConstraints(TypeHandle thParent, BOOL fThrowIfNotSatisfied/* = FALSE*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END;
+
+ // nice: cache (positive?) result in (instantiated) methoddesc
+ // caveat: this would be unsafe for instantiated method desc living in generic,
+ // hence possibly shared classes (with varying class instantiations).
+
+ if (!HasMethodInstantiation())
+ return TRUE;
+
+ Instantiation methodInst = LoadMethodInstantiation();
+ Instantiation typicalInst = LoadTypicalMethodDefinition()->GetMethodInstantiation();
+
+ //NB: according to the constructor's signature, thParent should be the declaring type,
+ // but the code appears to admit derived types too.
+ SigTypeContext typeContext(this,thParent);
+
+ for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
+ {
+ TypeHandle thArg = methodInst[i];
+ _ASSERTE(!thArg.IsNull());
+
+ TypeVarTypeDesc* tyvar = (TypeVarTypeDesc*) (typicalInst[i].AsTypeDesc());
+ _ASSERTE(tyvar != NULL);
+ _ASSERTE(TypeFromToken(tyvar->GetTypeOrMethodDef()) == mdtMethodDef);
+
+ tyvar->LoadConstraints(); //TODO: is this necessary for anything but the typical method?
+
+ if (!tyvar->SatisfiesConstraints(&typeContext,thArg))
+ {
+ if (fThrowIfNotSatisfied)
+ {
+ SString sParentName;
+ TypeString::AppendType(sParentName, thParent);
+
+ SString sMethodName(SString::Utf8, GetName());
+
+ SString sActualParamName;
+ TypeString::AppendType(sActualParamName, methodInst[i]);
+
+ SString sFormalParamName;
+ TypeString::AppendType(sFormalParamName, typicalInst[i]);
+
+ COMPlusThrow(kVerificationException,
+ IDS_EE_METHOD_CONSTRAINTS_VIOLATION,
+ sParentName.GetUnicode(),
+ sMethodName.GetUnicode(),
+ sActualParamName.GetUnicode(),
+ sFormalParamName.GetUnicode()
+ );
+
+
+ }
+ return FALSE;
+ }
+
+ }
+ return TRUE;
+}
+
+#endif // !DACCESS_COMPILE