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 |
69 | static 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 | // |
153 | static 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 | // |
182 | static 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 | // |
227 | static 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 | // |
265 | static 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 */ |
296 | InstantiatedMethodDesc * |
297 | InstantiatedMethodDesc::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. |
529 | InstantiatedMethodDesc* |
530 | InstantiatedMethodDesc::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 */ |
564 | InstantiatedMethodDesc* |
565 | InstantiatedMethodDesc::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 */ |
737 | MethodDesc* |
738 | MethodDesc::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 | // |
1316 | MethodDesc * 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 |
1387 | void 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 | |
1457 | void 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) |
1476 | void 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) |
1495 | void 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. |
1519 | BOOL 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 | |
1551 | void 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 | |
1613 | void 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 | |
1629 | BOOL 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 | |