1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
10 // Helper functions for generics prototype
14 // ============================================================================
21 #include "genericdict.h"
22 #include "stackprobe.h"
23 #include "typestring.h"
25 #include "dumpcommon.h"
28 #include "generics.inl"
29 #ifdef FEATURE_COMINTEROP
30 #include "winrttypenameconverter.h"
31 #endif // FEATURE_COMINTEROP
34 TypeHandle ClassLoader::CanonicalizeGenericArg(TypeHandle thGenericArg)
40 POSTCONDITION(CheckPointer(RETVAL));
44 #if defined(FEATURE_SHARE_GENERIC_CODE)
45 CorElementType et = thGenericArg.GetSignatureCorElementType();
47 // Note that generic variables do not share
49 if (CorTypeInfo::IsObjRef_NoThrow(et))
50 RETURN(TypeHandle(g_pCanonMethodTableClass));
52 if (et == ELEMENT_TYPE_VALUETYPE)
54 // Don't share structs. But sharability must be propagated through
55 // them (i.e. struct<object> * shares with struct<string> *)
56 RETURN(TypeHandle(thGenericArg.GetCanonicalMethodTable()));
59 _ASSERTE(et != ELEMENT_TYPE_PTR && et != ELEMENT_TYPE_FNPTR);
62 RETURN (thGenericArg);
63 #endif // FEATURE_SHARE_GENERIC_CODE
66 // Given the build-time ShareGenericCode setting, is the specified type
67 // representation-sharable as a type parameter to a generic type or method ?
68 /* static */ BOOL ClassLoader::IsSharableInstantiation(Instantiation inst)
78 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
80 if (CanonicalizeGenericArg(inst[i]).IsCanonicalSubtype())
86 /* static */ BOOL ClassLoader::IsCanonicalGenericInstantiation(Instantiation inst)
96 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
98 if (CanonicalizeGenericArg(inst[i]) != inst[i])
104 /* static */ BOOL ClassLoader::IsTypicalSharedInstantiation(Instantiation inst)
114 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
116 if (inst[i] != TypeHandle(g_pCanonMethodTableClass))
122 #ifndef DACCESS_COMPILE
124 TypeHandle ClassLoader::LoadCanonicalGenericInstantiation(TypeKey *pTypeKey,
125 LoadTypesFlag fLoadTypes/*=LoadTypes*/,
126 ClassLoadLevel level/*=CLASS_LOADED*/)
130 if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS;
131 if (FORBIDGC_LOADER_USE_ENABLED()) GC_NOTRIGGER; else GC_TRIGGERS;
132 if (FORBIDGC_LOADER_USE_ENABLED() || fLoadTypes != LoadTypes) { LOADS_TYPE(CLASS_LOAD_BEGIN); } else { LOADS_TYPE(level); }
133 POSTCONDITION(CheckPointer(RETVAL, ((fLoadTypes == LoadTypes) ? NULL_NOT_OK : NULL_OK)));
134 POSTCONDITION(RETVAL.IsNull() || RETVAL.CheckLoadLevel(level));
138 Instantiation inst = pTypeKey->GetInstantiation();
139 DWORD ntypars = inst.GetNumArgs();
141 // Canonicalize the type arguments.
142 DWORD dwAllocSize = 0;
143 if (!ClrSafeInt<DWORD>::multiply(ntypars, sizeof(TypeHandle), dwAllocSize))
144 ThrowHR(COR_E_OVERFLOW);
146 TypeHandle ret = TypeHandle();
147 DECLARE_INTERIOR_STACK_PROBE;
148 #ifndef DACCESS_COMPILE
149 if ((dwAllocSize/GetOsPageSize()+1) >= 2)
151 DO_INTERIOR_STACK_PROBE_FOR_NOTHROW_CHECK_THREAD((10+dwAllocSize/GetOsPageSize()+1), NO_FORBIDGC_LOADER_USE_ThrowSO(););
153 #endif // DACCESS_COMPILE
154 TypeHandle *repInst = (TypeHandle*) _alloca(dwAllocSize);
156 for (DWORD i = 0; i < ntypars; i++)
158 repInst[i] = ClassLoader::CanonicalizeGenericArg(inst[i]);
161 // Load the canonical instantiation
162 TypeKey canonKey(pTypeKey->GetModule(), pTypeKey->GetTypeToken(), Instantiation(repInst, ntypars));
163 ret = ClassLoader::LoadConstructedTypeThrowing(&canonKey, fLoadTypes, level);
165 END_INTERIOR_STACK_PROBE;
169 // Create a non-canonical instantiation of a generic type, by
170 // copying the method table of the canonical instantiation
174 ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation(
176 AllocMemTracker *pamTracker)
181 PRECONDITION(CheckPointer(pTypeKey));
182 PRECONDITION(CheckPointer(pamTracker));
183 PRECONDITION(pTypeKey->HasInstantiation());
184 PRECONDITION(ClassLoader::IsSharableInstantiation(pTypeKey->GetInstantiation()));
185 PRECONDITION(!TypeHandle::IsCanonicalSubtypeInstantiation(pTypeKey->GetInstantiation()));
186 POSTCONDITION(CheckPointer(RETVAL));
187 POSTCONDITION(RETVAL.CheckMatchesKey(pTypeKey));
191 Module *pLoaderModule = ClassLoader::ComputeLoaderModule(pTypeKey);
192 LoaderAllocator* pAllocator=pLoaderModule->GetLoaderAllocator();
194 Instantiation inst = pTypeKey->GetInstantiation();
195 pAllocator->EnsureInstantiation(pTypeKey->GetModule(), inst);
196 DWORD ntypars = inst.GetNumArgs();
199 if (LoggingOn(LF_CLASSLOADER, LL_INFO1000) || g_pConfig->BreakOnInstantiationEnabled())
201 StackSString debugTypeKeyName;
202 TypeString::AppendTypeKeyDebug(debugTypeKeyName, pTypeKey);
203 LOG((LF_CLASSLOADER, LL_INFO1000, "GENERICS: New instantiation requested: %S\n", debugTypeKeyName.GetUnicode()));
205 StackScratchBuffer buf;
206 if (g_pConfig->ShouldBreakOnInstantiation(debugTypeKeyName.GetUTF8(buf)))
207 CONSISTENCY_CHECK_MSGF(false, ("BreakOnInstantiation: typename '%s' ", debugTypeKeyName.GetUTF8(buf)));
211 TypeHandle canonType;
213 OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOAD_APPROXPARENTS);
214 canonType = ClassLoader::LoadCanonicalGenericInstantiation(pTypeKey, ClassLoader::LoadTypes, CLASS_LOAD_APPROXPARENTS);
217 // Now fabricate a method table
218 MethodTable* pOldMT = canonType.AsMethodTable();
220 // We only need true vtable entries as the rest can be found in the representative method table
221 WORD cSlots = static_cast<WORD>(pOldMT->GetNumVirtuals());
223 BOOL fContainsGenericVariables = MethodTable::ComputeContainsGenericVariables(inst);
225 // These are all copied across from the old MT, i.e. don't depend on the
227 BOOL fHasRemotingVtsInfo = FALSE;
228 BOOL fHasContextStatics = FALSE;
229 BOOL fHasGenericsStaticsInfo = pOldMT->HasGenericsStaticsInfo();
230 BOOL fHasThreadStatics = (pOldMT->GetNumThreadStaticFields() > 0);
232 #ifdef FEATURE_COMINTEROP
233 BOOL fHasDynamicInterfaceMap = pOldMT->HasDynamicInterfaceMap();
234 BOOL fHasRCWPerTypeData = pOldMT->HasRCWPerTypeData();
235 #else // FEATURE_COMINTEROP
236 BOOL fHasDynamicInterfaceMap = FALSE;
237 BOOL fHasRCWPerTypeData = FALSE;
238 #endif // FEATURE_COMINTEROP
240 // Collectible types have some special restrictions
241 if (pAllocator->IsCollectible())
243 if (fHasThreadStatics || fHasContextStatics)
245 ClassLoader::ThrowTypeLoadException(pTypeKey, IDS_CLASSLOAD_COLLECTIBLESPECIALSTATICS);
247 else if (pOldMT->HasFixedAddressVTStatics())
249 ClassLoader::ThrowTypeLoadException(pTypeKey, IDS_CLASSLOAD_COLLECTIBLEFIXEDVTATTR);
253 // The number of bytes used for GC info
254 size_t cbGC = pOldMT->ContainsPointers() ? ((CGCDesc*) pOldMT)->GetSize() : 0;
256 // Bytes are required for the vtable itself
257 S_SIZE_T safe_cbMT = S_SIZE_T( cbGC ) + S_SIZE_T( sizeof(MethodTable) );
258 safe_cbMT += MethodTable::GetNumVtableIndirections(cSlots) * sizeof(MethodTable::VTableIndir_t);
259 if (safe_cbMT.IsOverflow())
261 ThrowHR(COR_E_OVERFLOW);
263 const size_t cbMT = safe_cbMT.Value();
265 // After the optional members (see below) comes the duplicated interface map.
266 // For dynamic interfaces the interface map area begins one word
267 // before the location returned by GetInterfaceMap()
268 WORD wNumInterfaces = static_cast<WORD>(pOldMT->GetNumInterfaces());
269 DWORD cbIMap = pOldMT->GetInterfaceMapSize();
270 InterfaceInfo_t * pOldIMap = (InterfaceInfo_t *)pOldMT->GetInterfaceMap();
272 BOOL fHasGuidInfo = FALSE;
273 BOOL fHasCCWTemplate = FALSE;
275 Generics::DetermineCCWTemplateAndGUIDPresenceOnNonCanonicalMethodTable(pOldMT, fContainsGenericVariables, &fHasGuidInfo, &fHasCCWTemplate);
277 DWORD dwMultipurposeSlotsMask = 0;
278 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasPerInstInfo;
279 if (wNumInterfaces != 0)
280 dwMultipurposeSlotsMask |= MethodTable::enum_flag_HasInterfaceMap;
282 // NonVirtualSlots, DispatchMap and ModuleOverride multipurpose slots are used
283 // from the canonical methodtable, so we do not need to store them here.
285 // We need space for the optional members.
286 DWORD cbOptional = MethodTable::GetOptionalMembersAllocationSize(dwMultipurposeSlotsMask,
287 FALSE, // fHasRemotableMethodInfo
288 fHasGenericsStaticsInfo,
294 pOldMT->HasTokenOverflow());
296 // We need space for the PerInstInfo, i.e. the generic dictionary pointers...
297 DWORD cbPerInst = sizeof(GenericsDictInfo) + pOldMT->GetPerInstInfoSize();
299 // Finally we need space for the instantiation/dictionary for this type
300 DWORD cbInstAndDict = pOldMT->GetInstAndDictSize();
302 // Allocate from the high frequence heap of the correct domain
303 S_SIZE_T allocSize = safe_cbMT;
304 allocSize += cbOptional;
306 allocSize += cbPerInst;
307 allocSize += cbInstAndDict;
309 if (allocSize.IsOverflow())
311 ThrowHR(COR_E_OVERFLOW);
314 #ifdef FEATURE_PREJIT
315 Module *pComputedPZM = Module::ComputePreferredZapModule(pTypeKey);
316 BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pOldMT, pLoaderModule, pComputedPZM);
318 BOOL canShareVtableChunks = MethodTable::CanShareVtableChunksFrom(pOldMT, pLoaderModule);
319 #endif // FEATURE_PREJIT
321 SIZE_T offsetOfUnsharedVtableChunks = allocSize.Value();
323 // We either share all of the canonical's virtual slots or none of them
324 // If none, we need to allocate space for the slots
325 if (!canShareVtableChunks)
327 allocSize += S_SIZE_T( cSlots ) * S_SIZE_T( sizeof(PCODE) );
330 if (allocSize.IsOverflow())
332 ThrowHR(COR_E_OVERFLOW);
335 BYTE* pMemory = (BYTE *) pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem( allocSize ));
337 // Head of MethodTable memory
338 MethodTable *pMT = (MethodTable*) (pMemory + cbGC);
341 memcpy((BYTE*)pMT - cbGC, (BYTE*) pOldMT - cbGC, cbGC);
343 // Allocate the private data block ("private" during runtime in the ngen'ed case)
344 MethodTableWriteableData * pMTWriteableData = (MethodTableWriteableData *) (BYTE *)
345 pamTracker->Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(MethodTableWriteableData))));
346 // Note: Memory allocated on loader heap is zero filled
347 pMT->SetWriteableData(pMTWriteableData);
349 // This also disables IBC logging until the type is sufficiently intitialized so
350 // it needs to be done early
351 pMTWriteableData->SetIsNotFullyLoadedForBuildMethodTable();
353 // <TODO> this is incredibly fragile. We should just construct the MT all over agin. </TODO>
354 pMT->CopyFlags(pOldMT);
356 pMT->ClearFlag(MethodTable::enum_flag_MultipurposeSlotsMask);
357 pMT->SetMultipurposeSlotsMask(dwMultipurposeSlotsMask);
359 // Set generics flags
360 pMT->ClearFlag(MethodTable::enum_flag_GenericsMask);
361 pMT->SetFlag(MethodTable::enum_flag_GenericsMask_GenericInst);
363 // Freshly allocated - does not need restore
364 pMT->ClearFlag(MethodTable::enum_flag_IsZapped);
365 pMT->ClearFlag(MethodTable::enum_flag_IsPreRestored);
367 pMT->m_pParentMethodTable.SetValueMaybeNull(NULL);
369 // Non non-virtual slots
370 pMT->ClearFlag(MethodTable::enum_flag_HasSingleNonVirtualSlot);
372 pMT->SetBaseSize(pOldMT->GetBaseSize());
373 pMT->SetParentMethodTable(pOldMT->GetParentMethodTable());
374 pMT->SetCanonicalMethodTable(pOldMT);
376 pMT->m_wNumInterfaces = pOldMT->m_wNumInterfaces;
378 #ifdef FEATURE_TYPEEQUIVALENCE
379 if (pMT->IsInterface() && !pMT->HasTypeEquivalence())
381 // fHasTypeEquivalence flag is "inherited" from generic arguments so we can quickly detect
382 // types like IList<IFoo> where IFoo is an interface with the TypeIdentifierAttribute.
383 for (DWORD i = 0; i < ntypars; i++)
385 if (inst[i].HasTypeEquivalence())
387 pMT->SetHasTypeEquivalence();
392 #endif // FEATURE_TYPEEQUIVALENCE
394 if (pOldMT->IsInterface() && IsImplicitInterfaceOfSZArray(pOldMT))
396 // Determine if we are creating an interface methodtable that may be used to dispatch through VSD
397 // on an array object using a generic interface (such as IList<T>).
398 // Please read comments in IsArray block of code:MethodTable::FindDispatchImpl.
400 // Arrays are special because we use the same method table (object[]) for all arrays of reference
401 // classes (eg string[]). This means that the method table for an array is not a complete description of
402 // the type of the array and thus the target of if something list IList<T>::IndexOf can not be determined
403 // simply by looking at the method table of T[] (which might be the method table of object[], if T is a
406 // This is done to minimize MethodTables, but as a side-effect of this optimization,
407 // we end up using a domain-shared type (object[]) with a domain-specific dispatch token.
408 // This is a problem because the same domain-specific dispatch token value can appear in
409 // multiple unshared domains (VSD takes advantage of the fact that in general a shared type
410 // cannot implement an unshared interface). This means that the same <token, object[]> pair
411 // value can mean different things in different domains (since the token could represent
412 // IList<Foo> in one domain and IEnumerable<Bar> in another). This is a problem because the
413 // VSD polymorphic lookup mechanism relies on a process-wide cache table, and as a result
414 // these duplicate values would collide if we didn't use fat dispatch token to ensure uniqueness
415 // and the interface methodtable is not in the shared domain.
417 // Of note: there is also some interesting array-specific behaviour where if B inherits from A
418 // and you have an array of B (B[]) then B[] implements IList<B> and IList<A>, but a dispatch
419 // on an IList<A> reference results in a dispatch to SZArrayHelper<A> rather than
420 // SZArrayHelper<B> (i.e., the variance implemention is not done like virtual methods).
422 // For example If Sub inherits from Super inherits from Object, then
423 // * Sub[] implements IList<Super>
424 // * Sub[] implements IList<Sub>
426 // And as a result we have the following mappings:
427 // * IList<Super>::IndexOf for Sub[] goes to SZArrayHelper<Super>::IndexOf
428 // * IList<Sub>::IndexOf for Sub[] goes to SZArrayHelper<Sub>::IndexOf
430 pMT->SetRequiresFatDispatchTokens();
433 // Number of slots only includes vtable slots
434 pMT->SetNumVirtuals(cSlots);
436 // Fill out the vtable indirection slots
437 MethodTable::VtableIndirectionSlotIterator it = pMT->IterateVtableIndirectionSlots();
440 if (canShareVtableChunks)
442 // Share the canonical chunk
443 it.SetIndirectionSlot(pOldMT->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
447 // Use the locally allocated chunk
448 it.SetIndirectionSlot((PTR_PCODE)(pMemory+offsetOfUnsharedVtableChunks));
449 offsetOfUnsharedVtableChunks += it.GetSize();
453 // If we are not sharing parent chunks, copy down the slot contents
454 if (!canShareVtableChunks)
456 // Need to assign the slots one by one to filter out jump thunks
457 for (DWORD i = 0; i < cSlots; i++)
459 pMT->SetSlot(i, pOldMT->GetRestoredSlot(i));
463 // All flags on m_pNgenPrivateData data apart
464 // are initially false for a dynamically generated instantiation.
466 // Last time this was checked this included
467 // enum_flag_RemotingConfigChecked
468 // enum_flag_RequiresManagedActivation
469 // enum_flag_Unrestored
470 // enum_flag_CriticalTypePrepared
471 #ifdef FEATURE_PREJIT
472 // enum_flag_NGEN_IsFixedUp
473 // enum_flag_NGEN_NeedsRestoreCached
474 // enum_flag_NGEN_NeedsRestore
475 #endif // FEATURE_PREJIT
477 if (pOldMT->RequiresManagedActivation())
479 // Will also set enum_flag_RemotingConfigChecked
480 pMT->SetRequiresManagedActivation();
483 if (fContainsGenericVariables)
484 pMT->SetContainsGenericVariables();
486 if (fHasGenericsStaticsInfo)
487 pMT->SetDynamicStatics(TRUE);
490 #ifdef FEATURE_COMINTEROP
492 pMT->SetHasCCWTemplate();
494 pMT->SetHasGuidInfo();
497 // Since we are fabricating a new MT based on an existing one, the per-inst info should
499 _ASSERTE(pOldMT->HasPerInstInfo());
501 // Fill in per-inst map pointer (which points to the array of generic dictionary pointers)
502 pMT->SetPerInstInfo((MethodTable::PerInstInfoElem_t *) (pMemory + cbMT + cbOptional + cbIMap + sizeof(GenericsDictInfo)));
503 _ASSERTE(FitsIn<WORD>(pOldMT->GetNumDicts()));
504 _ASSERTE(FitsIn<WORD>(pOldMT->GetNumGenericArgs()));
505 pMT->SetDictInfo(static_cast<WORD>(pOldMT->GetNumDicts()), static_cast<WORD>(pOldMT->GetNumGenericArgs()));
507 // Fill in the last entry in the array of generic dictionary pointers ("per inst info")
508 // The others are filled in by LoadExactParents which copied down any inherited generic
509 // dictionary pointers.
510 Dictionary * pDict = (Dictionary*) (pMemory + cbMT + cbOptional + cbIMap + cbPerInst);
511 MethodTable::PerInstInfoElem_t *pPInstInfo = (MethodTable::PerInstInfoElem_t *) (pMT->GetPerInstInfo() + (pOldMT->GetNumDicts()-1));
512 pPInstInfo->SetValueMaybeNull(pDict);
514 // Fill in the instantiation section of the generic dictionary. The remainder of the
515 // generic dictionary will be zeroed, which is the correct initial state.
516 TypeHandle * pInstDest = (TypeHandle *)pDict->GetInstantiation();
517 for (DWORD iArg = 0; iArg < ntypars; iArg++)
519 pInstDest[iArg] = inst[iArg];
522 // Copy interface map across
523 InterfaceInfo_t * pInterfaceMap = (InterfaceInfo_t *)(pMemory + cbMT + cbOptional + (fHasDynamicInterfaceMap ? sizeof(DWORD_PTR) : 0));
525 #ifdef FEATURE_COMINTEROP
526 // Extensible RCW's are prefixed with the count of dynamic interfaces.
527 if (fHasDynamicInterfaceMap)
529 *(((DWORD_PTR *)pInterfaceMap) - 1) = 0;
531 #endif // FEATURE_COMINTEROP
533 for (WORD iItf = 0; iItf < wNumInterfaces; iItf++)
535 OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOAD_APPROXPARENTS);
536 pInterfaceMap[iItf].SetMethodTable(pOldIMap[iItf].GetApproxMethodTable(pOldMT->GetLoaderModule()));
539 // Set the interface map pointer stored in the main section of the vtable (actually
540 // an optional member) to point to the correct region within the newly
541 // allocated method table.
543 // Fill in interface map pointer
544 pMT->SetInterfaceMap(wNumInterfaces, pInterfaceMap);
546 // Copy across extra flags for these interfaces as well. We may need additional memory for this.
547 PVOID pExtraInterfaceInfo = NULL;
548 SIZE_T cbExtraInterfaceInfo = MethodTable::GetExtraInterfaceInfoSize(wNumInterfaces);
549 if (cbExtraInterfaceInfo)
550 pExtraInterfaceInfo = pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbExtraInterfaceInfo)));
552 // Call this even in the case where pExtraInterfaceInfo == NULL (certain cases are optimized and don't
553 // require extra buffer space).
554 pMT->InitializeExtraInterfaceInfo(pExtraInterfaceInfo);
556 for (UINT32 i = 0; i < pOldMT->GetNumInterfaces(); i++)
558 if (pOldMT->IsInterfaceDeclaredOnClass(i))
559 pMT->SetInterfaceDeclaredOnClass(i);
562 pMT->SetLoaderModule(pLoaderModule);
563 pMT->SetLoaderAllocator(pAllocator);
567 // Name for debugging
568 StackSString debug_ClassNameString;
569 TypeString::AppendTypeKey(debug_ClassNameString, pTypeKey, TypeString::FormatNamespace | TypeString::FormatAngleBrackets | TypeString::FormatFullInst);
570 StackScratchBuffer debug_ClassNameBuffer;
571 const char *debug_szClassNameBuffer = debug_ClassNameString.GetUTF8(debug_ClassNameBuffer);
572 S_SIZE_T safeLen = S_SIZE_T(strlen(debug_szClassNameBuffer)) + S_SIZE_T(1);
573 if (safeLen.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW);
575 size_t len = safeLen.Value();
576 char *debug_szClassName = (char *)pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(safeLen));
577 strcpy_s(debug_szClassName, len, debug_szClassNameBuffer);
578 pMT->SetDebugClassName(debug_szClassName);
580 // Debugging information
581 if (pOldMT->Debug_HasInjectedInterfaceDuplicates())
582 pMT->Debug_SetHasInjectedInterfaceDuplicates();
585 // <NICE>This logic is identical to logic in class.cpp. Factor these out.</NICE>
586 // No need to generate IDs for open types. However
587 // we still leave the optional member in the MethodTable holding the value -1 for the ID.
588 if (fHasGenericsStaticsInfo)
590 FieldDesc* pStaticFieldDescs = NULL;
592 if (pOldMT->GetNumStaticFields() != 0)
594 pStaticFieldDescs = (FieldDesc*) pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(FieldDesc)) * S_SIZE_T(pOldMT->GetNumStaticFields())));
595 FieldDesc* pOldFD = pOldMT->GetGenericsStaticFieldDescs();
597 g_IBCLogger.LogFieldDescsAccess(pOldFD);
599 for (DWORD i = 0; i < pOldMT->GetNumStaticFields(); i++)
601 pStaticFieldDescs[i].InitializeFrom(pOldFD[i], pMT);
604 pMT->SetupGenericsStaticsInfo(pStaticFieldDescs);
608 // VTS info doesn't depend on the exact instantiation but we make a copy
609 // anyway since we can't currently deal with the possibility of having a
610 // cross module pointer to the data block. Eventually we might be able to
611 // tokenize this reference, but determine first whether there's enough
612 // performance degradation to justify the extra complexity.
614 pMT->SetCl(pOldMT->GetCl());
616 // Check we've set up the flags correctly on the new method table
617 _ASSERTE(!fContainsGenericVariables == !pMT->ContainsGenericVariables());
618 _ASSERTE(!fHasGenericsStaticsInfo == !pMT->HasGenericsStaticsInfo());
619 _ASSERTE(!pLoaderModule->GetAssembly()->IsDomainNeutral() == !pMT->IsDomainNeutral());
620 #ifdef FEATURE_COMINTEROP
621 _ASSERTE(!fHasDynamicInterfaceMap == !pMT->HasDynamicInterfaceMap());
622 _ASSERTE(!fHasRCWPerTypeData == !pMT->HasRCWPerTypeData());
623 _ASSERTE(!fHasCCWTemplate == !pMT->HasCCWTemplate());
624 _ASSERTE(!fHasGuidInfo == !pMT->HasGuidInfo());
627 LOG((LF_CLASSLOADER, LL_INFO1000, "GENERICS: Replicated methodtable to create type %s\n", pMT->GetDebugClassName()));
630 if (g_pConfig->ShouldDumpOnClassLoad(debug_szClassName))
632 LOG((LF_ALWAYS, LL_ALWAYS,
633 "Method table summary for '%s' (instantiation):\n",
634 pMT->GetDebugClassName()));
635 pMT->Debug_DumpInterfaceMap("Approximate");
639 #ifdef FEATURE_PREJIT
640 _ASSERTE(pComputedPZM == Module::GetPreferredZapModuleForMethodTable(pMT));
641 #endif //FEATURE_PREJIT
643 // We never have non-virtual slots in this method table (set SetNumVtableSlots and SetNumVirtuals above)
644 _ASSERTE(!pMT->HasNonVirtualSlots());
646 pMTWriteableData->SetIsRestoredForBuildMethodTable();
648 RETURN(TypeHandle(pMT));
649 } // ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation
654 BOOL CheckInstantiation(Instantiation inst)
663 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
665 TypeHandle th = inst[i];
671 CorElementType type = th.GetSignatureCorElementType();
672 if (CorTypeInfo::IsGenericVariable_NoThrow(type))
677 g_IBCLogger.LogTypeMethodTableAccess(&th);
679 if ( type == ELEMENT_TYPE_BYREF
680 || type == ELEMENT_TYPE_TYPEDBYREF
681 || type == ELEMENT_TYPE_VOID
682 || type == ELEMENT_TYPE_PTR
683 || type == ELEMENT_TYPE_FNPTR)
688 MethodTable* pMT = th.GetMethodTable();
691 if (pMT->IsByRefLike())
700 // Just records the owner and links to the previous graph.
701 RecursionGraph::RecursionGraph(RecursionGraph *pPrev, TypeHandle thOwner)
703 LIMITED_METHOD_CONTRACT;
711 RecursionGraph::~RecursionGraph()
714 if (m_pNodes != NULL)
718 // Adds edges generated by the parent and implemented interfaces; returns TRUE iff
719 // an expanding cycle was found.
720 BOOL RecursionGraph::CheckForIllegalRecursion()
726 PRECONDITION(!m_thOwner.IsTypeDesc());
730 MethodTable *pMT = m_thOwner.AsMethodTable();
732 Instantiation inst = pMT->GetInstantiation();
734 // Initialize the node array.
735 m_pNodes = new Node[inst.GetNumArgs()];
737 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
739 m_pNodes[i].SetSourceVar(inst[i].AsGenericVariable());
742 // Record edges generated by inheriting from the parent.
743 MethodTable *pParentMT = pMT->GetParentMethodTable();
746 AddDependency(pParentMT);
749 // Record edges generated by implementing interfaces.
750 MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap();
753 AddDependency(it.GetInterface());
756 // Check all owned nodes for expanding cycles. The edges recorded above must all
757 // go from owned nodes so it suffices to look only at these.
758 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
760 if (HasExpandingCycle(&m_pNodes[i], &m_pNodes[i]))
767 // Returns TRUE iff the given type is already on the stack (in fact an analogue of
768 // code:TypeHandleList::Exists).
771 BOOL RecursionGraph::HasSeenType(RecursionGraph *pDepGraph, TypeHandle thType)
773 LIMITED_METHOD_CONTRACT;
775 while (pDepGraph != NULL)
777 if (pDepGraph->m_thOwner == thType) return TRUE;
778 pDepGraph = pDepGraph->m_pPrev;
783 // Adds the specified MT as a dependency (parent or interface) of the owner.
784 void RecursionGraph::AddDependency(MethodTable *pMT, TypeHandleList *pExpansionVars /*= NULL*/)
790 PRECONDITION(pMT != NULL);
795 // - If T appears as the actual type argument to be substituted for U in some referenced
796 // type D<..., U, ...> add a non-expanding (->) edge from T to U.
797 // - If T appears somewhere inside (but not as) the actual type argument to be substituted
798 // for U in referenced type D<..., U, ...> add an expanding (=>) edge from T to U.
800 // Non-generic dependencies are not interesting.
801 if (!pMT->HasInstantiation())
804 // Get the typical instantiation of pMT to figure out its type vars.
805 TypeHandle thTypical = ClassLoader::LoadTypeDefThrowing(
806 pMT->GetModule(), pMT->GetCl(),
807 ClassLoader::ThrowIfNotFound,
808 ClassLoader::PermitUninstDefOrRef, tdNoTypes,
809 CLASS_LOAD_APPROXPARENTS);
811 Instantiation inst = pMT->GetInstantiation();
812 Instantiation typicalInst = thTypical.GetInstantiation();
814 _ASSERTE(inst.GetNumArgs() == typicalInst.GetNumArgs());
816 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
818 TypeHandle thArg = inst[i];
819 TypeHandle thVar = typicalInst[i];
820 if (thArg.IsGenericVariable())
822 // Add a non-expanding edge from thArg to i-th generic parameter of pMT.
823 AddEdge(thArg.AsGenericVariable(), thVar.AsGenericVariable(), FALSE);
825 // Process the backlog.
827 TypeHandleList *pList = pExpansionVars;
828 while (TypeHandleList::GetNext(&pList, &thTo))
830 AddEdge(thArg.AsGenericVariable(), thTo.AsGenericVariable(), TRUE);
835 while (thArg.IsTypeDesc())
837 _ASSERTE(thArg.HasTypeParam());
838 thArg = (static_cast<PTR_ParamTypeDesc>(thArg.AsTypeDesc()))->GetModifiedType();
840 if (thArg.IsGenericVariable()) // : A<!T[]>
842 // Add an expanding edge from thArg to i-th parameter of pMT.
843 AddEdge(thArg.AsGenericVariable(), thVar.AsGenericVariable(), TRUE);
848 if (!thArg.IsTypeDesc()) // : A<B<!T>>
850 // We will add an expanding edge but we do not yet know from which variable(s).
851 // Add the to-variable to the list and call recursively to inspect thArg's
853 TypeHandleList newExpansionVars(thVar, pExpansionVars);
854 AddDependency(thArg.AsMethodTable(), &newExpansionVars);
860 // Add an edge from pFromVar to pToVar - either non-expanding or expanding.
861 void RecursionGraph::AddEdge(TypeVarTypeDesc *pFromVar, TypeVarTypeDesc *pToVar, BOOL fExpanding)
867 PRECONDITION(pFromVar != NULL);
868 PRECONDITION(pToVar != NULL);
872 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",
873 (fExpanding ? "EXPANDING" : "NON-EXPANDING"),
874 pFromVar->GetToken(), pFromVar->GetModule(),
875 pToVar->GetToken(), pToVar->GetModule(),
876 m_thOwner.AsMethodTable()));
878 // Get the source node.
879 Node *pNode = &m_pNodes[pFromVar->GetIndex()];
880 _ASSERTE(pFromVar == pNode->GetSourceVar());
883 ULONG_PTR edge = (ULONG_PTR)pToVar;
884 if (fExpanding) edge |= Node::EDGE_EXPANDING_FLAG;
886 IfFailThrow(pNode->GetEdges()->Append((void *)edge));
889 // Recursive worker that checks whether this node is part of an expanding cycle.
890 BOOL RecursionGraph::HasExpandingCycle(Node *pCurrentNode, Node *pStartNode, BOOL fExpanded /*= FALSE*/)
896 PRECONDITION(CheckPointer(pCurrentNode));
897 PRECONDITION(CheckPointer(pStartNode));
901 // This method performs a modified DFS. We are not looking for any cycle but for a cycle
902 // which has at least one expanding edge. Therefore we:
903 // 1) Pass aroung the fExpanded flag to indicate that we've seen an expanding edge.
904 // 2) Explicitly check for returning to the starting point rather an arbitrary visited node.
906 // Did we just find the cycle?
907 if (fExpanded && pCurrentNode == pStartNode)
910 // Have we been here before or is this a dead end?
911 if (pCurrentNode->IsVisited() || pCurrentNode->GetEdges()->GetCount() == 0)
914 pCurrentNode->SetVisited();
916 ArrayList::Iterator iter = pCurrentNode->GetEdges()->Iterate();
919 ULONG_PTR edge = (ULONG_PTR)iter.GetElement();
921 BOOL fExpanding = (edge & Node::EDGE_EXPANDING_FLAG);
923 TypeVarTypeDesc *pToVar = (TypeVarTypeDesc *)(edge & ~Node::EDGE_EXPANDING_FLAG);
924 unsigned int dwIndex = pToVar->GetIndex();
927 RecursionGraph *pGraph = this;
929 // Find the destination node.
932 if (pGraph->m_pNodes != NULL &&
933 dwIndex < pGraph->m_thOwner.GetNumGenericArgs() &&
934 pGraph->m_pNodes[dwIndex].GetSourceVar() == pToVar)
936 pNode = &pGraph->m_pNodes[dwIndex];
939 pGraph = pGraph->m_pPrev;
941 while (pGraph != NULL);
945 // The new path is expanding if it was expanding already or if the edge we follow is expanding.
946 if (HasExpandingCycle(pNode, pStartNode, fExpanded || fExpanding))
951 pCurrentNode->ClearVisited();
956 } // namespace Generics
958 #endif // !DACCESS_COMPILE
964 * GetExactInstantiationsOfMethodAndItsClassFromCallInformation
966 * This routine takes in the various pieces of information of a call site to managed code
967 * and returns the exact instatiations for the method and the class on which the method is defined.
970 * pRepMethod - A MethodDesc to the representative instantiation method.
971 * pThis - The OBJECTREF that is being passed to pRepMethod.
972 * pParamTypeArg - The extra argument passed to pRepMethod when pRepMethod is either
973 * RequiresInstMethodTableArg() or RequiresInstMethodDescArg().
974 * pSpecificClass - A pointer to a TypeHandle for storing the exact instantiation
975 * of the class on which pRepMethod is defined, based on the call information
976 * pSpecificMethod - A pointer to a MethodDesc* for storing the exact instantiation
977 * of pRepMethod, based on the call information
980 * TRUE if successful.
981 * FALSE if could not get the exact TypeHandle & MethodDesc requested. In this case,
982 * the SpecificClass may be correct, iff the class is not a generic class.
985 BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation(
986 /* in */ MethodDesc *pRepMethod,
987 /* in */ OBJECTREF pThis,
988 /* in */ PTR_VOID pParamTypeArg,
989 /* out*/ TypeHandle *pSpecificClass,
990 /* out*/ MethodDesc** pSpecificMethod
999 PRECONDITION(CheckPointer(pRepMethod));
1004 PTR_VOID pExactGenericArgsToken = NULL;
1006 if (pRepMethod->AcquiresInstMethodTableFromThis())
1010 // We could be missing the memory from a dump, or the target could have simply been corrupted.
1011 ALLOW_DATATARGET_MISSING_MEMORY(
1012 pExactGenericArgsToken = dac_cast<PTR_VOID>(pThis->GetMethodTable());
1018 pExactGenericArgsToken = pParamTypeArg;
1021 return GetExactInstantiationsOfMethodAndItsClassFromCallInformation(pRepMethod, pExactGenericArgsToken,
1022 pSpecificClass, pSpecificMethod);
1025 BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation(
1026 /* in */ MethodDesc *pRepMethod,
1027 /* in */ PTR_VOID pExactGenericArgsToken,
1028 /* out*/ TypeHandle *pSpecificClass,
1029 /* out*/ MethodDesc** pSpecificMethod
1038 PRECONDITION(CheckPointer(pRepMethod));
1044 // Start with some decent default values.
1046 MethodDesc * pMD = pRepMethod;
1047 MethodTable * pMT = pRepMethod->GetMethodTable();
1049 *pSpecificMethod = pMD;
1050 *pSpecificClass = pMT;
1052 if (!pRepMethod->IsSharedByGenericInstantiations())
1057 if (pExactGenericArgsToken == NULL)
1062 BOOL retVal = FALSE;
1064 // The following target memory reads will not necessarily succeed against dumps, and will throw on failure.
1065 EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY
1067 if (pRepMethod->RequiresInstMethodTableArg())
1069 pMT = dac_cast<PTR_MethodTable>(pExactGenericArgsToken);
1072 else if (pRepMethod->RequiresInstMethodDescArg())
1074 pMD = dac_cast<PTR_MethodDesc>(pExactGenericArgsToken);
1075 pMT = pMD->GetMethodTable();
1078 else if (pRepMethod->AcquiresInstMethodTableFromThis())
1080 // The exact token might actually be a child class of the class containing
1081 // the specified function so walk up the parent chain to make sure we return
1082 // an exact instantiation of the CORRECT parent class.
1083 pMT = pMD->GetExactDeclaringType(dac_cast<PTR_MethodTable>(pExactGenericArgsToken));
1084 _ASSERTE(pMT != NULL);
1089 _ASSERTE(!"Should not happen.");
1092 EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
1094 *pSpecificMethod = pMD;
1095 *pSpecificClass = pMT;
1100 } // namespace Generics;