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(MethodTable::VTableIndir2_t) );
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->ClearFlag(MethodTable::enum_flag_HasIndirectParent);
368 pMT->m_pParentMethodTable.SetValueMaybeNull(NULL);
370 // Non non-virtual slots
371 pMT->ClearFlag(MethodTable::enum_flag_HasSingleNonVirtualSlot);
373 pMT->SetBaseSize(pOldMT->GetBaseSize());
374 pMT->SetParentMethodTable(pOldMT->GetParentMethodTable());
375 pMT->SetCanonicalMethodTable(pOldMT);
377 pMT->m_wNumInterfaces = pOldMT->m_wNumInterfaces;
379 #ifdef FEATURE_TYPEEQUIVALENCE
380 if (pMT->IsInterface() && !pMT->HasTypeEquivalence())
382 // fHasTypeEquivalence flag is "inherited" from generic arguments so we can quickly detect
383 // types like IList<IFoo> where IFoo is an interface with the TypeIdentifierAttribute.
384 for (DWORD i = 0; i < ntypars; i++)
386 if (inst[i].HasTypeEquivalence())
388 pMT->SetHasTypeEquivalence();
393 #endif // FEATURE_TYPEEQUIVALENCE
395 if (pOldMT->IsInterface() && IsImplicitInterfaceOfSZArray(pOldMT))
397 // Determine if we are creating an interface methodtable that may be used to dispatch through VSD
398 // on an array object using a generic interface (such as IList<T>).
399 // Please read comments in IsArray block of code:MethodTable::FindDispatchImpl.
401 // Arrays are special because we use the same method table (object[]) for all arrays of reference
402 // classes (eg string[]). This means that the method table for an array is not a complete description of
403 // the type of the array and thus the target of if something list IList<T>::IndexOf can not be determined
404 // simply by looking at the method table of T[] (which might be the method table of object[], if T is a
407 // This is done to minimize MethodTables, but as a side-effect of this optimization,
408 // we end up using a domain-shared type (object[]) with a domain-specific dispatch token.
409 // This is a problem because the same domain-specific dispatch token value can appear in
410 // multiple unshared domains (VSD takes advantage of the fact that in general a shared type
411 // cannot implement an unshared interface). This means that the same <token, object[]> pair
412 // value can mean different things in different domains (since the token could represent
413 // IList<Foo> in one domain and IEnumerable<Bar> in another). This is a problem because the
414 // VSD polymorphic lookup mechanism relies on a process-wide cache table, and as a result
415 // these duplicate values would collide if we didn't use fat dispatch token to ensure uniqueness
416 // and the interface methodtable is not in the shared domain.
418 // Of note: there is also some interesting array-specific behaviour where if B inherits from A
419 // and you have an array of B (B[]) then B[] implements IList<B> and IList<A>, but a dispatch
420 // on an IList<A> reference results in a dispatch to SZArrayHelper<A> rather than
421 // SZArrayHelper<B> (i.e., the variance implemention is not done like virtual methods).
423 // For example If Sub inherits from Super inherits from Object, then
424 // * Sub[] implements IList<Super>
425 // * Sub[] implements IList<Sub>
427 // And as a result we have the following mappings:
428 // * IList<Super>::IndexOf for Sub[] goes to SZArrayHelper<Super>::IndexOf
429 // * IList<Sub>::IndexOf for Sub[] goes to SZArrayHelper<Sub>::IndexOf
431 pMT->SetRequiresFatDispatchTokens();
434 // Number of slots only includes vtable slots
435 pMT->SetNumVirtuals(cSlots);
437 // Fill out the vtable indirection slots
438 MethodTable::VtableIndirectionSlotIterator it = pMT->IterateVtableIndirectionSlots();
441 if (canShareVtableChunks)
443 // Share the canonical chunk
444 it.SetIndirectionSlot(pOldMT->GetVtableIndirections()[it.GetIndex()].GetValueMaybeNull());
448 // Use the locally allocated chunk
449 it.SetIndirectionSlot((MethodTable::VTableIndir2_t *)(pMemory+offsetOfUnsharedVtableChunks));
450 offsetOfUnsharedVtableChunks += it.GetSize();
454 // If we are not sharing parent chunks, copy down the slot contents
455 if (!canShareVtableChunks)
457 // Need to assign the slots one by one to filter out jump thunks
458 for (DWORD i = 0; i < cSlots; i++)
460 pMT->SetSlot(i, pOldMT->GetRestoredSlot(i));
464 // All flags on m_pNgenPrivateData data apart
465 // are initially false for a dynamically generated instantiation.
467 // Last time this was checked this included
468 // enum_flag_RemotingConfigChecked
469 // enum_flag_RequiresManagedActivation
470 // enum_flag_Unrestored
471 // enum_flag_CriticalTypePrepared
472 #ifdef FEATURE_PREJIT
473 // enum_flag_NGEN_IsFixedUp
474 // enum_flag_NGEN_NeedsRestoreCached
475 // enum_flag_NGEN_NeedsRestore
476 #endif // FEATURE_PREJIT
478 if (pOldMT->RequiresManagedActivation())
480 // Will also set enum_flag_RemotingConfigChecked
481 pMT->SetRequiresManagedActivation();
484 if (fContainsGenericVariables)
485 pMT->SetContainsGenericVariables();
487 if (fHasGenericsStaticsInfo)
488 pMT->SetDynamicStatics(TRUE);
491 #ifdef FEATURE_COMINTEROP
493 pMT->SetHasCCWTemplate();
495 pMT->SetHasGuidInfo();
498 // Since we are fabricating a new MT based on an existing one, the per-inst info should
500 _ASSERTE(pOldMT->HasPerInstInfo());
502 // Fill in per-inst map pointer (which points to the array of generic dictionary pointers)
503 pMT->SetPerInstInfo((MethodTable::PerInstInfoElem_t *) (pMemory + cbMT + cbOptional + cbIMap + sizeof(GenericsDictInfo)));
504 _ASSERTE(FitsIn<WORD>(pOldMT->GetNumDicts()));
505 _ASSERTE(FitsIn<WORD>(pOldMT->GetNumGenericArgs()));
506 pMT->SetDictInfo(static_cast<WORD>(pOldMT->GetNumDicts()), static_cast<WORD>(pOldMT->GetNumGenericArgs()));
508 // Fill in the last entry in the array of generic dictionary pointers ("per inst info")
509 // The others are filled in by LoadExactParents which copied down any inherited generic
510 // dictionary pointers.
511 Dictionary * pDict = (Dictionary*) (pMemory + cbMT + cbOptional + cbIMap + cbPerInst);
512 MethodTable::PerInstInfoElem_t *pPInstInfo = (MethodTable::PerInstInfoElem_t *) (pMT->GetPerInstInfo() + (pOldMT->GetNumDicts()-1));
513 pPInstInfo->SetValueMaybeNull(pDict);
515 // Fill in the instantiation section of the generic dictionary. The remainder of the
516 // generic dictionary will be zeroed, which is the correct initial state.
517 TypeHandle * pInstDest = (TypeHandle *)pDict->GetInstantiation();
518 for (DWORD iArg = 0; iArg < ntypars; iArg++)
520 pInstDest[iArg] = inst[iArg];
523 // Copy interface map across
524 InterfaceInfo_t * pInterfaceMap = (InterfaceInfo_t *)(pMemory + cbMT + cbOptional + (fHasDynamicInterfaceMap ? sizeof(DWORD_PTR) : 0));
526 #ifdef FEATURE_COMINTEROP
527 // Extensible RCW's are prefixed with the count of dynamic interfaces.
528 if (fHasDynamicInterfaceMap)
530 *(((DWORD_PTR *)pInterfaceMap) - 1) = 0;
532 #endif // FEATURE_COMINTEROP
534 for (WORD iItf = 0; iItf < wNumInterfaces; iItf++)
536 OVERRIDE_TYPE_LOAD_LEVEL_LIMIT(CLASS_LOAD_APPROXPARENTS);
537 pInterfaceMap[iItf].SetMethodTable(pOldIMap[iItf].GetApproxMethodTable(pOldMT->GetLoaderModule()));
540 // Set the interface map pointer stored in the main section of the vtable (actually
541 // an optional member) to point to the correct region within the newly
542 // allocated method table.
544 // Fill in interface map pointer
545 pMT->SetInterfaceMap(wNumInterfaces, pInterfaceMap);
547 // Copy across extra flags for these interfaces as well. We may need additional memory for this.
548 PVOID pExtraInterfaceInfo = NULL;
549 SIZE_T cbExtraInterfaceInfo = MethodTable::GetExtraInterfaceInfoSize(wNumInterfaces);
550 if (cbExtraInterfaceInfo)
551 pExtraInterfaceInfo = pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(cbExtraInterfaceInfo)));
553 // Call this even in the case where pExtraInterfaceInfo == NULL (certain cases are optimized and don't
554 // require extra buffer space).
555 pMT->InitializeExtraInterfaceInfo(pExtraInterfaceInfo);
557 for (UINT32 i = 0; i < pOldMT->GetNumInterfaces(); i++)
559 if (pOldMT->IsInterfaceDeclaredOnClass(i))
560 pMT->SetInterfaceDeclaredOnClass(i);
563 pMT->SetLoaderModule(pLoaderModule);
564 pMT->SetLoaderAllocator(pAllocator);
568 // Name for debugging
569 StackSString debug_ClassNameString;
570 TypeString::AppendTypeKey(debug_ClassNameString, pTypeKey, TypeString::FormatNamespace | TypeString::FormatAngleBrackets | TypeString::FormatFullInst);
571 StackScratchBuffer debug_ClassNameBuffer;
572 const char *debug_szClassNameBuffer = debug_ClassNameString.GetUTF8(debug_ClassNameBuffer);
573 S_SIZE_T safeLen = S_SIZE_T(strlen(debug_szClassNameBuffer)) + S_SIZE_T(1);
574 if (safeLen.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW);
576 size_t len = safeLen.Value();
577 char *debug_szClassName = (char *)pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(safeLen));
578 strcpy_s(debug_szClassName, len, debug_szClassNameBuffer);
579 pMT->SetDebugClassName(debug_szClassName);
581 // Debugging information
582 if (pOldMT->Debug_HasInjectedInterfaceDuplicates())
583 pMT->Debug_SetHasInjectedInterfaceDuplicates();
586 // <NICE>This logic is identical to logic in class.cpp. Factor these out.</NICE>
587 // No need to generate IDs for open types. However
588 // we still leave the optional member in the MethodTable holding the value -1 for the ID.
589 if (fHasGenericsStaticsInfo)
591 FieldDesc* pStaticFieldDescs = NULL;
593 if (pOldMT->GetNumStaticFields() != 0)
595 pStaticFieldDescs = (FieldDesc*) pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(FieldDesc)) * S_SIZE_T(pOldMT->GetNumStaticFields())));
596 FieldDesc* pOldFD = pOldMT->GetGenericsStaticFieldDescs();
598 g_IBCLogger.LogFieldDescsAccess(pOldFD);
600 for (DWORD i = 0; i < pOldMT->GetNumStaticFields(); i++)
602 pStaticFieldDescs[i].InitializeFrom(pOldFD[i], pMT);
605 pMT->SetupGenericsStaticsInfo(pStaticFieldDescs);
609 // VTS info doesn't depend on the exact instantiation but we make a copy
610 // anyway since we can't currently deal with the possibility of having a
611 // cross module pointer to the data block. Eventually we might be able to
612 // tokenize this reference, but determine first whether there's enough
613 // performance degradation to justify the extra complexity.
615 pMT->SetCl(pOldMT->GetCl());
617 // Check we've set up the flags correctly on the new method table
618 _ASSERTE(!fContainsGenericVariables == !pMT->ContainsGenericVariables());
619 _ASSERTE(!fHasGenericsStaticsInfo == !pMT->HasGenericsStaticsInfo());
620 _ASSERTE(!pLoaderModule->GetAssembly()->IsDomainNeutral() == !pMT->IsDomainNeutral());
621 #ifdef FEATURE_COMINTEROP
622 _ASSERTE(!fHasDynamicInterfaceMap == !pMT->HasDynamicInterfaceMap());
623 _ASSERTE(!fHasRCWPerTypeData == !pMT->HasRCWPerTypeData());
624 _ASSERTE(!fHasCCWTemplate == !pMT->HasCCWTemplate());
625 _ASSERTE(!fHasGuidInfo == !pMT->HasGuidInfo());
628 LOG((LF_CLASSLOADER, LL_INFO1000, "GENERICS: Replicated methodtable to create type %s\n", pMT->GetDebugClassName()));
631 if (g_pConfig->ShouldDumpOnClassLoad(debug_szClassName))
633 LOG((LF_ALWAYS, LL_ALWAYS,
634 "Method table summary for '%s' (instantiation):\n",
635 pMT->GetDebugClassName()));
636 pMT->Debug_DumpInterfaceMap("Approximate");
640 #ifdef FEATURE_PREJIT
641 _ASSERTE(pComputedPZM == Module::GetPreferredZapModuleForMethodTable(pMT));
642 #endif //FEATURE_PREJIT
644 // We never have non-virtual slots in this method table (set SetNumVtableSlots and SetNumVirtuals above)
645 _ASSERTE(!pMT->HasNonVirtualSlots());
647 pMTWriteableData->SetIsRestoredForBuildMethodTable();
649 RETURN(TypeHandle(pMT));
650 } // ClassLoader::CreateTypeHandleForNonCanonicalGenericInstantiation
655 BOOL CheckInstantiation(Instantiation inst)
664 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
666 TypeHandle th = inst[i];
672 CorElementType type = th.GetSignatureCorElementType();
673 if (CorTypeInfo::IsGenericVariable_NoThrow(type))
678 g_IBCLogger.LogTypeMethodTableAccess(&th);
680 if ( type == ELEMENT_TYPE_BYREF
681 || type == ELEMENT_TYPE_TYPEDBYREF
682 || type == ELEMENT_TYPE_VOID
683 || type == ELEMENT_TYPE_PTR
684 || type == ELEMENT_TYPE_FNPTR)
689 MethodTable* pMT = th.GetMethodTable();
692 if (pMT->IsByRefLike())
701 // Just records the owner and links to the previous graph.
702 RecursionGraph::RecursionGraph(RecursionGraph *pPrev, TypeHandle thOwner)
704 LIMITED_METHOD_CONTRACT;
712 RecursionGraph::~RecursionGraph()
715 if (m_pNodes != NULL)
719 // Adds edges generated by the parent and implemented interfaces; returns TRUE iff
720 // an expanding cycle was found.
721 BOOL RecursionGraph::CheckForIllegalRecursion()
727 PRECONDITION(!m_thOwner.IsTypeDesc());
731 MethodTable *pMT = m_thOwner.AsMethodTable();
733 Instantiation inst = pMT->GetInstantiation();
735 // Initialize the node array.
736 m_pNodes = new Node[inst.GetNumArgs()];
738 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
740 m_pNodes[i].SetSourceVar(inst[i].AsGenericVariable());
743 // Record edges generated by inheriting from the parent.
744 MethodTable *pParentMT = pMT->GetParentMethodTable();
747 AddDependency(pParentMT);
750 // Record edges generated by implementing interfaces.
751 MethodTable::InterfaceMapIterator it = pMT->IterateInterfaceMap();
754 AddDependency(it.GetInterface());
757 // Check all owned nodes for expanding cycles. The edges recorded above must all
758 // go from owned nodes so it suffices to look only at these.
759 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
761 if (HasExpandingCycle(&m_pNodes[i], &m_pNodes[i]))
768 // Returns TRUE iff the given type is already on the stack (in fact an analogue of
769 // code:TypeHandleList::Exists).
772 BOOL RecursionGraph::HasSeenType(RecursionGraph *pDepGraph, TypeHandle thType)
774 LIMITED_METHOD_CONTRACT;
776 while (pDepGraph != NULL)
778 if (pDepGraph->m_thOwner == thType) return TRUE;
779 pDepGraph = pDepGraph->m_pPrev;
784 // Adds the specified MT as a dependency (parent or interface) of the owner.
785 void RecursionGraph::AddDependency(MethodTable *pMT, TypeHandleList *pExpansionVars /*= NULL*/)
791 PRECONDITION(pMT != NULL);
796 // - If T appears as the actual type argument to be substituted for U in some referenced
797 // type D<..., U, ...> add a non-expanding (->) edge from T to U.
798 // - If T appears somewhere inside (but not as) the actual type argument to be substituted
799 // for U in referenced type D<..., U, ...> add an expanding (=>) edge from T to U.
801 // Non-generic dependencies are not interesting.
802 if (!pMT->HasInstantiation())
805 // Get the typical instantiation of pMT to figure out its type vars.
806 TypeHandle thTypical = ClassLoader::LoadTypeDefThrowing(
807 pMT->GetModule(), pMT->GetCl(),
808 ClassLoader::ThrowIfNotFound,
809 ClassLoader::PermitUninstDefOrRef, tdNoTypes,
810 CLASS_LOAD_APPROXPARENTS);
812 Instantiation inst = pMT->GetInstantiation();
813 Instantiation typicalInst = thTypical.GetInstantiation();
815 _ASSERTE(inst.GetNumArgs() == typicalInst.GetNumArgs());
817 for (DWORD i = 0; i < inst.GetNumArgs(); i++)
819 TypeHandle thArg = inst[i];
820 TypeHandle thVar = typicalInst[i];
821 if (thArg.IsGenericVariable())
823 // Add a non-expanding edge from thArg to i-th generic parameter of pMT.
824 AddEdge(thArg.AsGenericVariable(), thVar.AsGenericVariable(), FALSE);
826 // Process the backlog.
828 TypeHandleList *pList = pExpansionVars;
829 while (TypeHandleList::GetNext(&pList, &thTo))
831 AddEdge(thArg.AsGenericVariable(), thTo.AsGenericVariable(), TRUE);
836 while (thArg.IsTypeDesc())
838 _ASSERTE(thArg.HasTypeParam());
839 thArg = (static_cast<PTR_ParamTypeDesc>(thArg.AsTypeDesc()))->GetModifiedType();
841 if (thArg.IsGenericVariable()) // : A<!T[]>
843 // Add an expanding edge from thArg to i-th parameter of pMT.
844 AddEdge(thArg.AsGenericVariable(), thVar.AsGenericVariable(), TRUE);
849 if (!thArg.IsTypeDesc()) // : A<B<!T>>
851 // We will add an expanding edge but we do not yet know from which variable(s).
852 // Add the to-variable to the list and call recursively to inspect thArg's
854 TypeHandleList newExpansionVars(thVar, pExpansionVars);
855 AddDependency(thArg.AsMethodTable(), &newExpansionVars);
861 // Add an edge from pFromVar to pToVar - either non-expanding or expanding.
862 void RecursionGraph::AddEdge(TypeVarTypeDesc *pFromVar, TypeVarTypeDesc *pToVar, BOOL fExpanding)
868 PRECONDITION(pFromVar != NULL);
869 PRECONDITION(pToVar != NULL);
873 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",
874 (fExpanding ? "EXPANDING" : "NON-EXPANDING"),
875 pFromVar->GetToken(), pFromVar->GetModule(),
876 pToVar->GetToken(), pToVar->GetModule(),
877 m_thOwner.AsMethodTable()));
879 // Get the source node.
880 Node *pNode = &m_pNodes[pFromVar->GetIndex()];
881 _ASSERTE(pFromVar == pNode->GetSourceVar());
884 ULONG_PTR edge = (ULONG_PTR)pToVar;
885 if (fExpanding) edge |= Node::EDGE_EXPANDING_FLAG;
887 IfFailThrow(pNode->GetEdges()->Append((void *)edge));
890 // Recursive worker that checks whether this node is part of an expanding cycle.
891 BOOL RecursionGraph::HasExpandingCycle(Node *pCurrentNode, Node *pStartNode, BOOL fExpanded /*= FALSE*/)
897 PRECONDITION(CheckPointer(pCurrentNode));
898 PRECONDITION(CheckPointer(pStartNode));
902 // This method performs a modified DFS. We are not looking for any cycle but for a cycle
903 // which has at least one expanding edge. Therefore we:
904 // 1) Pass aroung the fExpanded flag to indicate that we've seen an expanding edge.
905 // 2) Explicitly check for returning to the starting point rather an arbitrary visited node.
907 // Did we just find the cycle?
908 if (fExpanded && pCurrentNode == pStartNode)
911 // Have we been here before or is this a dead end?
912 if (pCurrentNode->IsVisited() || pCurrentNode->GetEdges()->GetCount() == 0)
915 pCurrentNode->SetVisited();
917 ArrayList::Iterator iter = pCurrentNode->GetEdges()->Iterate();
920 ULONG_PTR edge = (ULONG_PTR)iter.GetElement();
922 BOOL fExpanding = (edge & Node::EDGE_EXPANDING_FLAG);
924 TypeVarTypeDesc *pToVar = (TypeVarTypeDesc *)(edge & ~Node::EDGE_EXPANDING_FLAG);
925 unsigned int dwIndex = pToVar->GetIndex();
928 RecursionGraph *pGraph = this;
930 // Find the destination node.
933 if (pGraph->m_pNodes != NULL &&
934 dwIndex < pGraph->m_thOwner.GetNumGenericArgs() &&
935 pGraph->m_pNodes[dwIndex].GetSourceVar() == pToVar)
937 pNode = &pGraph->m_pNodes[dwIndex];
940 pGraph = pGraph->m_pPrev;
942 while (pGraph != NULL);
946 // The new path is expanding if it was expanding already or if the edge we follow is expanding.
947 if (HasExpandingCycle(pNode, pStartNode, fExpanded || fExpanding))
952 pCurrentNode->ClearVisited();
957 } // namespace Generics
959 #endif // !DACCESS_COMPILE
965 * GetExactInstantiationsOfMethodAndItsClassFromCallInformation
967 * This routine takes in the various pieces of information of a call site to managed code
968 * and returns the exact instatiations for the method and the class on which the method is defined.
971 * pRepMethod - A MethodDesc to the representative instantiation method.
972 * pThis - The OBJECTREF that is being passed to pRepMethod.
973 * pParamTypeArg - The extra argument passed to pRepMethod when pRepMethod is either
974 * RequiresInstMethodTableArg() or RequiresInstMethodDescArg().
975 * pSpecificClass - A pointer to a TypeHandle for storing the exact instantiation
976 * of the class on which pRepMethod is defined, based on the call information
977 * pSpecificMethod - A pointer to a MethodDesc* for storing the exact instantiation
978 * of pRepMethod, based on the call information
981 * TRUE if successful.
982 * FALSE if could not get the exact TypeHandle & MethodDesc requested. In this case,
983 * the SpecificClass may be correct, iff the class is not a generic class.
986 BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation(
987 /* in */ MethodDesc *pRepMethod,
988 /* in */ OBJECTREF pThis,
989 /* in */ PTR_VOID pParamTypeArg,
990 /* out*/ TypeHandle *pSpecificClass,
991 /* out*/ MethodDesc** pSpecificMethod
1000 PRECONDITION(CheckPointer(pRepMethod));
1005 PTR_VOID pExactGenericArgsToken = NULL;
1007 if (pRepMethod->AcquiresInstMethodTableFromThis())
1011 // We could be missing the memory from a dump, or the target could have simply been corrupted.
1012 ALLOW_DATATARGET_MISSING_MEMORY(
1013 pExactGenericArgsToken = dac_cast<PTR_VOID>(pThis->GetMethodTable());
1019 pExactGenericArgsToken = pParamTypeArg;
1022 return GetExactInstantiationsOfMethodAndItsClassFromCallInformation(pRepMethod, pExactGenericArgsToken,
1023 pSpecificClass, pSpecificMethod);
1026 BOOL GetExactInstantiationsOfMethodAndItsClassFromCallInformation(
1027 /* in */ MethodDesc *pRepMethod,
1028 /* in */ PTR_VOID pExactGenericArgsToken,
1029 /* out*/ TypeHandle *pSpecificClass,
1030 /* out*/ MethodDesc** pSpecificMethod
1039 PRECONDITION(CheckPointer(pRepMethod));
1045 // Start with some decent default values.
1047 MethodDesc * pMD = pRepMethod;
1048 MethodTable * pMT = pRepMethod->GetMethodTable();
1050 *pSpecificMethod = pMD;
1051 *pSpecificClass = pMT;
1053 if (!pRepMethod->IsSharedByGenericInstantiations())
1058 if (pExactGenericArgsToken == NULL)
1063 BOOL retVal = FALSE;
1065 // The following target memory reads will not necessarily succeed against dumps, and will throw on failure.
1066 EX_TRY_ALLOW_DATATARGET_MISSING_MEMORY
1068 if (pRepMethod->RequiresInstMethodTableArg())
1070 pMT = dac_cast<PTR_MethodTable>(pExactGenericArgsToken);
1073 else if (pRepMethod->RequiresInstMethodDescArg())
1075 pMD = dac_cast<PTR_MethodDesc>(pExactGenericArgsToken);
1076 pMT = pMD->GetMethodTable();
1079 else if (pRepMethod->AcquiresInstMethodTableFromThis())
1081 // The exact token might actually be a child class of the class containing
1082 // the specified function so walk up the parent chain to make sure we return
1083 // an exact instantiation of the CORRECT parent class.
1084 pMT = pMD->GetExactDeclaringType(dac_cast<PTR_MethodTable>(pExactGenericArgsToken));
1085 _ASSERTE(pMT != NULL);
1090 _ASSERTE(!"Should not happen.");
1093 EX_END_CATCH_ALLOW_DATATARGET_MISSING_MEMORY
1095 *pSpecificMethod = pMD;
1096 *pSpecificClass = pMT;
1101 } // namespace Generics;