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.
4// File: genmeth.cpp
5//
6// Most functionality for generic methods is put here
7//
8
9
10
11#include "common.h"
12#include "method.hpp"
13#include "field.h"
14#include "eeconfig.h"
15#include "perfcounters.h"
16#include "crst.h"
17#include "generics.h"
18#include "genericdict.h"
19#include "instmethhash.h"
20#include "typestring.h"
21#include "typedesc.h"
22#include "comdelegate.h"
23
24// Instantiated generic methods
25//
26// Method descriptors for instantiated generic methods are allocated on demand and inserted
27// into the InstMethodHashTable for the LoaderModule of the descriptor. (See ceeload.h for more
28// information about loader modules).
29//
30// For non-shared instantiations, entering the prestub for such a method descriptor causes the method to
31// be JIT-compiled, specialized to that instantiation.
32//
33// For shared instantiations, entering the prestub generates a piece of stub code that passes the
34// method descriptor as an extra argument and then jumps to code shared between compatible
35// instantiations. This code has its own method descriptor whose instantiation is *canonical*
36// (with reference-type type parameters replaced by Object).
37//
38// Thus for example the shared method descriptor for m<object> is different to the
39// exact-instantiation method descriptor for m<object>.
40//
41// Complete example:
42//
43// class C<T> { public void m<S>(S x, T y) { ... } }
44//
45// Suppose that code sharing is turned on.
46//
47// Upon compiling calls to C<string>.m<string>, C<string>.m<Type>, C<Type>.m<string> and C<Type>.m<Type>
48
49// Given a generic method descriptor and an instantiation, create a new instantiated method
50// descriptor and chain it into the list attached to the generic method descriptor
51//
52// pMT is the owner method table. If looking for a shared MD this should be
53// the MT for the shared class.
54//
55// pGenericMD is the generic method descriptor (owner may be instantiated)
56// pWrappedMD is the corresponding shared md for use when creating stubs
57// nGenericMethodArgs/genericMethodArgs is the instantiation
58// getWrappedCode=TRUE if you want a shared instantiated md whose code expects an extra argument. In this
59// case pWrappedMD should be NULL.
60//
61// The result is put in ppMD
62//
63// If getWrappedCode. In thise case the genericMethodArgs
64// should be the normalized representative genericMethodArgs (see typehandle.h)
65//
66
67
68// Helper method that creates a method-desc off a template method desc
69static MethodDesc* CreateMethodDesc(LoaderAllocator *pAllocator,
70 MethodTable *pMT,
71 MethodDesc *pTemplateMD,
72 DWORD classification,
73 BOOL fNativeCodeSlot,
74 BOOL fComPlusCallInfo,
75 AllocMemTracker *pamTracker)
76{
77 CONTRACTL
78 {
79 THROWS;
80 GC_TRIGGERS;
81 INJECT_FAULT(COMPlusThrowOM(););
82 PRECONDITION(CheckPointer(pAllocator));
83 PRECONDITION(CheckPointer(pMT));
84 PRECONDITION(CheckPointer(pTemplateMD));
85 PRECONDITION(pTemplateMD->IsRestored());
86 PRECONDITION(pMT->IsRestored_NoLogging());
87 PRECONDITION(pTemplateMD->GetMethodTable()->GetCanonicalMethodTable() == pMT->GetCanonicalMethodTable());
88 }
89 CONTRACTL_END
90
91 mdMethodDef token = pTemplateMD->GetMemberDef();
92
93 // Create a singleton chunk for the method desc
94 MethodDescChunk *pChunk =
95 MethodDescChunk::CreateChunk(pAllocator->GetHighFrequencyHeap(),
96 1,
97 classification,
98 TRUE /* fNonVtableSlot*/,
99 fNativeCodeSlot,
100 fComPlusCallInfo,
101 pMT,
102 pamTracker);
103
104 // Now initialize the MDesc at the single method descriptor in
105 // the new chunk
106 MethodDesc *pMD = pChunk->GetFirstMethodDesc();
107
108 //We copy over the flags one by one. This is fragile w.r.t. adding
109 // new flags, but other techniques are also fragile. <NICE>We should move
110 // to using constructors on MethodDesc</NICE>
111 if (pTemplateMD->IsStatic())
112 {
113 pMD->SetStatic();
114 }
115 if (pTemplateMD->IsNotInline())
116 {
117 pMD->SetNotInline(true);
118 }
119 if (pTemplateMD->IsSynchronized())
120 {
121 pMD->SetSynchronized();
122 }
123 if (pTemplateMD->IsJitIntrinsic())
124 {
125 pMD->SetIsJitIntrinsic();
126 }
127
128 pMD->SetMemberDef(token);
129 pMD->SetSlot(pTemplateMD->GetSlot());
130
131#ifdef _DEBUG
132 pMD->m_pszDebugMethodName = pTemplateMD->m_pszDebugMethodName;
133 //<NICE> more info here</NICE>
134 pMD->m_pszDebugMethodSignature = "<generic method signature>";
135 pMD->m_pszDebugClassName = "<generic method class name>";
136 pMD->m_pszDebugMethodName = "<generic method name>";
137 pMD->m_pDebugMethodTable.SetValue(pMT);
138#endif // _DEBUG
139
140 return pMD;
141}
142
143//
144// The following methods map between tightly bound boxing and unboxing MethodDesc.
145// We always layout boxing and unboxing MethodDescs next to each other in same
146// MethodDescChunk. It allows us to avoid brute-force iteration over all methods
147// on the type to perform the mapping.
148//
149
150//
151// Find matching tightly-bound methoddesc
152//
153static MethodDesc * FindTightlyBoundWrappedMethodDesc(MethodDesc * pMD)
154{
155 CONTRACTL
156 {
157 NOTHROW;
158 GC_NOTRIGGER;
159 PRECONDITION(CheckPointer(pMD));
160 }
161 CONTRACTL_END
162
163 if (pMD->IsUnboxingStub() && pMD->GetClassification() == mcInstantiated)
164 pMD = pMD->AsInstantiatedMethodDesc()->IMD_GetWrappedMethodDesc();
165
166 // Find matching MethodDesc in the MethodTable
167 if (!pMD->IsTightlyBoundToMethodTable())
168 pMD = pMD->GetCanonicalMethodTable()->GetParallelMethodDesc(pMD);
169 _ASSERTE(pMD->IsTightlyBoundToMethodTable());
170
171 // Real MethodDesc immediately follows unboxing stub
172 if (pMD->IsUnboxingStub())
173 pMD = MethodTable::IntroducedMethodIterator::GetNext(pMD);
174 _ASSERTE(!pMD->IsUnboxingStub());
175
176 return pMD;
177}
178
179//
180// Find matching tightly-bound unboxing stub if there is one
181//
182static MethodDesc * FindTightlyBoundUnboxingStub(MethodDesc * pMD)
183{
184 CONTRACTL
185 {
186 NOTHROW;
187 GC_NOTRIGGER;
188 PRECONDITION(CheckPointer(pMD));
189 }
190 CONTRACTL_END
191
192 // Find matching MethodDesc in the MethodTable
193 if (!pMD->IsTightlyBoundToMethodTable())
194 pMD = pMD->GetCanonicalMethodTable()->GetParallelMethodDesc(pMD);
195 _ASSERTE(pMD->IsTightlyBoundToMethodTable());
196
197 // We are done if we have unboxing stub already
198 if (pMD->IsUnboxingStub())
199 return pMD;
200
201 //
202 // Unboxing stub immediately precedes real methoddesc
203 //
204 MethodDesc * pCurMD = pMD->GetMethodDescChunk()->GetFirstMethodDesc();
205
206 if (pCurMD == pMD)
207 return NULL;
208
209 for (;;)
210 {
211 MethodDesc * pNextMD = MethodTable::IntroducedMethodIterator::GetNext(pCurMD);
212 if (pNextMD == pMD)
213 break;
214 pCurMD = pNextMD;
215 }
216
217 return pCurMD->IsUnboxingStub() ? pCurMD : NULL;
218}
219
220#ifdef _DEBUG
221//
222// Alternative brute-force implementation of FindTightlyBoundWrappedMethodDesc for debug-only check.
223//
224// Please note that this does not do the same up-front checks as the non-debug version to
225// see whether or not the input pMD is even an unboxing stub in the first place.
226//
227static MethodDesc * FindTightlyBoundWrappedMethodDesc_DEBUG(MethodDesc * pMD)
228{
229 CONTRACTL
230 {
231 THROWS;
232 GC_NOTRIGGER;
233 PRECONDITION(CheckPointer(pMD));
234 }
235 CONTRACTL_END
236
237 mdMethodDef methodDef = pMD->GetMemberDef();
238 Module *pModule = pMD->GetModule();
239
240 MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
241 it.MoveToEnd();
242 for (; it.IsValid(); it.Prev()) {
243 if (!it.IsVirtual()) {
244 // Get the MethodDesc for current method
245 MethodDesc* pCurMethod = it.GetMethodDesc();
246
247 if (pCurMethod && !pCurMethod->IsUnboxingStub()) {
248 if ((pCurMethod->GetMemberDef() == methodDef) &&
249 (pCurMethod->GetModule() == pModule))
250 {
251 return pCurMethod;
252 }
253 }
254 }
255 }
256 return NULL;
257}
258
259//
260// Alternative brute-force implementation of FindTightlyBoundUnboxingStub for debug-only check
261//
262// Please note that this does not do the same up-front checks as the non-debug version to
263// see whether or not the input pMD even qualifies to have a corresponding unboxing stub.
264//
265static MethodDesc * FindTightlyBoundUnboxingStub_DEBUG(MethodDesc * pMD)
266{
267 CONTRACTL
268 {
269 THROWS;
270 GC_NOTRIGGER;
271 PRECONDITION(CheckPointer(pMD));
272 }
273 CONTRACTL_END
274
275 mdMethodDef methodDef = pMD->GetMemberDef();
276 Module *pModule = pMD->GetModule();
277
278 MethodTable::MethodIterator it(pMD->GetCanonicalMethodTable());
279 it.MoveToEnd();
280 for (; it.IsValid(); it.Prev()) {
281 if (it.IsVirtual()) {
282 MethodDesc* pCurMethod = it.GetMethodDesc();
283 if (pCurMethod && pCurMethod->IsUnboxingStub()) {
284 if ((pCurMethod->GetMemberDef() == methodDef) &&
285 (pCurMethod->GetModule() == pModule)) {
286 return pCurMethod;
287 }
288 }
289 }
290 }
291 return NULL;
292}
293#endif // _DEBUG
294
295/* static */
296InstantiatedMethodDesc *
297InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT,
298 MethodDesc* pGenericMDescInRepMT,
299 MethodDesc* pWrappedMD,
300 Instantiation methodInst,
301 BOOL getWrappedCode)
302{
303 CONTRACT(InstantiatedMethodDesc*)
304 {
305 THROWS;
306 GC_TRIGGERS;
307 INJECT_FAULT(COMPlusThrowOM(););
308 PRECONDITION(CheckPointer(pExactMT));
309 PRECONDITION(CheckPointer(pGenericMDescInRepMT));
310 PRECONDITION(pGenericMDescInRepMT->IsRestored());
311 PRECONDITION(pWrappedMD == NULL || pWrappedMD->IsRestored());
312 PRECONDITION(methodInst.IsEmpty() || pGenericMDescInRepMT->IsGenericMethodDefinition());
313 PRECONDITION(methodInst.GetNumArgs() == pGenericMDescInRepMT->GetNumGenericMethodArgs());
314 POSTCONDITION(CheckPointer(RETVAL));
315 POSTCONDITION(RETVAL->IsRestored());
316 POSTCONDITION(getWrappedCode == RETVAL->IsSharedByGenericInstantiations());
317 POSTCONDITION(methodInst.IsEmpty() || RETVAL->HasMethodInstantiation());
318 }
319 CONTRACT_END;
320
321 // All instantiated method descs live off the RepMT for the
322 // instantiated class they live in.
323 INDEBUG(MethodTable * pCanonMT = pExactMT->GetCanonicalMethodTable();)
324
325 _ASSERTE(pGenericMDescInRepMT->GetMethodTable() == pCanonMT);
326
327 if (getWrappedCode)
328 {
329 _ASSERTE(pWrappedMD == NULL);
330 _ASSERTE(pExactMT->IsCanonicalMethodTable());
331 _ASSERTE(pCanonMT == pExactMT);
332 _ASSERTE(pExactMT->IsSharedByGenericInstantiations() || ClassLoader::IsSharableInstantiation(methodInst));
333
334 }
335
336 InstantiatedMethodDesc *pNewMD;
337 //@todo : move this into the domain
338 Module * pExactMDLoaderModule = ClassLoader::ComputeLoaderModule(pExactMT, pGenericMDescInRepMT->GetMemberDef(), methodInst);
339
340 LoaderAllocator * pAllocator = pExactMDLoaderModule->GetLoaderAllocator();
341
342 // Create LoaderAllocator to LoaderAllocator links for members of the instantiations of this method
343 pAllocator->EnsureInstantiation(pExactMT->GetLoaderModule(), pExactMT->GetInstantiation());
344 pAllocator->EnsureInstantiation(pGenericMDescInRepMT->GetLoaderModule(), methodInst);
345
346 {
347 // Acquire crst to prevent tripping up other threads searching in the same hashtable
348 CrstHolder ch(&pExactMDLoaderModule->m_InstMethodHashTableCrst);
349
350 // Check whether another thread beat us to it!
351 pNewMD = FindLoadedInstantiatedMethodDesc(pExactMT,
352 pGenericMDescInRepMT->GetMemberDef(),
353 methodInst,
354 getWrappedCode);
355
356 // Crst goes out of scope here
357 // We don't need to hold the crst while we build the MethodDesc, but we reacquire it later
358 }
359
360 if (pNewMD != NULL)
361 {
362 pNewMD->CheckRestore();
363 }
364 else
365 {
366 TypeHandle *pInstOrPerInstInfo = NULL;
367 DictionaryLayout *pDL = NULL;
368 DWORD infoSize = 0;
369 IBCLoggerAwareAllocMemTracker amt;
370
371 if (!methodInst.IsEmpty())
372 {
373 if (pWrappedMD)
374 {
375 if (pWrappedMD->IsSharedByGenericMethodInstantiations())
376 {
377 pDL = pWrappedMD->AsInstantiatedMethodDesc()->GetDictLayoutRaw();
378 }
379 }
380 else if (getWrappedCode)
381 {
382 // 4 seems like a good number
383 pDL = DictionaryLayout::Allocate(4, pAllocator, &amt);
384#ifdef _DEBUG
385 {
386 SString name;
387 TypeString::AppendMethodDebug(name, pGenericMDescInRepMT);
388 LOG((LF_JIT, LL_INFO1000, "GENERICS: Created new dictionary layout for dictionary of size %d for %S\n",
389 DictionaryLayout::GetFirstDictionaryBucketSize(pGenericMDescInRepMT->GetNumGenericMethodArgs(), pDL), name.GetUnicode()));
390 }
391#endif // _DEBUG
392 }
393
394 // Allocate space for the instantiation and dictionary
395 infoSize = DictionaryLayout::GetFirstDictionaryBucketSize(methodInst.GetNumArgs(), pDL);
396 pInstOrPerInstInfo = (TypeHandle *) (void*) amt.Track(pAllocator->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(infoSize)));
397 for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
398 pInstOrPerInstInfo[i] = methodInst[i];
399 }
400
401 BOOL forComInterop = FALSE;
402#ifdef FEATURE_COMINTEROP
403 if (pExactMT->IsProjectedFromWinRT())
404 {
405 forComInterop = (pExactMT->IsInterface() || (pExactMT->IsDelegate() && COMDelegate::IsDelegateInvokeMethod(pGenericMDescInRepMT)));
406 }
407 else
408 {
409 // redirected interfaces and delegates also support interop
410 forComInterop = (pExactMT->IsWinRTRedirectedInterface(TypeHandle::Interop_ManagedToNative) ||
411 (pExactMT->IsWinRTRedirectedDelegate() && COMDelegate::IsDelegateInvokeMethod(pGenericMDescInRepMT)));
412 }
413#endif // FEATURE_COMINTEROP
414
415 // Create a new singleton chunk for the new instantiated method descriptor
416 // Notice that we've passed in the method table pointer; this gets
417 // used in some of the subsequent setup methods for method descs.
418 //
419 pNewMD = (InstantiatedMethodDesc*) (CreateMethodDesc(pAllocator,
420 pExactMT,
421 pGenericMDescInRepMT,
422 mcInstantiated,
423 !pWrappedMD, // This is pesimistic estimate for fNativeCodeSlot
424 forComInterop,
425 &amt));
426
427 // Initialize the MD the way it needs to be
428 if (pWrappedMD)
429 {
430 pNewMD->SetupWrapperStubWithInstantiations(pWrappedMD, methodInst.GetNumArgs(), pInstOrPerInstInfo);
431 _ASSERTE(pNewMD->IsInstantiatingStub());
432 }
433 else if (getWrappedCode)
434 {
435 pNewMD->SetupSharedMethodInstantiation(methodInst.GetNumArgs(), pInstOrPerInstInfo, pDL);
436 _ASSERTE(!pNewMD->IsInstantiatingStub());
437 }
438 else
439 {
440 pNewMD->SetupUnsharedMethodInstantiation(methodInst.GetNumArgs(), pInstOrPerInstInfo);
441 }
442
443 // Check that whichever field holds the inst. got setup correctly
444 _ASSERTE((PVOID)pNewMD->GetMethodInstantiation().GetRawArgs() == (PVOID)pInstOrPerInstInfo);
445
446 pNewMD->SetTemporaryEntryPoint(pAllocator, &amt);
447
448 {
449 // The canonical instantiation is exempt from constraint checks. It's used as the basis
450 // for all other reference instantiations so we can't not load it. The Canon type is
451 // not visible to users so it can't be abused.
452
453 BOOL fExempt =
454 TypeHandle::IsCanonicalSubtypeInstantiation(methodInst) ||
455 TypeHandle::IsCanonicalSubtypeInstantiation(pNewMD->GetClassInstantiation());
456
457 if (!fExempt)
458 {
459 pNewMD->SatisfiesMethodConstraints(TypeHandle(pExactMT), TRUE);
460 }
461 }
462
463 // OK, now we have a candidate MethodDesc.
464 {
465 CrstHolder ch(&pExactMDLoaderModule->m_InstMethodHashTableCrst);
466
467 // We checked before, but make sure again that another thread didn't beat us to it!
468 InstantiatedMethodDesc *pOldMD = FindLoadedInstantiatedMethodDesc(pExactMT,
469 pGenericMDescInRepMT->GetMemberDef(),
470 methodInst,
471 getWrappedCode);
472
473 if (pOldMD == NULL)
474 {
475 // No one else got there first, our MethodDesc wins.
476 amt.SuppressRelease();
477
478#ifdef _DEBUG
479 SString name(SString::Utf8);
480 TypeString::AppendMethodDebug(name, pNewMD);
481 StackScratchBuffer buff;
482 const char* pDebugNameUTF8 = name.GetUTF8(buff);
483 const char* verb = "Created";
484 if (pWrappedMD)
485 LOG((LF_CLASSLOADER, LL_INFO1000,
486 "GENERICS: %s instantiating-stub method desc %s with dictionary size %d\n",
487 verb, pDebugNameUTF8, infoSize));
488 else
489 LOG((LF_CLASSLOADER, LL_INFO1000,
490 "GENERICS: %s instantiated method desc %s\n",
491 verb, pDebugNameUTF8));
492
493 S_SIZE_T safeLen = S_SIZE_T(strlen(pDebugNameUTF8))+S_SIZE_T(1);
494 if(safeLen.IsOverflow()) COMPlusThrowHR(COR_E_OVERFLOW);
495
496 size_t len = safeLen.Value();
497 pNewMD->m_pszDebugMethodName = (char*) (void*)pAllocator->GetLowFrequencyHeap()->AllocMem(safeLen);
498 _ASSERTE(pNewMD->m_pszDebugMethodName);
499 strcpy_s((char *) pNewMD->m_pszDebugMethodName, len, pDebugNameUTF8);
500 pNewMD->m_pszDebugClassName = pExactMT->GetDebugClassName();
501 pNewMD->m_pszDebugMethodSignature = (LPUTF8)pNewMD->m_pszDebugMethodName;
502#endif // _DEBUG
503
504 // Generic methods can't be varargs. code:MethodTableBuilder::ValidateMethods should have checked it.
505 _ASSERTE(!pNewMD->IsVarArg());
506
507 // Verify that we are not creating redundant MethodDescs
508 _ASSERTE(!pNewMD->IsTightlyBoundToMethodTable());
509
510 // The method desc is fully set up; now add to the table
511 InstMethodHashTable* pTable = pExactMDLoaderModule->GetInstMethodHashTable();
512 pTable->InsertMethodDesc(pNewMD);
513 }
514 else
515 pNewMD = pOldMD;
516 // CrstHolder goes out of scope here
517 }
518
519 }
520
521 RETURN pNewMD;
522}
523
524// Calling this method is equivalent to
525// FindOrCreateAssociatedMethodDesc(pCanonicalMD, pExactMT, FALSE, Instantiation(), FALSE, TRUE)
526// except that it also creates InstantiatedMethodDescs based on shared class methods. This is
527// convenient for interop where, unlike ordinary managed methods, marshaling stubs for say Foo<string>
528// and Foo<object> look very different and need separate representation.
529InstantiatedMethodDesc*
530InstantiatedMethodDesc::FindOrCreateExactClassMethod(MethodTable *pExactMT,
531 MethodDesc *pCanonicalMD)
532{
533 CONTRACTL
534 {
535 THROWS;
536 GC_TRIGGERS;
537 MODE_ANY;
538 PRECONDITION(!pExactMT->IsSharedByGenericInstantiations());
539 PRECONDITION(pCanonicalMD->IsSharedByGenericInstantiations());
540 }
541 CONTRACTL_END;
542
543 InstantiatedMethodDesc *pInstMD = FindLoadedInstantiatedMethodDesc(pExactMT,
544 pCanonicalMD->GetMemberDef(),
545 Instantiation(),
546 FALSE);
547
548 if (pInstMD == NULL)
549 {
550 // create a new MD if not found
551 pInstMD = NewInstantiatedMethodDesc(pExactMT,
552 pCanonicalMD,
553 pCanonicalMD,
554 Instantiation(),
555 FALSE);
556 }
557
558 return pInstMD;
559}
560
561// N.B. it is not guarantee that the returned InstantiatedMethodDesc is restored.
562// It is the caller's responsibility to call CheckRestore on the returned value.
563/* static */
564InstantiatedMethodDesc*
565InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(MethodTable *pExactOrRepMT,
566 mdMethodDef methodDef,
567 Instantiation methodInst,
568 BOOL getWrappedCode)
569{
570 CONTRACT(InstantiatedMethodDesc *)
571 {
572 THROWS;
573 GC_NOTRIGGER;
574 FORBID_FAULT;
575 PRECONDITION(CheckPointer(pExactOrRepMT));
576
577 // All wrapped method descriptors (except BoxedEntryPointStubs, which don't use this path) are
578 // canonical and exhibit some kind of code sharing.
579 PRECONDITION(!getWrappedCode || pExactOrRepMT->IsCanonicalMethodTable());
580 PRECONDITION(!getWrappedCode || pExactOrRepMT->IsSharedByGenericInstantiations() || ClassLoader::IsSharableInstantiation(methodInst));
581
582 // Unboxing stubs are dealt with separately in FindOrCreateAssociatedMethodDesc. This should
583 // probably be streamlined...
584 POSTCONDITION(!RETVAL || !RETVAL->IsUnboxingStub());
585
586 // All wrapped method descriptors (except BoxedEntryPointStubs, which don't use this path) take an inst arg.
587 // The only ones that don't should have been found in the type's meth table.
588 POSTCONDITION(!getWrappedCode || !RETVAL || !RETVAL->IsRestored() || RETVAL->RequiresInstArg());
589 }
590 CONTRACT_END
591
592
593 // First look in the table for the runtime loader module in case someone created it before any
594 // zap modules got loaded
595 Module *pLoaderModule = ClassLoader::ComputeLoaderModule(pExactOrRepMT, methodDef, methodInst);
596
597 InstMethodHashTable* pTable = pLoaderModule->GetInstMethodHashTable();
598 MethodDesc *resultMD = pTable->FindMethodDesc(TypeHandle(pExactOrRepMT),
599 methodDef,
600 FALSE /* not forceBoxedEntryPoint */,
601 methodInst,
602 getWrappedCode);
603
604 if (resultMD != NULL)
605 RETURN((InstantiatedMethodDesc*) resultMD);
606
607#ifdef FEATURE_PREJIT
608 // Next look in the preferred zap module
609 Module *pPreferredZapModule = Module::ComputePreferredZapModule(pExactOrRepMT->GetModule(),
610 pExactOrRepMT->GetInstantiation(),
611 methodInst);
612 if (pPreferredZapModule->HasNativeImage())
613 {
614 resultMD = pPreferredZapModule->GetInstMethodHashTable()->FindMethodDesc(TypeHandle(pExactOrRepMT),
615 methodDef,
616 FALSE /* not forceBoxedEntryPoint */,
617 methodInst,
618 getWrappedCode);
619
620 if (resultMD != NULL)
621 RETURN((InstantiatedMethodDesc*) resultMD);
622 }
623#endif // FEATURE_PREJIT
624
625 RETURN(NULL);
626}
627
628
629// Given a method descriptor, find (or create) an instantiated
630// method descriptor or BoxedEntryPointStub associated with that method
631// and a particular instantiation of any generic method arguments. Also check
632// the method instantiation is valid.
633//
634// This routine also works for non-generic methods - it will be fast
635// in most cases. In this case nothing in particular
636// occurs except for static methods in shared generic classes, where an
637// instantiating stub is needed.
638//
639// The generic parameters provided are only those for the generic method.
640// pExactMT should be used to specify any class parameters.
641//
642// Unboxing stubs
643// --------------
644//
645// These are required to provide callable addresses with a uniform calling convention
646// for all methods on a boxed value class object. There are a wide range of possible
647// methods:
648// 1 virtual, non-generic instance methods
649// 2 non-virtual, non-generic instance methods
650// 3 virtual, generic instance methods
651// 4 non-virtual, generic instance methods
652// There is no substantial difference between case 3 and case 4: the only times
653// when BoxedEntryPointStubs are used for non-virtual methods are when calling a delegate or
654// making a reflection call.
655//
656// The only substantial difference between 1 and 2 is that the stubs are stored in
657// different places - we are forced to create the BoxedEntryPointStubs for (1) at class
658// creation time (they form part of the vtable and dispatch maps). Hence these
659// stubs are "owned" by method tables. We store all other stubs in the AssociatedMethTable.
660//
661// Unboxing stubs and generics
662// ---------------------------
663//
664// Generics code sharing complicates matters. The typical cases are where the struct
665// is in a shared-codegenerics struct such as
666//
667// struct Pair<string,object>
668//
669// which shares code with other types such as Pair<object,object>. All the code that ends up
670// being run for all the methods in such a struct takes an instantiation parameter, i.e.
671// is RequiresInstArg(), a non-uniform calling convention. We obviously can't give these out as
672// targets of delegate calls. Hence we have to wrap this shared code in various stubs in
673// order to get the right type context parameter provided to the shared code.
674//
675// Unboxing stubs on shared-code generic structs, e.g. Pair<object,string>,
676// acquire the class-portion of their type context from the "this" pointer.
677//
678// Thus there are two flavours of BoxedEntryPointStubs:
679//
680// - Methods that are not themselves generic:
681//
682// These wrap possibly-shared code (hence allowInstParam == TRUE).
683//
684// These directly call the possible-shared code for the instance method. This code
685// can have the following calling conventions:
686// - RequiresMethodTableInstArg() (if pMT->SharedByGenericInstantiations())
687// - Uniform (if !pMT->SharedByGenericInstantiations())
688//
689// Thus if the code they are
690//
691// - Methods that are themselves generic:
692//
693// These wrap unshared code (hence allowInstParam == FALSE):
694//
695// These are always invoked by slow paths (how often do you use a generic method in a struct?),
696// such as JIT_VirtualFunctionPointer or a reflection call. These paths eventually
697// use FindOrCreateAssociatedMethodDesc to piece together the exact instantiation provided by the "this"
698// pointer with the exact instantiation provided by the wrapped method pointer.
699//
700// These call a stub for the instance method which provides the instantiation
701// context, possibly in turn calling further shared code. This stub will
702// always be !RequiresInstArg()
703//
704// If the method being called is aMethod calls via BoxedEntryPointStubs
705//
706// Remotable methods
707// -----------------
708//
709// Remoting has high requirements for method descs passed to it (i.e. the method desc that represents the client "view" of the
710// method to be called on the real server object). Since the identity of the method call is serialized and passed on the wire before
711// be resolved into the real target method on the server remoting needs to be able to extract exact instantiation information from
712// its inputs (a method desc and a this pointer).
713//
714// To that end generic methods should always be passed via an instantiating stub (i.e. set allowInstParam to FALSE when calling
715// FindOrCreateAssociatedMethodDesc).
716//
717// There's a more subtle problem though. If the client method call is via a non-generic method on a generic interface we won't have
718// enough information to serialize the call. That's because such methods don't have instantiated method descs by default (these are
719// weighty structures and most of the runtime would never use the extra information). The this pointer doesn't help provide the
720// additional information in this case (consider the case of a class that implements both IFoo<String> and IFoo<Random>).
721//
722// So instead we create instantiated interface method descs on demand (i.e. during stub-based interface dispatch). Setting the
723// forceRemotableMethod predicate to TRUE below will ensure this (it's a no-op for methods that don't match this pattern, so can be
724// freely set to true for all calls intended to produce a remotable ready method). This characteristic of a methoddesc that is fully
725// descriptive of the method and class used is also necessary in certain places in reflection. In particular, it is known to be needed
726// for the Delegate.CreateDelegate logic.
727//
728// allowCreate may be set to FALSE to enforce that the method searched
729// should already be in existence - thus preventing creation and GCs during
730// inappropriate times.
731
732#ifdef _PREFAST_
733#pragma warning(push)
734#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
735#endif
736/* static */
737MethodDesc*
738MethodDesc::FindOrCreateAssociatedMethodDesc(MethodDesc* pDefMD,
739 MethodTable *pExactMT,
740 BOOL forceBoxedEntryPoint,
741 Instantiation methodInst,
742 BOOL allowInstParam,
743 BOOL forceRemotableMethod,
744 BOOL allowCreate,
745 ClassLoadLevel level)
746{
747 CONTRACT(MethodDesc*)
748 {
749 THROWS;
750 if (allowCreate) { GC_TRIGGERS; } else { GC_NOTRIGGER; }
751 INJECT_FAULT(COMPlusThrowOM(););
752
753 PRECONDITION(CheckPointer(pDefMD));
754 PRECONDITION(CheckPointer(pExactMT));
755 PRECONDITION(pDefMD->IsRestored_NoLogging());
756 PRECONDITION(pExactMT->IsRestored_NoLogging());
757
758 // If the method descriptor belongs to a generic type then
759 // the input exact type must be an instantiation of that type.
760 // DISABLED PRECONDITION - too strict - the classes may be in
761 // a subtype relation to each other.
762 //
763 // PRECONDITION(!pDefMD->HasClassInstantiation() || pDefMD->GetMethodTable()->HasSameTypeDefAs(pExactMT));
764
765 // You may only request an BoxedEntryPointStub for an instance method on a value type
766 PRECONDITION(!forceBoxedEntryPoint || pExactMT->IsValueType());
767 PRECONDITION(!forceBoxedEntryPoint || !pDefMD->IsStatic());
768
769 // For remotable methods we better not be allowing instantiation parameters.
770 PRECONDITION(!forceRemotableMethod || !allowInstParam);
771
772 POSTCONDITION(((RETVAL == NULL) && !allowCreate) || CheckPointer(RETVAL));
773 POSTCONDITION(((RETVAL == NULL) && !allowCreate) || RETVAL->IsRestored());
774 POSTCONDITION(((RETVAL == NULL) && !allowCreate) || forceBoxedEntryPoint || !RETVAL->IsUnboxingStub());
775 POSTCONDITION(((RETVAL == NULL) && !allowCreate) || allowInstParam || !RETVAL->RequiresInstArg());
776 }
777 CONTRACT_END;
778
779 // Quick exit for the common cases where the result is the same as the primary MD we are given
780 if (!pDefMD->HasClassOrMethodInstantiation() &&
781 methodInst.IsEmpty() &&
782 !forceBoxedEntryPoint &&
783 !pDefMD->IsUnboxingStub())
784 {
785 // Make sure that pDefMD->GetMethodTable() and pExactMT are related types even
786 // if we took the fast path.
787 _ASSERTE(pDefMD->IsArray() || pDefMD->GetExactDeclaringType(pExactMT) != NULL);
788
789 RETURN pDefMD;
790 }
791
792 // Get the version of the method desc. for the instantiated shared class, e.g.
793 // e.g. if pDefMD == List<T>.m()
794 // pExactMT = List<string>
795 // then pMDescInCanonMT = List<object>.m()
796 // or
797 // e.g. if pDefMD == List<T>.m<U>()
798 // pExactMT = List<string>
799 // then pMDescInCanonMT = List<object>.m<U>()
800
801 MethodDesc * pMDescInCanonMT = pDefMD;
802
803 // Some callers pass a pExactMT that is a subtype of a parent type of pDefMD.
804 // Find the actual exact parent of pDefMD.
805 pExactMT = pDefMD->GetExactDeclaringType(pExactMT);
806 _ASSERTE(pExactMT != NULL);
807
808 if (pDefMD->HasClassOrMethodInstantiation() || !methodInst.IsEmpty())
809 {
810 // General checks related to generics: arity (if any) must match and generic method
811 // instantiation (if any) must be well-formed.
812 if (pDefMD->GetNumGenericMethodArgs() != methodInst.GetNumArgs() ||
813 !Generics::CheckInstantiation(methodInst))
814 {
815 COMPlusThrowHR(COR_E_BADIMAGEFORMAT);
816 }
817
818 pMDescInCanonMT = pExactMT->GetCanonicalMethodTable()->GetParallelMethodDesc(pDefMD);
819
820 if (!allowCreate && (!pMDescInCanonMT->IsRestored() ||
821 !pMDescInCanonMT->GetMethodTable()->IsFullyLoaded()))
822
823 {
824 RETURN(NULL);
825 }
826
827 pMDescInCanonMT->CheckRestore(level);
828 }
829
830 // This case covers nearly all "normal" (i.e. non-associate) MethodDescs. Just return
831 // the MethodDesc in the canonical method table.
832 //
833 // Also, it will be taken for methods which acquire their type context from the "this" parameter
834 // - we don't need instantiating stubs for these.
835 if ( methodInst.IsEmpty()
836 && (allowInstParam || !pMDescInCanonMT->RequiresInstArg())
837 && (forceBoxedEntryPoint == pMDescInCanonMT->IsUnboxingStub())
838 && (!forceRemotableMethod || !pMDescInCanonMT->IsInterface()
839 || !pMDescInCanonMT->GetMethodTable()->IsSharedByGenericInstantiations()) )
840 {
841 RETURN(pMDescInCanonMT);
842 }
843
844 // Unboxing stubs
845 else if (forceBoxedEntryPoint)
846 {
847
848 // This assert isn't quite right, for example the repro from NDPWhidbey 18737
849 // fires it, because we fetch an BoxedEntryPointStub for a virtual method on a struct
850 // when the uninstantiated MethodDesc for the generic virtual method actually
851 // qualifies as an BoxedEntryPointStub... Hence we weaken the assert a little.
852 //
853 // _ASSERTE(!pDefMD->IsUnboxingStub());
854 // _ASSERTE(pDefMD->IsGenericMethodDefinition() || !pDefMD->IsUnboxingStub());
855
856 //Unboxing stubs for non-generic methods and generic methods are
857 // subtly different... For non-generic methods we can look in the
858 // shared vtable, and then go to the hash table only if needed.
859 // Furthermore even if we have to go to hash table we still base
860 // the BoxedEntryPointStub on an unerlying _shared_ method descriptor.
861 //
862 // For generic methods we must build an BoxedEntryPointStub that calls an
863 // underlying instantiating stub. The underlying instantiating stub
864 // will be an _exact_ method descriptor.
865 MethodDesc *pResultMD;
866 if (methodInst.IsEmpty())
867 {
868 // First search for the unboxing MD in the shared vtable for the value type
869 pResultMD = FindTightlyBoundUnboxingStub(pMDescInCanonMT);
870
871 // Verify that we get the same result by alternative method. There is a possibility
872 // that there is no associated unboxing stub, and FindTightlyBoundUnboxingStub takes
873 // this into account but the _DEBUG version does not, so only use it if the method
874 // returned is actually different.
875 _ASSERTE(pResultMD == pMDescInCanonMT ||
876 pResultMD == FindTightlyBoundUnboxingStub_DEBUG(pMDescInCanonMT));
877
878 if (pResultMD != NULL)
879 {
880 _ASSERTE(pResultMD->IsRestored() && pResultMD->GetMethodTable()->IsFullyLoaded());
881 g_IBCLogger.LogMethodDescAccess(pResultMD);
882 RETURN(pResultMD);
883 }
884
885 MethodTable *pRepMT = pMDescInCanonMT->GetMethodTable();
886 mdMethodDef methodDef = pDefMD->GetMemberDef();
887
888 Module *pLoaderModule = ClassLoader::ComputeLoaderModule(pRepMT, methodDef, methodInst);
889 LoaderAllocator* pAllocator=pLoaderModule->GetLoaderAllocator();
890
891 InstMethodHashTable* pTable = pLoaderModule->GetInstMethodHashTable();
892 // If we didn't find it there then go to the hash table
893 pResultMD = pTable->FindMethodDesc(TypeHandle(pRepMT),
894 methodDef,
895 TRUE /* forceBoxedEntryPoint */,
896 Instantiation(),
897 FALSE /* no inst param */);
898
899 // If we didn't find it then create it...
900 if (!pResultMD)
901 {
902 // !allowCreate ==> GC_NOTRIGGER ==> no entering Crst
903 if (!allowCreate)
904 {
905 RETURN(NULL);
906 }
907
908 CrstHolder ch(&pLoaderModule->m_InstMethodHashTableCrst);
909
910 // Check whether another thread beat us to it!
911 pResultMD = pTable->FindMethodDesc(TypeHandle(pRepMT),
912 methodDef,
913 TRUE,
914 Instantiation(),
915 FALSE);
916 if (pResultMD == NULL)
917 {
918 IBCLoggerAwareAllocMemTracker amt;
919
920 pResultMD = CreateMethodDesc(pAllocator,
921 pRepMT,
922 pMDescInCanonMT,
923 mcInstantiated,
924 FALSE /* fNativeCodeSlot */,
925 FALSE /* fComPlusCallInfo */,
926 &amt);
927
928 // Indicate that this is a stub method which takes a BOXed this pointer.
929 // An BoxedEntryPointStub may still be an InstantiatedMethodDesc
930 pResultMD->SetIsUnboxingStub();
931 pResultMD->AsInstantiatedMethodDesc()->SetupWrapperStubWithInstantiations(pMDescInCanonMT, NULL, NULL);
932
933 pResultMD->SetTemporaryEntryPoint(pAllocator, &amt);
934
935 amt.SuppressRelease();
936
937 // Verify that we are not creating redundant MethodDescs
938 _ASSERTE(!pResultMD->IsTightlyBoundToMethodTable());
939
940 // Add it to the table
941 pTable->InsertMethodDesc(pResultMD);
942 }
943
944 // CrstHolder goes out of scope here
945 }
946
947 }
948 else
949 {
950 mdMethodDef methodDef = pDefMD->GetMemberDef();
951
952 Module *pLoaderModule = ClassLoader::ComputeLoaderModule(pExactMT, methodDef, methodInst);
953 LoaderAllocator* pAllocator = pLoaderModule->GetLoaderAllocator();
954
955 InstMethodHashTable* pTable = pLoaderModule->GetInstMethodHashTable();
956 // First check the hash table...
957 pResultMD = pTable->FindMethodDesc(TypeHandle(pExactMT),
958 methodDef,
959 TRUE, /* forceBoxedEntryPoint */
960 methodInst,
961 FALSE /* no inst param */);
962
963 if (!pResultMD)
964 {
965 // !allowCreate ==> GC_NOTRIGGER ==> no entering Crst
966 if (!allowCreate)
967 {
968 RETURN(NULL);
969 }
970
971 // Enter the critical section *after* we've found or created the non-unboxing instantiating stub (else we'd have a race)
972 CrstHolder ch(&pLoaderModule->m_InstMethodHashTableCrst);
973
974 // Check whether another thread beat us to it!
975 pResultMD = pTable->FindMethodDesc(TypeHandle(pExactMT),
976 methodDef,
977 TRUE, /* forceBoxedEntryPoint */
978 methodInst,
979 FALSE /* no inst param */);
980
981 if (pResultMD == NULL)
982 {
983 // Recursively get the non-unboxing instantiating stub. Thus we chain an unboxing
984 // stub with an instantiating stub.
985 MethodDesc* pNonUnboxingStub=
986 MethodDesc::FindOrCreateAssociatedMethodDesc(pDefMD,
987 pExactMT,
988 FALSE /* not Unboxing */,
989 methodInst,
990 FALSE);
991
992 _ASSERTE(pNonUnboxingStub->GetClassification() == mcInstantiated);
993 _ASSERTE(!pNonUnboxingStub->RequiresInstArg());
994 _ASSERTE(!pNonUnboxingStub->IsUnboxingStub());
995
996 IBCLoggerAwareAllocMemTracker amt;
997
998 _ASSERTE(pDefMD->GetClassification() == mcInstantiated);
999
1000 pResultMD = CreateMethodDesc(pAllocator,
1001 pExactMT,
1002 pNonUnboxingStub,
1003 mcInstantiated,
1004 FALSE /* fNativeCodeSlot */,
1005 FALSE /* fComPlusCallInfo */,
1006 &amt);
1007
1008 pResultMD->SetIsUnboxingStub();
1009 pResultMD->AsInstantiatedMethodDesc()->SetupWrapperStubWithInstantiations(pNonUnboxingStub,
1010 pNonUnboxingStub->GetNumGenericMethodArgs(),
1011 (TypeHandle *)pNonUnboxingStub->GetMethodInstantiation().GetRawArgs());
1012
1013 pResultMD->SetTemporaryEntryPoint(pAllocator, &amt);
1014
1015 amt.SuppressRelease();
1016
1017 // Verify that we are not creating redundant MethodDescs
1018 _ASSERTE(!pResultMD->IsTightlyBoundToMethodTable());
1019
1020 pTable->InsertMethodDesc(pResultMD);
1021 }
1022
1023 // CrstHolder goes out of scope here
1024 }
1025 }
1026 _ASSERTE(pResultMD);
1027
1028 if (!allowCreate && (!pResultMD->IsRestored() || !pResultMD->GetMethodTable()->IsFullyLoaded()))
1029 {
1030 RETURN(NULL);
1031 }
1032
1033 pResultMD->CheckRestore(level);
1034 _ASSERTE(pResultMD->IsUnboxingStub());
1035 _ASSERTE(!pResultMD->IsInstantiatingStub());
1036 RETURN(pResultMD);
1037 }
1038
1039
1040 // Now all generic method instantiations and static/shared-struct-instance-method wrappers...
1041 else
1042 {
1043 _ASSERTE(!forceBoxedEntryPoint);
1044
1045 mdMethodDef methodDef = pDefMD->GetMemberDef();
1046 Module *pModule = pDefMD->GetModule();
1047
1048 // Some unboxed entry points are attached to canonical method tables. This is because
1049 // we have to fill in vtables and/or dispatch maps at load time,
1050 // and boxed entry points are created to do this. (note vtables and dispatch maps
1051 // are only created for canonical instantiations). These boxed entry points
1052 // in turn refer to unboxed entry points.
1053
1054 if (// Check if we're looking for something at the canonical instantiation
1055 (allowInstParam || pExactMT->IsCanonicalMethodTable()) &&
1056 // Only value types have BoxedEntryPointStubs in the canonical method table
1057 pExactMT->IsValueType() &&
1058 // The only generic methods whose BoxedEntryPointStubs are in the canonical method table
1059 // are those open MethodDescs at the "typical" isntantiation, e.g.
1060 // VC<int>.m<T>
1061 // <NICE> This is probably actually not needed </NICE>
1062 ClassLoader::IsTypicalInstantiation(pModule, methodDef, methodInst)
1063
1064 )
1065 {
1066 MethodDesc * pResultMD = FindTightlyBoundWrappedMethodDesc(pMDescInCanonMT);
1067
1068 // Verify that we get the same result by alternative method. There is a possibility
1069 // that this is not an unboxing stub, and FindTightlyBoundWrappedMethodDesc takes
1070 // this into account but the _DEBUG version does not, so only use it if the method
1071 // returned is actually different.
1072 _ASSERTE(pResultMD == pMDescInCanonMT ||
1073 pResultMD == FindTightlyBoundWrappedMethodDesc_DEBUG(pMDescInCanonMT));
1074
1075 if (pResultMD != NULL)
1076 {
1077 _ASSERTE(pResultMD->IsRestored() && pResultMD->GetMethodTable()->IsFullyLoaded());
1078
1079 g_IBCLogger.LogMethodDescAccess(pResultMD);
1080
1081 if (allowInstParam || !pResultMD->RequiresInstArg())
1082 {
1083 RETURN(pResultMD);
1084 }
1085 }
1086 }
1087
1088 // Are either the generic type arguments or the generic method arguments shared?
1089 BOOL sharedInst =
1090 pExactMT->GetCanonicalMethodTable()->IsSharedByGenericInstantiations()
1091 || ClassLoader::IsSharableInstantiation(methodInst);
1092
1093 // Is it the "typical" instantiation in the correct type that does not require wrapper?
1094 if (!sharedInst &&
1095 pExactMT == pMDescInCanonMT->GetMethodTable() &&
1096 ClassLoader::IsTypicalInstantiation(pModule, methodDef, methodInst))
1097 {
1098 _ASSERTE(!pMDescInCanonMT->IsUnboxingStub());
1099 RETURN(pMDescInCanonMT);
1100 }
1101
1102 // OK, so we now know the thing we're looking for can only be found in the MethodDesc table.
1103
1104 // If getWrappedCode == true, we are looking for a wrapped MethodDesc
1105
1106 BOOL getWrappedCode = allowInstParam && sharedInst;
1107 BOOL getWrappedThenStub = !allowInstParam && sharedInst;
1108
1109 CQuickBytes qbRepInst;
1110 TypeHandle *repInst = NULL;
1111 if (getWrappedCode || getWrappedThenStub)
1112 {
1113 // Canonicalize the type arguments.
1114 DWORD cbAllocaSize = 0;
1115 if (!ClrSafeInt<DWORD>::multiply(methodInst.GetNumArgs(), sizeof(TypeHandle), cbAllocaSize))
1116 ThrowHR(COR_E_OVERFLOW);
1117
1118 repInst = reinterpret_cast<TypeHandle *>(qbRepInst.AllocThrows(cbAllocaSize));
1119
1120 for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
1121 {
1122 repInst[i] = ClassLoader::CanonicalizeGenericArg(methodInst[i]);
1123 }
1124 }
1125
1126 // <NICE> These paths can probably be merged together more nicely, and the lookup-lock-lookup pattern made much
1127 // more obvious </NICE>
1128 InstantiatedMethodDesc *pInstMD;
1129 if (getWrappedCode)
1130 {
1131 // Get the underlying shared code using the canonical instantiations
1132 pInstMD =
1133 InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT->GetCanonicalMethodTable(),
1134 methodDef,
1135 Instantiation(repInst, methodInst.GetNumArgs()),
1136 TRUE);
1137
1138 // No - so create one.
1139 if (pInstMD == NULL)
1140 {
1141 if (!allowCreate)
1142 {
1143 RETURN(NULL);
1144 }
1145
1146 pInstMD = InstantiatedMethodDesc::NewInstantiatedMethodDesc(pExactMT->GetCanonicalMethodTable(),
1147 pMDescInCanonMT,
1148 NULL,
1149 Instantiation(repInst, methodInst.GetNumArgs()),
1150 TRUE);
1151 }
1152 }
1153 else if (getWrappedThenStub)
1154 {
1155 // See if we've already got the instantiated method desc for this one.
1156 pInstMD =
1157 InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT,
1158 methodDef,
1159 methodInst,
1160 FALSE);
1161
1162 // No - so create one. Go fetch the shared one first
1163 if (pInstMD == NULL)
1164 {
1165 if (!allowCreate)
1166 {
1167 RETURN(NULL);
1168 }
1169
1170 // This always returns the shared code. Repeat the original call except with
1171 // approximate params and allowInstParam=true
1172 MethodDesc* pWrappedMD = FindOrCreateAssociatedMethodDesc(pDefMD,
1173 pExactMT->GetCanonicalMethodTable(),
1174 FALSE,
1175 Instantiation(repInst, methodInst.GetNumArgs()),
1176 TRUE);
1177
1178 _ASSERTE(pWrappedMD->IsSharedByGenericInstantiations());
1179 _ASSERTE(!methodInst.IsEmpty() || !pWrappedMD->IsSharedByGenericMethodInstantiations());
1180
1181 pInstMD = InstantiatedMethodDesc::NewInstantiatedMethodDesc(pExactMT,
1182 pMDescInCanonMT,
1183 pWrappedMD,
1184 methodInst,
1185 FALSE);
1186 }
1187 }
1188 else
1189 {
1190 // See if we've already got the instantiated method desc for this one.
1191 // If looking for shared code use the representative inst.
1192 pInstMD =
1193 InstantiatedMethodDesc::FindLoadedInstantiatedMethodDesc(pExactMT,
1194 methodDef,
1195 methodInst,
1196 FALSE);
1197
1198 // No - so create one.
1199 if (pInstMD == NULL)
1200 {
1201 if (!allowCreate)
1202 {
1203 RETURN(NULL);
1204 }
1205
1206 pInstMD = InstantiatedMethodDesc::NewInstantiatedMethodDesc(pExactMT,
1207 pMDescInCanonMT,
1208 NULL,
1209 methodInst,
1210 FALSE);
1211 }
1212 }
1213 _ASSERTE(pInstMD);
1214
1215 if (!allowCreate && (!pInstMD->IsRestored() || !pInstMD->GetMethodTable()->IsFullyLoaded()))
1216 {
1217 RETURN(NULL);
1218 }
1219
1220 pInstMD->CheckRestore(level);
1221
1222 RETURN(pInstMD);
1223 }
1224}
1225#ifdef _PREFAST_
1226#pragma warning(pop)
1227#endif
1228
1229// Normalize the methoddesc for reflection
1230/*static*/ MethodDesc* MethodDesc::FindOrCreateAssociatedMethodDescForReflection(
1231 MethodDesc *pMethod,
1232 TypeHandle instType,
1233 Instantiation methodInst)
1234{
1235 CONTRACTL {
1236 THROWS;
1237 GC_TRIGGERS; // Because allowCreate is TRUE
1238 PRECONDITION(CheckPointer(pMethod));
1239 }
1240 CONTRACTL_END;
1241
1242 MethodDesc *pInstMD = pMethod;
1243
1244 // no stubs for TypeDesc
1245 if (instType.IsTypeDesc())
1246 return pInstMD;
1247
1248 MethodTable* pMT = instType.AsMethodTable();
1249
1250 if (!methodInst.IsEmpty())
1251 {
1252 // method.BindGenericParameters() was called and we need to retrieve an instantiating stub
1253
1254 // pMethod is not necessarily a generic method definition, ResolveMethod could pass in an
1255 // instantiated generic method.
1256 _ASSERTE(pMethod->HasMethodInstantiation());
1257
1258 if (methodInst.GetNumArgs() != pMethod->GetNumGenericMethodArgs())
1259 COMPlusThrow(kArgumentException);
1260
1261 // we base the creation of an unboxing stub on whether the original method was one already
1262 // that keeps the reflection logic the same for value types
1263 pInstMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
1264 pMethod,
1265 pMT,
1266 pMethod->IsUnboxingStub(),
1267 methodInst,
1268 FALSE, /* no allowInstParam */
1269 TRUE /* force remotable method (i.e. inst wrappers for non-generic methods on generic interfaces) */);
1270 }
1271 else if ( !pMethod->HasMethodInstantiation() &&
1272 ( instType.IsValueType() ||
1273 ( instType.HasInstantiation() &&
1274 !instType.IsGenericTypeDefinition() &&
1275 ( instType.IsInterface() || pMethod->IsStatic() ) ) ) )
1276 {
1277 //
1278 // Called at MethodInfos cache creation
1279 // the method is either a normal method or a generic method definition
1280 // Also called at MethodBase.GetMethodBaseFromHandle
1281 // the method is either a normal method, a generic method definition, or an instantiated generic method
1282 // Needs an instantiating stub if
1283 // - non generic static method on a generic class
1284 // - non generic instance method on a struct
1285 // - non generic method on a generic interface
1286 //
1287
1288 // we base the creation of an unboxing stub on whether the original method was one already
1289 // that keeps the reflection logic the same for value types
1290
1291 // we need unboxing stubs for virtual methods on value types unless the method is generic
1292 BOOL fNeedUnboxingStub = pMethod->IsUnboxingStub() ||
1293 ( instType.IsValueType() && pMethod->IsVirtual() );
1294
1295 pInstMD = MethodDesc::FindOrCreateAssociatedMethodDesc(
1296 pMethod, /* the original MD */
1297 pMT, /* the method table */
1298 fNeedUnboxingStub, /* create boxing stub */
1299 Instantiation(), /* no generic instantiation */
1300 FALSE, /* no allowInstParam */
1301 TRUE /* force remotable method (i.e. inst wrappers for non-generic methods on generic interfaces) */);
1302 }
1303
1304 return pInstMD;
1305}
1306
1307// Given a typical method desc (i.e. instantiated at formal type
1308// parameters if it is a generic method or lives in a generic class),
1309// instantiate any type parameters at <__Canon>
1310//
1311// NOTE: If allowCreate is FALSE, typically you must also set ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE()
1312// allowCreate may be set to FALSE to enforce that the method searched
1313// should already be in existence - thus preventing creation and GCs during
1314// inappropriate times.
1315//
1316MethodDesc * MethodDesc::FindOrCreateTypicalSharedInstantiation(BOOL allowCreate /* = TRUE */)
1317{
1318 CONTRACT(MethodDesc*)
1319 {
1320 THROWS;
1321 GC_TRIGGERS;
1322 PRECONDITION(IsTypicalMethodDefinition());
1323 POSTCONDITION(CheckPointer(RETVAL));
1324 POSTCONDITION(RETVAL->IsTypicalSharedInstantiation());
1325 }
1326 CONTRACT_END
1327
1328 MethodDesc *pMD = this;
1329 MethodTable *pMT = pMD->GetMethodTable();
1330
1331 // First instantiate the declaring type at <__Canon,...,__Canon>
1332 DWORD nGenericClassArgs = pMT->GetNumGenericArgs();
1333 DWORD dwAllocSize = 0;
1334 if (!ClrSafeInt<DWORD>::multiply(sizeof(TypeHandle), nGenericClassArgs, dwAllocSize))
1335 ThrowHR(COR_E_OVERFLOW);
1336
1337 CQuickBytes qbGenericClassArgs;
1338 TypeHandle* pGenericClassArgs = reinterpret_cast<TypeHandle*>(qbGenericClassArgs.AllocThrows(dwAllocSize));
1339
1340 for (DWORD i = 0; i < nGenericClassArgs; i++)
1341 {
1342 pGenericClassArgs[i] = TypeHandle(g_pCanonMethodTableClass);
1343 }
1344
1345 pMT = ClassLoader::LoadGenericInstantiationThrowing(pMT->GetModule(),
1346 pMT->GetCl(),
1347 Instantiation(pGenericClassArgs, nGenericClassArgs),
1348 allowCreate ? ClassLoader::LoadTypes : ClassLoader::DontLoadTypes
1349 ).GetMethodTable();
1350
1351 if (pMT == NULL)
1352 {
1353 _ASSERTE(!allowCreate);
1354 return NULL;
1355 }
1356
1357 // Now instantiate the method at <__Canon,...,__Canon>, creating the shared code.
1358 // This will not create an instantiating stub just yet.
1359 DWORD nGenericMethodArgs = pMD->GetNumGenericMethodArgs();
1360 CQuickBytes qbGenericMethodArgs;
1361 TypeHandle *genericMethodArgs = NULL;
1362
1363 // The rest of this method instantiates a generic method
1364 // Instantiate at "__Canon" if a NULL "genericMethodArgs" is given
1365 if (nGenericMethodArgs)
1366 {
1367 dwAllocSize = 0;
1368 if (!ClrSafeInt<DWORD>::multiply(sizeof(TypeHandle), nGenericMethodArgs, dwAllocSize))
1369 ThrowHR(COR_E_OVERFLOW);
1370
1371 genericMethodArgs = reinterpret_cast<TypeHandle*>(qbGenericMethodArgs.AllocThrows(dwAllocSize));
1372
1373 for (DWORD i =0; i < nGenericMethodArgs; i++)
1374 genericMethodArgs[i] = TypeHandle(g_pCanonMethodTableClass);
1375 }
1376
1377 RETURN(MethodDesc::FindOrCreateAssociatedMethodDesc(pMD,
1378 pMT,
1379 FALSE, /* don't get unboxing entry point */
1380 Instantiation(genericMethodArgs, nGenericMethodArgs),
1381 TRUE,
1382 FALSE,
1383 allowCreate));
1384}
1385
1386//@GENERICSVER: Set the typical (ie. formal) instantiation
1387void InstantiatedMethodDesc::SetupGenericMethodDefinition(IMDInternalImport *pIMDII,
1388 LoaderAllocator* pAllocator,
1389 AllocMemTracker *pamTracker,
1390 Module *pModule,
1391 mdMethodDef tok)
1392{
1393 CONTRACTL
1394 {
1395 THROWS;
1396 GC_TRIGGERS;
1397 INJECT_FAULT(COMPlusThrowOM(););
1398 PRECONDITION(CheckPointer(pModule));
1399 PRECONDITION(CheckPointer(pIMDII));
1400 }
1401 CONTRACTL_END;
1402
1403 // The first field is never used
1404 m_wFlags2 = GenericMethodDefinition | (m_wFlags2 & ~KindMask);
1405
1406 //@GENERICSVER: allocate space for and initialize the typical instantiation
1407 //we share the typical instantiation among all instantiations by placing it in the generic method desc
1408 LOG((LF_JIT, LL_INFO10000, "GENERICSVER: Initializing typical method instantiation with type handles\n"));
1409 mdGenericParam tkTyPar;
1410 HENUMInternalHolder hEnumTyPars(pIMDII);
1411 hEnumTyPars.EnumInit(mdtGenericParam, tok);
1412
1413 // Initialize the typical instantiation
1414 DWORD numTyPars = hEnumTyPars.EnumGetCount();
1415 if (!FitsIn<WORD>(numTyPars))
1416 {
1417 LPCSTR szMethodName;
1418 if (FAILED(pIMDII->GetNameOfMethodDef(tok, &szMethodName)))
1419 {
1420 szMethodName = "Invalid MethodDef record";
1421 }
1422 pModule->GetAssembly()->ThrowTypeLoadException(szMethodName, IDS_CLASSLOAD_TOOMANYGENERICARGS);
1423 }
1424 m_wNumGenericArgs = static_cast<WORD>(numTyPars);
1425 _ASSERTE(m_wNumGenericArgs > 0);
1426
1427 S_SIZE_T dwAllocSize = S_SIZE_T(numTyPars) * S_SIZE_T(sizeof(TypeHandle));
1428
1429 // the memory allocated for m_pMethInst will be freed if the declaring type fails to load
1430 m_pPerInstInfo.SetValue((Dictionary *) pamTracker->Track(pAllocator->GetLowFrequencyHeap()->AllocMem(dwAllocSize)));
1431
1432 TypeHandle * pInstDest = (TypeHandle *) IMD_GetMethodDictionaryNonNull();
1433 for(unsigned int i = 0; i < numTyPars; i++)
1434 {
1435 hEnumTyPars.EnumNext(&tkTyPar);
1436
1437 // code:Module.m_GenericParamToDescMap maps generic parameter RIDs to TypeVarTypeDesc
1438 // instances so that we do not leak by allocating them all over again, if the declaring
1439 // type repeatedly fails to load.
1440 TypeVarTypeDesc *pTypeVarTypeDesc = pModule->LookupGenericParam(tkTyPar);
1441 if (pTypeVarTypeDesc == NULL)
1442 {
1443 // Do NOT use pamTracker for this memory as we need it stay allocated even if the load fails.
1444 void *mem = (void *)pAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(TypeVarTypeDesc)));
1445 pTypeVarTypeDesc = new (mem) TypeVarTypeDesc(pModule, tok, i, tkTyPar);
1446
1447 // No race here - the row in GenericParam table is owned exclusively by this method and we
1448 // are holding a lock preventing other threads from loading the declaring type and setting
1449 // up this method desc.
1450 pModule->StoreGenericParamThrowing(tkTyPar, pTypeVarTypeDesc);
1451 }
1452 pInstDest[i] = TypeHandle(pTypeVarTypeDesc);
1453 }
1454 LOG((LF_JIT, LL_INFO10000, "GENERICSVER: Initialized typical method instantiation with %d type handles\n",numTyPars));
1455}
1456
1457void InstantiatedMethodDesc::SetupWrapperStubWithInstantiations(MethodDesc* wrappedMD,DWORD numGenericArgs, TypeHandle *pInst)
1458{
1459 WRAPPER_NO_CONTRACT;
1460
1461 //_ASSERTE(sharedMD->IMD_IsSharedByGenericMethodInstantiations());
1462
1463 m_pWrappedMethodDesc.SetValue(wrappedMD);
1464 m_wFlags2 = WrapperStubWithInstantiations | (m_wFlags2 & ~KindMask);
1465 m_pPerInstInfo.SetValueMaybeNull((Dictionary*)pInst);
1466
1467 _ASSERTE(FitsIn<WORD>(numGenericArgs));
1468 m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
1469
1470 _ASSERTE(IMD_IsWrapperStubWithInstantiations());
1471 _ASSERTE(((MethodDesc *) this)->IsInstantiatingStub() || ((MethodDesc *) this)->IsUnboxingStub());
1472}
1473
1474
1475// Set the instantiation in the per-inst section (this is actually a dictionary)
1476void InstantiatedMethodDesc::SetupSharedMethodInstantiation(DWORD numGenericArgs, TypeHandle *pPerInstInfo, DictionaryLayout *pDL)
1477{
1478 WRAPPER_NO_CONTRACT;
1479
1480 _ASSERTE(numGenericArgs != 0);
1481 // Initially the dictionary layout is empty
1482 m_wFlags2 = SharedMethodInstantiation | (m_wFlags2 & ~KindMask);
1483 m_pPerInstInfo.SetValueMaybeNull((Dictionary *)pPerInstInfo);
1484
1485 _ASSERTE(FitsIn<WORD>(numGenericArgs));
1486 m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
1487
1488 m_pDictLayout.SetValueMaybeNull(pDL);
1489
1490
1491 _ASSERTE(IMD_IsSharedByGenericMethodInstantiations());
1492}
1493
1494// Set the instantiation in the per-inst section (this is actually a dictionary)
1495void InstantiatedMethodDesc::SetupUnsharedMethodInstantiation(DWORD numGenericArgs, TypeHandle *pInst)
1496{
1497 LIMITED_METHOD_CONTRACT;
1498
1499 // The first field is never used
1500 m_wFlags2 = UnsharedMethodInstantiation | (m_wFlags2 & ~KindMask);
1501 m_pPerInstInfo.SetValueMaybeNull((Dictionary *)pInst);
1502
1503 _ASSERTE(FitsIn<WORD>(numGenericArgs));
1504 m_wNumGenericArgs = static_cast<WORD>(numGenericArgs);
1505
1506 _ASSERTE(!IsUnboxingStub());
1507 _ASSERTE(!IsInstantiatingStub());
1508 _ASSERTE(!IMD_IsWrapperStubWithInstantiations());
1509 _ASSERTE(!IMD_IsSharedByGenericMethodInstantiations());
1510 _ASSERTE(!IMD_IsGenericMethodDefinition());
1511}
1512
1513
1514// A type variable is bounded to some depth iff it
1515// has no chain of type variable bounds of that depth.
1516// We use this is a simple test for circularity among class and method type parameter constraints:
1517// the constraints on a set of n variables are well-founded iff every variable is bounded by n.
1518// The test is cheap for the common case that few, if any, constraints are variables.
1519BOOL Bounded(TypeVarTypeDesc *tyvar, DWORD depth) {
1520 CONTRACTL {
1521 THROWS;
1522 GC_TRIGGERS;
1523 MODE_ANY;
1524 PRECONDITION(CheckPointer(tyvar));
1525 } CONTRACTL_END;
1526
1527 if (depth == 0)
1528 {
1529 return FALSE;
1530 }
1531
1532 DWORD numConstraints;
1533 TypeHandle *constraints = tyvar->GetConstraints(&numConstraints, CLASS_DEPENDENCIES_LOADED);
1534 for (unsigned i = 0; i < numConstraints; i++)
1535 {
1536 TypeHandle constraint = constraints[i];
1537 if (constraint.IsGenericVariable())
1538 {
1539 TypeVarTypeDesc* constraintVar = (TypeVarTypeDesc*) constraint.AsTypeDesc();
1540 //only consider bounds between same sort of variables (VAR or MVAR)
1541 if (tyvar->GetInternalCorElementType() == constraintVar->GetInternalCorElementType())
1542 {
1543 if (!Bounded(constraintVar, depth - 1))
1544 return FALSE;
1545 }
1546 }
1547 }
1548 return TRUE;
1549}
1550
1551void MethodDesc::LoadConstraintsForTypicalMethodDefinition(BOOL *pfHasCircularClassConstraints, BOOL *pfHasCircularMethodConstraints, ClassLoadLevel level/* = CLASS_LOADED*/)
1552{
1553 CONTRACTL {
1554 THROWS;
1555 GC_TRIGGERS;
1556 MODE_ANY;
1557 PRECONDITION(IsTypicalMethodDefinition());
1558 PRECONDITION(CheckPointer(pfHasCircularClassConstraints));
1559 PRECONDITION(CheckPointer(pfHasCircularMethodConstraints));
1560 } CONTRACTL_END;
1561
1562 *pfHasCircularClassConstraints = FALSE;
1563 *pfHasCircularMethodConstraints = FALSE;
1564
1565 // Force a load of the constraints on the type parameters
1566 Instantiation classInst = GetClassInstantiation();
1567 for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
1568 {
1569 TypeVarTypeDesc* tyvar = classInst[i].AsGenericVariable();
1570 _ASSERTE(tyvar != NULL);
1571 tyvar->LoadConstraints(level);
1572 }
1573
1574 Instantiation methodInst = GetMethodInstantiation();
1575 for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
1576 {
1577 TypeVarTypeDesc* tyvar = methodInst[i].AsGenericVariable();
1578 _ASSERTE(tyvar != NULL);
1579 tyvar->LoadConstraints(level);
1580
1581 VOID DoAccessibilityCheckForConstraints(MethodTable *pAskingMT, TypeVarTypeDesc *pTyVar, UINT resIDWhy);
1582 DoAccessibilityCheckForConstraints(GetMethodTable(), tyvar, E_ACCESSDENIED);
1583 }
1584
1585 // reject circular class constraints
1586 for (DWORD i = 0; i < classInst.GetNumArgs(); i++)
1587 {
1588 TypeVarTypeDesc* tyvar = classInst[i].AsGenericVariable();
1589 _ASSERTE(tyvar != NULL);
1590 if(!Bounded(tyvar, classInst.GetNumArgs()))
1591 {
1592 *pfHasCircularClassConstraints = TRUE;
1593 }
1594 }
1595
1596 // reject circular method constraints
1597 for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
1598 {
1599 TypeVarTypeDesc* tyvar = methodInst[i].AsGenericVariable();
1600 _ASSERTE(tyvar != NULL);
1601 if(!Bounded(tyvar, methodInst.GetNumArgs()))
1602 {
1603 *pfHasCircularMethodConstraints = TRUE;
1604 }
1605 }
1606
1607 return;
1608}
1609
1610
1611#ifdef FEATURE_PREJIT
1612
1613void MethodDesc::PrepopulateDictionary(DataImage * image, BOOL nonExpansive)
1614{
1615 STANDARD_VM_CONTRACT;
1616
1617 // Note the strong similarity to MethodTable::PrepopulateDictionary
1618 if (GetMethodDictionary())
1619 {
1620 LOG((LF_JIT, LL_INFO10000, "GENERICS: Prepopulating dictionary for MD %s\n", this));
1621 GetMethodDictionary()->PrepopulateDictionary(this, NULL, nonExpansive);
1622 }
1623}
1624
1625#endif // FEATURE_PREJIT
1626
1627#ifndef DACCESS_COMPILE
1628
1629BOOL MethodDesc::SatisfiesMethodConstraints(TypeHandle thParent, BOOL fThrowIfNotSatisfied/* = FALSE*/)
1630{
1631 CONTRACTL
1632 {
1633 THROWS;
1634 GC_TRIGGERS;
1635 MODE_ANY;
1636
1637 INJECT_FAULT(COMPlusThrowOM());
1638 }
1639 CONTRACTL_END;
1640
1641 // nice: cache (positive?) result in (instantiated) methoddesc
1642 // caveat: this would be unsafe for instantiated method desc living in generic,
1643 // hence possibly shared classes (with varying class instantiations).
1644
1645 if (!HasMethodInstantiation())
1646 return TRUE;
1647
1648 Instantiation methodInst = LoadMethodInstantiation();
1649 Instantiation typicalInst = LoadTypicalMethodDefinition()->GetMethodInstantiation();
1650
1651 //NB: according to the constructor's signature, thParent should be the declaring type,
1652 // but the code appears to admit derived types too.
1653 SigTypeContext typeContext(this,thParent);
1654
1655 for (DWORD i = 0; i < methodInst.GetNumArgs(); i++)
1656 {
1657 TypeHandle thArg = methodInst[i];
1658 _ASSERTE(!thArg.IsNull());
1659
1660 TypeVarTypeDesc* tyvar = (TypeVarTypeDesc*) (typicalInst[i].AsTypeDesc());
1661 _ASSERTE(tyvar != NULL);
1662 _ASSERTE(TypeFromToken(tyvar->GetTypeOrMethodDef()) == mdtMethodDef);
1663
1664 tyvar->LoadConstraints(); //TODO: is this necessary for anything but the typical method?
1665
1666 if (!tyvar->SatisfiesConstraints(&typeContext,thArg))
1667 {
1668 if (fThrowIfNotSatisfied)
1669 {
1670 SString sParentName;
1671 TypeString::AppendType(sParentName, thParent);
1672
1673 SString sMethodName(SString::Utf8, GetName());
1674
1675 SString sActualParamName;
1676 TypeString::AppendType(sActualParamName, methodInst[i]);
1677
1678 SString sFormalParamName;
1679 TypeString::AppendType(sFormalParamName, typicalInst[i]);
1680
1681 COMPlusThrow(kVerificationException,
1682 IDS_EE_METHOD_CONSTRAINTS_VIOLATION,
1683 sParentName.GetUnicode(),
1684 sMethodName.GetUnicode(),
1685 sActualParamName.GetUnicode(),
1686 sFormalParamName.GetUnicode()
1687 );
1688
1689
1690 }
1691 return FALSE;
1692 }
1693
1694 }
1695 return TRUE;
1696}
1697
1698#endif // !DACCESS_COMPILE
1699