diff options
Diffstat (limited to 'src/vm/generics.cpp')
-rw-r--r-- | src/vm/generics.cpp | 1145 |
1 files changed, 1145 insertions, 0 deletions
diff --git a/src/vm/generics.cpp b/src/vm/generics.cpp new file mode 100644 index 0000000000..3ff3d5aa2e --- /dev/null +++ b/src/vm/generics.cpp @@ -0,0 +1,1145 @@ +// 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: generics.cpp +// + + +// +// Helper functions for generics prototype +// + +// +// ============================================================================ + +#include "common.h" +#include "method.hpp" +#include "field.h" +#include "eeconfig.h" +#include "generics.h" +#include "genericdict.h" +#include "stackprobe.h" +#include "typestring.h" +#include "typekey.h" +#include "dumpcommon.h" +#include "array.h" + +#include "generics.inl" +#ifdef FEATURE_COMINTEROP +#include "winrttypenameconverter.h" +#endif // FEATURE_COMINTEROP + +/* static */ +TypeHandle ClassLoader::CanonicalizeGenericArg(TypeHandle thGenericArg) +{ + CONTRACT(TypeHandle) + { + NOTHROW; + GC_NOTRIGGER; + POSTCONDITION(CheckPointer(RETVAL)); + } + CONTRACT_END + +#if defined(FEATURE_SHARE_GENERIC_CODE) + CorElementType et = thGenericArg.GetSignatureCorElementType(); + + // Note that generic variables do not share + + if (CorTypeInfo::IsObjRef_NoThrow(et)) + RETURN(TypeHandle(g_pCanonMethodTableClass)); + + if (et == ELEMENT_TYPE_VALUETYPE) + { + // Don't share structs. But sharability must be propagated through + // them (i.e. struct<object> * shares with struct<string> *) + RETURN(TypeHandle(thGenericArg.GetCanonicalMethodTable())); + } + + _ASSERTE(et != ELEMENT_TYPE_PTR && et != ELEMENT_TYPE_FNPTR); + RETURN(thGenericArg); +#else + RETURN (thGenericArg); +#endif // FEATURE_SHARE_GENERIC_CODE +} + + // Given the build-time ShareGenericCode setting, is the specified type +// representation-sharable as a type parameter to a generic type or method ? +/* static */ BOOL ClassLoader::IsSharableInstantiation(Instantiation inst) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END + + for (DWORD i = 0; i < inst.GetNumArgs(); i++) + { + if (CanonicalizeGenericArg(inst[i]).IsCanonicalSubtype()) + return TRUE; + } + return FALSE; +} + +/* static */ BOOL ClassLoader::IsCanonicalGenericInstantiation(Instantiation inst) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END + + for (DWORD i = 0; i < inst.GetNumArgs(); i++) + { + if (CanonicalizeGenericArg(inst[i]) != inst[i]) + return FALSE; + } + return TRUE; +} + +/* static */ BOOL ClassLoader::IsTypicalSharedInstantiation(Instantiation inst) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + FORBID_FAULT; + } + CONTRACTL_END + + for (DWORD i = 0; i < inst.GetNumArgs(); i++) + { + if (inst[i] != TypeHandle(g_pCanonMethodTableClass)) + return FALSE; + } + return TRUE; +} + +#ifndef DACCESS_COMPILE + +TypeHandle ClassLoader::LoadCanonicalGenericInstantiation(TypeKey *pTypeKey, + LoadTypesFlag fLoadTypes/*=LoadTypes*/, + ClassLoadLevel level/*=CLASS_LOADED*/) +{ + CONTRACT(TypeHandle) + { + if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; + if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS; + if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); } + POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK))); + POSTCONDITION(RETVAL.IsNull() || RETVAL.CheckLoadLevel(level)); + } + CONTRACT_END + + Instantiation inst = pTypeKey->GetInstantiation(); + DWORD ntypars = inst.GetNumArgs(); + + // Canonicalize the type arguments. + DWORD dwAllocSize = 0; + if (!ClrSafeInt<DWORD>::multiply(ntypars, sizeof(TypeHandle), dwAllocSize)) + ThrowHR(COR_E_OVERFLOW); + + TypeHandle ret = TypeHandle(); + DECLARE_INTERIOR_STACK_PROBE; +#ifndef DACCESS_COMPILE + if ((dwAllocSize/PAGE_SIZE+1) >= 2) + { + DO_INTERIOR_STACK_PROBE_FOR_NOTHROW_CHECK_THREAD((10+dwAllocSize/PAGE_SIZE+1), NO_FORBIDGC_LOADER_USE_ThrowSO();); + } +#endif // DACCESS_COMPILE + TypeHandle *repInst = (TypeHandle*) _alloca(dwAllocSize); + + for (DWORD i = 0; i < ntypars; i++) + { + repInst[i] = ClassLoader::CanonicalizeGenericArg(inst[i]); + } + + // Load the canonical instantiation + TypeKey canonKey(pTypeKey->GetModule(), pTypeKey->GetTypeToken(), Instantiation(repInst, ntypars)); + ret = ClassLoader::LoadConstructedTypeThrowing(&canonKey, fLoadTypes, level); + + END_INTERIOR_STACK_PROBE; + RETURN(ret); +} + +// Create a non-canonical instantiation of a generic type, by +// copying the method table of the canonical instantiation +// +/* static */ +TypeHandle +ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation( + TypeKey *pTypeKey, + AllocMemTracker *pamTracker) +{ + CONTRACT(TypeHandle) + { + STANDARD_VM_CHECK; + PRECONDITION(CheckPointer(pTypeKey)); + PRECONDITION(CheckPointer(pamTracker)); + PRECONDITION(pTypeKey->HasInstantiation()); + PRECONDITION(ClassLoader::IsSharableInstantiation(pTypeKey->GetInstantiation())); + PRECONDITION(!TypeHandle::IsCanonicalSubtypeInstantiation(pTypeKey->GetInstantiation())); + POSTCONDITION(CheckPointer(RETVAL)); + POSTCONDITION(RETVAL.CheckMatchesKey(pTypeKey)); + } + CONTRACT_END + + Module *pLoaderModule = ClassLoader::ComputeLoaderModule(pTypeKey); + LoaderAllocator* pAllocator=pLoaderModule->GetLoaderAllocator(); + + Instantiation inst = pTypeKey->GetInstantiation(); + pAllocator->EnsureInstantiation(pTypeKey->GetModule(), inst); + DWORD ntypars = inst.GetNumArgs(); + +#ifdef _DEBUG + if (LoggingOn(LF_CLASSLOADER, LL_INFO1000) || g_pConfig->BreakOnInstantiationEnabled()) + { + StackSString debugTypeKeyName; + TypeString::AppendTypeKeyDebug(debugTypeKeyName, pTypeKey); + LOG((LF_CLASSLOADER, LL_INFO1000, "GENERICS: New instantiation requested: %S\n", debugTypeKeyName.GetUnicode())); + + StackScratchBuffer buf; + if (g_pConfig->ShouldBreakOnInstantiation(debugTypeKeyName.GetUTF8(buf))) + CONSISTENCY_CHECK_MSGF(false, ("BreakOnInstantiation: typename '%s' ", debugTypeKeyName.GetUTF8(buf))); + } +#endif // _DEBUG + + TypeHandle canonType; + { + OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOAD_APPROXPARENTS); + canonType = ClassLoader::LoadCanonicalGenericInstantiation(pTypeKey, ClassLoader::LoadTypes, CLASS_LOAD_APPROXPARENTS); + } + + // Now fabricate a method table + MethodTable* pOldMT = canonType.AsMethodTable(); + + // We only need true vtable entries as the rest can be found in the representative method table + WORD cSlots = static_cast<WORD>(pOldMT->GetNumVirtuals()); + + BOOL fContainsGenericVariables = MethodTable::ComputeContainsGenericVariables(inst); + + // These are all copied across from the old MT, i.e. don't depend on the + // instantiation. +#ifdef FEATURE_REMOTING + BOOL fHasRemotingVtsInfo = pOldMT->HasRemotingVtsInfo(); + BOOL fHasContextStatics = pOldMT->HasContextStatics(); +#else + BOOL fHasRemotingVtsInfo = FALSE; + BOOL fHasContextStatics = FALSE; +#endif + BOOL fHasGenericsStaticsInfo = pOldMT->HasGenericsStaticsInfo(); + BOOL fHasThreadStatics = (pOldMT->GetNumThreadStaticFields() > 0); + +#ifdef FEATURE_COMINTEROP + BOOL fHasDynamicInterfaceMap = pOldMT->HasDynamicInterfaceMap(); + BOOL fHasRCWPerTypeData = pOldMT->HasRCWPerTypeData(); +#else // FEATURE_COMINTEROP + BOOL fHasDynamicInterfaceMap = FALSE; + BOOL fHasRCWPerTypeData = FALSE; +#endif // FEATURE_COMINTEROP + + // Collectible types have some special restrictions + if (pAllocator->IsCollectible()) + { + if (fHasThreadStatics || fHasContextStatics) + { + ClassLoader::ThrowTypeLoadException(pTypeKey, IDS_CLASSLOAD_COLLECTIBLESPECIALSTATICS); + } + else if (pOldMT->HasFixedAddressVTStatics()) + { + ClassLoader::ThrowTypeLoadException(pTypeKey, IDS_CLASSLOAD_COLLECTIBLEFIXEDVTATTR); + } + } + + // The number of bytes used for GC info + size_t cbGC = pOldMT->ContainsPointers() ? ((CGCDesc*) pOldMT)->GetSize() : 0; + + // Bytes are required for the vtable itself + S_SIZE_T safe_cbMT = S_SIZE_T( cbGC ) + S_SIZE_T( sizeof(MethodTable) ); + safe_cbMT += MethodTable::GetNumVtableIndirections(cSlots) * sizeof(PTR_PCODE); + if (safe_cbMT.IsOverflow()) + { + ThrowHR(COR_E_OVERFLOW); + } + const size_t cbMT = safe_cbMT.Value(); + + // After the optional members (see below) comes the duplicated interface map. + // For dynamic interfaces the interface map area begins one word + // before the location returned by GetInterfaceMap() + WORD wNumInterfaces = static_cast<WORD>(pOldMT->GetNumInterfaces()); + DWORD cbIMap = pOldMT->GetInterfaceMapSize(); + InterfaceInfo_t * pOldIMap = (InterfaceInfo_t *)pOldMT->GetInterfaceMap(); + + BOOL fHasGuidInfo = FALSE; + BOOL fHasCCWTemplate = FALSE; + + Generics::DetermineCCWTemplateAndGUIDPresenceOnNonCanonicalMethodTable(pOldMT, fContainsGenericVariables, &fHasGuidInfo, &fHasCCWTemplate); + + DWORD dwMultipurposeSlotsMask = 0; + dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasPerInstInfo; + if (wNumInterfaces != 0) + dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasInterfaceMap; + + // NonVirtualSlots, DispatchMap and ModuleOverride multipurpose slots are used + // from the canonical methodtable, so we do not need to store them here. + + // We need space for the optional members. + DWORD cbOptional = MethodTable::GetOptionalMembersAllocationSize(dwMultipurposeSlotsMask, + FALSE, // fHasRemotableMethodInfo + fHasGenericsStaticsInfo, + fHasGuidInfo, + fHasCCWTemplate, + fHasRCWPerTypeData, + fHasRemotingVtsInfo, + fHasContextStatics, + pOldMT->HasTokenOverflow()); + + // We need space for the PerInstInfo, i.e. the generic dictionary pointers... + DWORD cbPerInst = sizeof(GenericsDictInfo) + pOldMT->GetPerInstInfoSize(); + + // Finally we need space for the instantiation/dictionary for this type + DWORD cbInstAndDict = pOldMT->GetInstAndDictSize(); + + // Allocate from the high frequence heap of the correct domain + S_SIZE_T allocSize = safe_cbMT; + allocSize += cbOptional; + allocSize += cbIMap; + allocSize += cbPerInst; + allocSize += cbInstAndDict; + + if (allocSize.IsOverflow()) + { + ThrowHR(COR_E_OVERFLOW); + } + +#ifdef FEATURE_PREJIT + Module *pComputedPZM = Module::ComputePreferredZapModule(pTypeKey); + BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pOldMT, pLoaderModule, pComputedPZM); +#else + BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pOldMT, pLoaderModule); +#endif // FEATURE_PREJIT + + SIZE_T offsetOfUnsharedVtableChunks = allocSize.Value(); + + // We either share all of the canonical's virtual slots or none of them + // If none, we need to allocate space for the slots + if (!canShareVtableChunks) + { + allocSize += S_SIZE_T( cSlots ) * S_SIZE_T( sizeof(PCODE) ); + } + + if (allocSize.IsOverflow()) + { + ThrowHR(COR_E_OVERFLOW); + } + + BYTE* pMemory = (BYTE *) pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem( allocSize )); + + // Head of MethodTable memory + MethodTable *pMT = (MethodTable*) (pMemory + cbGC); + + // Copy of GC + memcpy((BYTE*)pMT - cbGC, (BYTE*) pOldMT - cbGC, cbGC); + + // Allocate the private data block ("private" during runtime in the ngen'ed case) + MethodTableWriteableData * pMTWriteableData = (MethodTableWriteableData *) (BYTE *) + pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(MethodTableWriteableData)))); + // Note: Memory allocated on loader heap is zero filled + pMT->SetWriteableData(pMTWriteableData); + + // This also disables IBC logging until the type is sufficiently intitialized so + // it needs to be done early + pMTWriteableData->SetIsNotFullyLoadedForBuildMethodTable(); + + // <TODO> this is incredibly fragile. We should just construct the MT all over agin. </TODO> + pMT->CopyFlags(pOldMT); + + pMT->ClearFlag(MethodTable::enum_flag_MultipurposeSlotsMask); + pMT->SetMultipurposeSlotsMask(dwMultipurposeSlotsMask); + + // Set generics flags + pMT->ClearFlag(MethodTable::enum_flag_GenericsMask); + pMT->SetFlag(MethodTable::enum_flag_GenericsMask_GenericInst); + + // Freshly allocated - does not need restore + pMT->ClearFlag(MethodTable::enum_flag_IsZapped); + pMT->ClearFlag(MethodTable::enum_flag_IsPreRestored); + + pMT->ClearFlag(MethodTable::enum_flag_HasIndirectParent); + + // Non non-virtual slots + pMT->ClearFlag(MethodTable::enum_flag_HasSingleNonVirtualSlot); + + pMT->SetBaseSize(pOldMT->GetBaseSize()); + pMT->SetParentMethodTable(pOldMT->GetParentMethodTable()); + pMT->SetCanonicalMethodTable(pOldMT); + + pMT->m_wNumInterfaces = pOldMT->m_wNumInterfaces; + +#ifdef FEATURE_TYPEEQUIVALENCE + if (pMT->IsInterface() && !pMT->HasTypeEquivalence()) + { + // fHasTypeEquivalence flag is "inherited" from generic arguments so we can quickly detect + // types like IList<IFoo> where IFoo is an interface with the TypeIdentifierAttribute. + for (DWORD i = 0; i < ntypars; i++) + { + if (inst[i].HasTypeEquivalence()) + { + pMT->SetHasTypeEquivalence(); + break; + } + } + } +#endif // FEATURE_TYPEEQUIVALENCE + + if (pOldMT->IsInterface() && IsImplicitInterfaceOfSZArray(pOldMT)) + { + // Determine if we are creating an interface methodtable that may be used to dispatch through VSD + // on an array object using a generic interface (such as IList<T>). + // Please read comments in IsArray block of code:MethodTable::FindDispatchImpl. + // + // Arrays are special because we use the same method table (object[]) for all arrays of reference + // classes (eg string[]). This means that the method table for an array is not a complete description of + // the type of the array and thus the target of if something list IList<T>::IndexOf can not be determined + // simply by looking at the method table of T[] (which might be the method table of object[], if T is a + // reference type). + // + // This is done to minimize MethodTables, but as a side-effect of this optimization, + // we end up using a domain-shared type (object[]) with a domain-specific dispatch token. + // This is a problem because the same domain-specific dispatch token value can appear in + // multiple unshared domains (VSD takes advantage of the fact that in general a shared type + // cannot implement an unshared interface). This means that the same <token, object[]> pair + // value can mean different things in different domains (since the token could represent + // IList<Foo> in one domain and IEnumerable<Bar> in another). This is a problem because the + // VSD polymorphic lookup mechanism relies on a process-wide cache table, and as a result + // these duplicate values would collide if we didn't use fat dispatch token to ensure uniqueness + // and the interface methodtable is not in the shared domain. + // + // Of note: there is also some interesting array-specific behaviour where if B inherits from A + // and you have an array of B (B[]) then B[] implements IList<B> and IList<A>, but a dispatch + // on an IList<A> reference results in a dispatch to SZArrayHelper<A> rather than + // SZArrayHelper<B> (i.e., the variance implemention is not done like virtual methods). + // + // For example If Sub inherits from Super inherits from Object, then + // * Sub[] implements IList<Super> + // * Sub[] implements IList<Sub> + // + // And as a result we have the following mappings: + // * IList<Super>::IndexOf for Sub[] goes to SZArrayHelper<Super>::IndexOf + // * IList<Sub>::IndexOf for Sub[] goes to SZArrayHelper<Sub>::IndexOf + // + pMT->SetRequiresFatDispatchTokens(); + } + + // Number of slots only includes vtable slots + pMT->SetNumVirtuals(cSlots); + + // Fill out the vtable indirection slots + MethodTable::VtableIndirectionSlotIterator it = pMT->IterateVtableIndirectionSlots(); + while (it.Next()) + { + if (canShareVtableChunks) + { + // Share the canonical chunk + it.SetIndirectionSlot(pOldMT->GetVtableIndirections()[it.GetIndex()]); + } + else + { + // Use the locally allocated chunk + it.SetIndirectionSlot((PTR_PCODE)(pMemory+offsetOfUnsharedVtableChunks)); + offsetOfUnsharedVtableChunks += it.GetSize(); + } + } + + // If we are not sharing parent chunks, copy down the slot contents + if (!canShareVtableChunks) + { + // Need to assign the slots one by one to filter out jump thunks + for (DWORD i = 0; i < cSlots; i++) + { + pMT->SetSlot(i, pOldMT->GetRestoredSlot(i)); + } + } + + // All flags on m_pNgenPrivateData data apart + // are initially false for a dynamically generated instantiation. + // + // Last time this was checked this included + // enum_flag_RemotingConfigChecked + // enum_flag_RequiresManagedActivation + // enum_flag_Unrestored + // enum_flag_CriticalTypePrepared +#ifdef FEATURE_PREJIT + // enum_flag_NGEN_IsFixedUp + // enum_flag_NGEN_NeedsRestoreCached + // enum_flag_NGEN_NeedsRestore +#endif // FEATURE_PREJIT + +#if defined(_DEBUG) && defined (FEATURE_REMOTING) + if (pOldMT->IsContextful() || pOldMT->GetClass()->HasRemotingProxyAttribute()) + { + _ASSERTE(pOldMT->RequiresManagedActivation()); + } +#endif // _DEBUG + if (pOldMT->RequiresManagedActivation()) + { + // Will also set enum_flag_RemotingConfigChecked + pMT->SetRequiresManagedActivation(); + } + + if (fContainsGenericVariables) + pMT->SetContainsGenericVariables(); + + if (fHasGenericsStaticsInfo) + pMT->SetDynamicStatics(TRUE); + +#ifdef FEATURE_REMOTING + if (fHasRemotingVtsInfo) + pMT->SetHasRemotingVtsInfo(); + if (fHasContextStatics) + pMT->SetHasContextStatics(); +#endif + +#ifdef FEATURE_COMINTEROP + if (fHasCCWTemplate) + pMT->SetHasCCWTemplate(); + if (fHasGuidInfo) + pMT->SetHasGuidInfo(); +#endif + + // Since we are fabricating a new MT based on an existing one, the per-inst info should + // be non-null + _ASSERTE(pOldMT->HasPerInstInfo()); + + // Fill in per-inst map pointer (which points to the array of generic dictionary pointers) + pMT->SetPerInstInfo ((Dictionary**) (pMemory + cbMT + cbOptional + cbIMap + sizeof(GenericsDictInfo))); + _ASSERTE(FitsIn<WORD>(pOldMT->GetNumDicts())); + _ASSERTE(FitsIn<WORD>(pOldMT->GetNumGenericArgs())); + pMT->SetDictInfo(static_cast<WORD>(pOldMT->GetNumDicts()), static_cast<WORD>(pOldMT->GetNumGenericArgs())); + + // Fill in the last entry in the array of generic dictionary pointers ("per inst info") + // The others are filled in by LoadExactParents which copied down any inherited generic + // dictionary pointers. + Dictionary * pDict = (Dictionary*) (pMemory + cbMT + cbOptional + cbIMap + cbPerInst); + *(pMT->GetPerInstInfo() + (pOldMT->GetNumDicts()-1)) = pDict; + + // Fill in the instantiation section of the generic dictionary. The remainder of the + // generic dictionary will be zeroed, which is the correct initial state. + TypeHandle * pInstDest = (TypeHandle *)pDict->GetInstantiation(); + for (DWORD iArg = 0; iArg < ntypars; iArg++) + { + pInstDest[iArg] = inst[iArg]; + } + + // Copy interface map across + InterfaceInfo_t * pInterfaceMap = (InterfaceInfo_t *)(pMemory + cbMT + cbOptional + (fHasDynamicInterfaceMap ? sizeof(DWORD_PTR) : 0)); + +#ifdef FEATURE_COMINTEROP + // Extensible RCW's are prefixed with the count of dynamic interfaces. + if (fHasDynamicInterfaceMap) + { + *(((DWORD_PTR *)pInterfaceMap) - 1) = 0; + } +#endif // FEATURE_COMINTEROP + + for (WORD iItf = 0; iItf < wNumInterfaces; iItf++) + { + OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOAD_APPROXPARENTS); + pInterfaceMap[iItf].SetMethodTable(pOldIMap[iItf].GetApproxMethodTable(pOldMT->GetLoaderModule())); + } + + // Set the interface map pointer stored in the main section of the vtable (actually + // an optional member) to point to the correct region within the newly + // allocated method table. + + // Fill in interface map pointer + pMT->SetInterfaceMap(wNumInterfaces, pInterfaceMap); + + // Copy across extra flags for these interfaces as well. We may need additional memory for this. + PVOID pExtraInterfaceInfo = NULL; + SIZE_T cbExtraInterfaceInfo = MethodTable::GetExtraInterfaceInfoSize(wNumInterfaces); + if (cbExtraInterfaceInfo) + pExtraInterfaceInfo = pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbExtraInterfaceInfo))); + + // Call this even in the case where pExtraInterfaceInfo == NULL (certain cases are optimized and don't + // require extra buffer space). + pMT->InitializeExtraInterfaceInfo(pExtraInterfaceInfo); + + for (UINT32 i = 0; i < pOldMT->GetNumInterfaces(); i++) + { + if (pOldMT->IsInterfaceDeclaredOnClass(i)) + pMT->SetInterfaceDeclaredOnClass(i); + } + + pMT->SetLoaderModule(pLoaderModule); + pMT->SetLoaderAllocator(pAllocator); + + +#ifdef _DEBUG + // Name for debugging + StackSString debug_ClassNameString; + TypeString::AppendTypeKey(debug_ClassNameString, pTypeKey, TypeString::FormatNamespace | TypeString::FormatAngleBrackets | TypeString::FormatFullInst); + StackScratchBuffer debug_ClassNameBuffer; + const char *debug_szClassNameBuffer = debug_ClassNameString.GetUTF8(debug_ClassNameBuffer); + S_SIZE_T safeLen = S_SIZE_T(strlen(debug_szClassNameBuffer)) + S_SIZE_T(1); + if (safeLen.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW); + + size_t len = safeLen.Value(); + char *debug_szClassName = (char *)pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(safeLen)); + strcpy_s(debug_szClassName, len, debug_szClassNameBuffer); + pMT->SetDebugClassName(debug_szClassName); + + // Debugging information + if (pOldMT->Debug_HasInjectedInterfaceDuplicates()) + pMT->Debug_SetHasInjectedInterfaceDuplicates(); +#endif // _DEBUG + + // <NICE>This logic is identical to logic in class.cpp. Factor these out.</NICE> + // No need to generate IDs for open types. However + // we still leave the optional member in the MethodTable holding the value -1 for the ID. + if (fHasGenericsStaticsInfo) + { + FieldDesc* pStaticFieldDescs = NULL; + + if (pOldMT->GetNumStaticFields() != 0) + { + pStaticFieldDescs = (FieldDesc*) pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(FieldDesc)) * S_SIZE_T(pOldMT->GetNumStaticFields()))); + FieldDesc* pOldFD = pOldMT->GetGenericsStaticFieldDescs(); + + g_IBCLogger.LogFieldDescsAccess(pOldFD); + + for (DWORD i = 0; i < pOldMT->GetNumStaticFields(); i++) + { + pStaticFieldDescs[i] = pOldFD[i]; + pStaticFieldDescs[i].SetMethodTable(pMT); + } + } + pMT->SetupGenericsStaticsInfo(pStaticFieldDescs); + } + +#ifdef FEATURE_REMOTING + // We do not cache the data for for non-canonical methods. + _ASSERTE(!pMT->HasRemotableMethodInfo()); +#endif + + // VTS info doesn't depend on the exact instantiation but we make a copy + // anyway since we can't currently deal with the possibility of having a + // cross module pointer to the data block. Eventually we might be able to + // tokenize this reference, but determine first whether there's enough + // performance degradation to justify the extra complexity. +#ifdef FEATURE_REMOTING + if (fHasRemotingVtsInfo) + { + RemotingVtsInfo *pOldInfo = pOldMT->GetRemotingVtsInfo(); + DWORD cbInfo = RemotingVtsInfo::GetSize(pOldMT->GetNumIntroducedInstanceFields()); + RemotingVtsInfo *pNewInfo = (RemotingVtsInfo*)pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbInfo))); + + memcpyNoGCRefs(pNewInfo, pOldInfo, cbInfo); + + *(pMT->GetRemotingVtsInfoPtr()) = pNewInfo; + } + + // if there are thread or context static make room for them there is no sharing with the other MethodTable + if (fHasContextStatics) + { + // this is responsible for setting the flag and allocation in the loader heap + pMT->SetupContextStatics(pamTracker, pOldMT->GetContextStaticsSize()); + } +#endif //FEATURE_REMOTING + + pMT->SetCl(pOldMT->GetCl()); + + // Check we've set up the flags correctly on the new method table + _ASSERTE(!fContainsGenericVariables == !pMT->ContainsGenericVariables()); + _ASSERTE(!fHasGenericsStaticsInfo == !pMT->HasGenericsStaticsInfo()); + _ASSERTE(!pLoaderModule->GetAssembly()->IsDomainNeutral() == !pMT->IsDomainNeutral()); +#ifdef FEATURE_REMOTING + _ASSERTE(!fHasRemotingVtsInfo == !pMT->HasRemotingVtsInfo()); + _ASSERTE(!fHasContextStatics == !pMT->HasContextStatics()); +#endif +#ifdef FEATURE_COMINTEROP + _ASSERTE(!fHasDynamicInterfaceMap == !pMT->HasDynamicInterfaceMap()); + _ASSERTE(!fHasRCWPerTypeData == !pMT->HasRCWPerTypeData()); + _ASSERTE(!fHasCCWTemplate == !pMT->HasCCWTemplate()); + _ASSERTE(!fHasGuidInfo == !pMT->HasGuidInfo()); +#endif + + LOG((LF_CLASSLOADER, LL_INFO1000, "GENERICS: Replicated methodtable to create type %s\n", pMT->GetDebugClassName())); + +#ifdef _DEBUG + if (g_pConfig->ShouldDumpOnClassLoad(debug_szClassName)) + { + LOG((LF_ALWAYS, LL_ALWAYS, + "Method table summary for '%s' (instantiation):\n", + pMT->GetDebugClassName())); + pMT->Debug_DumpInterfaceMap("Approximate"); + } +#endif //_DEBUG + +#ifdef FEATURE_PREJIT + _ASSERTE(pComputedPZM == Module::GetPreferredZapModuleForMethodTable(pMT)); +#endif //FEATURE_PREJIT + + // We never have non-virtual slots in this method table (set SetNumVtableSlots and SetNumVirtuals above) + _ASSERTE(!pMT->HasNonVirtualSlots()); + + pMTWriteableData->SetIsRestoredForBuildMethodTable(); + + RETURN(TypeHandle(pMT)); +} // ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation + +namespace Generics +{ + +BOOL CheckInstantiation(Instantiation inst) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END + + for (DWORD i = 0; i < inst.GetNumArgs(); i++) + { + TypeHandle th = inst[i]; + if (th.IsNull()) + { + return FALSE; + } + + CorElementType type = th.GetSignatureCorElementType(); + if (CorTypeInfo::IsGenericVariable_NoThrow(type)) + { + return TRUE; + } + + g_IBCLogger.LogTypeMethodTableAccess(&th); + + if ( type == ELEMENT_TYPE_BYREF + || type == ELEMENT_TYPE_TYPEDBYREF + || type == ELEMENT_TYPE_VOID + || type == ELEMENT_TYPE_PTR + || type == ELEMENT_TYPE_FNPTR) + { + return FALSE; + } + + MethodTable* pMT = th.GetMethodTable(); + if (pMT != NULL) + { + if (pMT->IsByRefLike()) + { + return FALSE; + } + } + } + return TRUE; +} + +// Just records the owner and links to the previous graph. +RecursionGraph::RecursionGraph(RecursionGraph *pPrev, TypeHandle thOwner) +{ + LIMITED_METHOD_CONTRACT; + + m_pPrev = pPrev; + m_thOwner = thOwner; + + m_pNodes = NULL; +} + +RecursionGraph::~RecursionGraph() +{ + WRAPPER_NO_CONTRACT; + if (m_pNodes != NULL) + delete [] m_pNodes; +} + +// Adds edges generated by the parent and implemented interfaces; returns TRUE iff +// an expanding cycle was found. +BOOL RecursionGraph::CheckForIllegalRecursion() +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(!m_thOwner.IsTypeDesc()); + } + CONTRACTL_END; + + MethodTable *pMT = m_thOwner.AsMethodTable(); + + Instantiation inst = pMT->GetInstantiation(); + + // Initialize the node array. + m_pNodes = new Node[inst.GetNumArgs()]; + + for (DWORD i = 0; i < inst.GetNumArgs(); i++) + { + m_pNodes[i].SetSourceVar(inst[i].AsGenericVariable()); + } + + // Record edges generated by inheriting from the parent. + MethodTable *pParentMT = pMT->GetParentMethodTable(); + if (pParentMT) + { + AddDependency(pParentMT); + } + + // Record edges generated by implementing interfaces. + MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap(); + while (it.Next()) + { + AddDependency(it.GetInterface()); + } + + // Check all owned nodes for expanding cycles. The edges recorded above must all + // go from owned nodes so it suffices to look only at these. + for (DWORD i = 0; i < inst.GetNumArgs(); i++) + { + if (HasExpandingCycle(&m_pNodes[i], &m_pNodes[i])) + return TRUE; + } + + return FALSE; +} + +// Returns TRUE iff the given type is already on the stack (in fact an analogue of +// code:TypeHandleList::Exists). +// +// static +BOOL RecursionGraph::HasSeenType(RecursionGraph *pDepGraph, TypeHandle thType) +{ + LIMITED_METHOD_CONTRACT; + + while (pDepGraph != NULL) + { + if (pDepGraph->m_thOwner == thType) return TRUE; + pDepGraph = pDepGraph->m_pPrev; + } + return FALSE; +} + +// Adds the specified MT as a dependency (parent or interface) of the owner. +void RecursionGraph::AddDependency(MethodTable *pMT, TypeHandleList *pExpansionVars /*= NULL*/) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + PRECONDITION(pMT != NULL); + } + CONTRACTL_END + + // ECMA: + // - If T appears as the actual type argument to be substituted for U in some referenced + // type D<..., U, ...> add a non-expanding (->) edge from T to U. + // - If T appears somewhere inside (but not as) the actual type argument to be substituted + // for U in referenced type D<..., U, ...> add an expanding (=>) edge from T to U. + + // Non-generic dependencies are not interesting. + if (!pMT->HasInstantiation()) + return; + + // Get the typical instantiation of pMT to figure out its type vars. + TypeHandle thTypical = ClassLoader::LoadTypeDefThrowing( + pMT->GetModule(), pMT->GetCl(), + ClassLoader::ThrowIfNotFound, + ClassLoader::PermitUninstDefOrRef, tdNoTypes, + CLASS_LOAD_APPROXPARENTS); + + Instantiation inst = pMT->GetInstantiation(); + Instantiation typicalInst = thTypical.GetInstantiation(); + + _ASSERTE(inst.GetNumArgs() == typicalInst.GetNumArgs()); + + for (DWORD i = 0; i < inst.GetNumArgs(); i++) + { + TypeHandle thArg = inst[i]; + TypeHandle thVar = typicalInst[i]; + if (thArg.IsGenericVariable()) + { + // Add a non-expanding edge from thArg to i-th generic parameter of pMT. + AddEdge(thArg.AsGenericVariable(), thVar.AsGenericVariable(), FALSE); + + // Process the backlog. + TypeHandle thTo; + TypeHandleList *pList = pExpansionVars; + while (TypeHandleList::GetNext(&pList, &thTo)) + { + AddEdge(thArg.AsGenericVariable(), thTo.AsGenericVariable(), TRUE); + } + } + else + { + while (thArg.IsTypeDesc()) + { + _ASSERTE(thArg.HasTypeParam()); + thArg = (static_cast<PTR_ParamTypeDesc>(thArg.AsTypeDesc()))->GetModifiedType(); + + if (thArg.IsGenericVariable()) // : A<!T[]> + { + // Add an expanding edge from thArg to i-th parameter of pMT. + AddEdge(thArg.AsGenericVariable(), thVar.AsGenericVariable(), TRUE); + break; + } + } + + if (!thArg.IsTypeDesc()) // : A<B<!T>> + { + // We will add an expanding edge but we do not yet know from which variable(s). + // Add the to-variable to the list and call recursively to inspect thArg's + // instantiation. + TypeHandleList newExpansionVars(thVar, pExpansionVars); + AddDependency(thArg.AsMethodTable(), &newExpansionVars); + } + } + } +} + +// Add an edge from pFromVar to pToVar - either non-expanding or expanding. +void RecursionGraph::AddEdge(TypeVarTypeDesc *pFromVar, TypeVarTypeDesc *pToVar, BOOL fExpanding) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + PRECONDITION(pFromVar != NULL); + PRECONDITION(pToVar != NULL); + } + CONTRACTL_END + + LOG((LF_CLASSLOADER, LL_INFO10000, "GENERICS: Adding %s edge: from %x(0x%x) to %x(0x%x) into recursion graph owned by MT: %x\n", + (fExpanding ? "EXPANDING" : "NON-EXPANDING"), + pFromVar->GetToken(), pFromVar->GetModule(), + pToVar->GetToken(), pToVar->GetModule(), + m_thOwner.AsMethodTable())); + + // Get the source node. + Node *pNode = &m_pNodes[pFromVar->GetIndex()]; + _ASSERTE(pFromVar == pNode->GetSourceVar()); + + // Add the edge. + ULONG_PTR edge = (ULONG_PTR)pToVar; + if (fExpanding) edge |= Node::EDGE_EXPANDING_FLAG; + + IfFailThrow(pNode->GetEdges()->Append((void *)edge)); +} + +// Recursive worker that checks whether this node is part of an expanding cycle. +BOOL RecursionGraph::HasExpandingCycle(Node *pCurrentNode, Node *pStartNode, BOOL fExpanded /*= FALSE*/) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + PRECONDITION(CheckPointer(pCurrentNode)); + PRECONDITION(CheckPointer(pStartNode)); + } + CONTRACTL_END; + + // This method performs a modified DFS. We are not looking for any cycle but for a cycle + // which has at least one expanding edge. Therefore we: + // 1) Pass aroung the fExpanded flag to indicate that we've seen an expanding edge. + // 2) Explicitly check for returning to the starting point rather an arbitrary visited node. + + // Did we just find the cycle? + if (fExpanded && pCurrentNode == pStartNode) + return TRUE; + + // Have we been here before or is this a dead end? + if (pCurrentNode->IsVisited() || pCurrentNode->GetEdges()->GetCount() == 0) + return FALSE; + + pCurrentNode->SetVisited(); + + ArrayList::Iterator iter = pCurrentNode->GetEdges()->Iterate(); + while (iter.Next()) + { + ULONG_PTR edge = (ULONG_PTR)iter.GetElement(); + + BOOL fExpanding = (edge & Node::EDGE_EXPANDING_FLAG); + + TypeVarTypeDesc *pToVar = (TypeVarTypeDesc *)(edge & ~Node::EDGE_EXPANDING_FLAG); + unsigned int dwIndex = pToVar->GetIndex(); + + Node *pNode = NULL; + RecursionGraph *pGraph = this; + + // Find the destination node. + do + { + if (pGraph->m_pNodes != NULL && + dwIndex < pGraph->m_thOwner.GetNumGenericArgs() && + pGraph->m_pNodes[dwIndex].GetSourceVar() == pToVar) + { + pNode = &pGraph->m_pNodes[dwIndex]; + break; + } + pGraph = pGraph->m_pPrev; + } + while (pGraph != NULL); + + if (pNode != NULL) + { + // The new path is expanding if it was expanding already or if the edge we follow is expanding. + if (HasExpandingCycle(pNode, pStartNode, fExpanded || fExpanding)) + return TRUE; + } + } + + pCurrentNode->ClearVisited(); + + return FALSE; +} + +} // namespace Generics + +#endif // !DACCESS_COMPILE + +namespace Generics +{ + +/* + * GetExactInstantiationsOfMethodAndItsClassFromCallInformation + * + * This routine takes in the various pieces of information of a call site to managed code + * and returns the exact instatiations for the method and the class on which the method is defined. + * + * Parameters: + * pRepMethod - A MethodDesc to the representative instantiation method. + * pThis - The OBJECTREF that is being passed to pRepMethod. + * pParamTypeArg - The extra argument passed to pRepMethod when pRepMethod is either + * RequiresInstMethodTableArg() or RequiresInstMethodDescArg(). + * pSpecificClass - A pointer to a TypeHandle for storing the exact instantiation + * of the class on which pRepMethod is defined, based on the call information + * pSpecificMethod - A pointer to a MethodDesc* for storing the exact instantiation + * of pRepMethod, based on the call information + * + * Returns: + * TRUE if successful. + * FALSE if could not get the exact TypeHandle & MethodDesc requested. In this case, + * the SpecificClass may be correct, iff the class is not a generic class. + * + */ +BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation( + /* in */ MethodDesc *pRepMethod, + /* in */ OBJECTREF pThis, + /* in */ PTR_VOID pParamTypeArg, + /* out*/ TypeHandle *pSpecificClass, + /* out*/ MethodDesc** pSpecificMethod + ) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + PRECONDITION(CheckPointer(pRepMethod)); + SUPPORTS_DAC; + } + CONTRACTL_END; + + PTR_VOID pExactGenericArgsToken = NULL; + + if (pRepMethod->AcquiresInstMethodTableFromThis()) + { + if (pThis != NULL) + { + // We could be missing the memory from a dump, or the target could have simply been corrupted. + ALLOW_DATATARGET_MISSING_MEMORY( + pExactGenericArgsToken = dac_cast<PTR_VOID>(pThis->GetMethodTable()); + ); + } + } + else + { + pExactGenericArgsToken = pParamTypeArg; + } + + return GetExactInstantiationsOfMethodAndItsClassFromCallInformation(pRepMethod, pExactGenericArgsToken, + pSpecificClass, pSpecificMethod); +} + +BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation( + /* in */ MethodDesc *pRepMethod, + /* in */ PTR_VOID pExactGenericArgsToken, + /* out*/ TypeHandle *pSpecificClass, + /* out*/ MethodDesc** pSpecificMethod + ) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + CANNOT_TAKE_LOCK; + PRECONDITION(CheckPointer(pRepMethod)); + SUPPORTS_DAC; + } + CONTRACTL_END; + + // + // Start with some decent default values. + // + MethodDesc * pMD = pRepMethod; + MethodTable * pMT = pRepMethod->GetMethodTable(); + + *pSpecificMethod = pMD; + *pSpecificClass = pMT; + + if (!pRepMethod->IsSharedByGenericInstantiations()) + { + return TRUE; + } + + if (pExactGenericArgsToken == NULL) + { + return FALSE; + } + + BOOL retVal = FALSE; + + // The following target memory reads will not necessarily succeed against dumps, and will throw on failure. + EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY + { + if (pRepMethod->RequiresInstMethodTableArg()) + { + pMT = dac_cast<PTR_MethodTable>(pExactGenericArgsToken); + retVal = TRUE; + } + else if (pRepMethod->RequiresInstMethodDescArg()) + { + pMD = dac_cast<PTR_MethodDesc>(pExactGenericArgsToken); + pMT = pMD->GetMethodTable(); + retVal = TRUE; + } + else if (pRepMethod->AcquiresInstMethodTableFromThis()) + { + // The exact token might actually be a child class of the class containing + // the specified function so walk up the parent chain to make sure we return + // an exact instantiation of the CORRECT parent class. + pMT = pMD->GetExactDeclaringType(dac_cast<PTR_MethodTable>(pExactGenericArgsToken)); + _ASSERTE(pMT != NULL); + retVal = TRUE; + } + else + { + _ASSERTE(!"Should not happen."); + } + } + EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY + + *pSpecificMethod = pMD; + *pSpecificClass = pMT; + + return retVal; +} + +} // namespace Generics; + |