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 | // =========================================================================== |
5 | // File: Method.CPP |
6 | // |
7 | |
8 | // |
9 | // See the book of the runtime entry for overall design: |
10 | // file:../../doc/BookOfTheRuntime/ClassLoader/MethodDescDesign.doc |
11 | // |
12 | |
13 | |
14 | #include "common.h" |
15 | #include "excep.h" |
16 | #include "dbginterface.h" |
17 | #include "ecall.h" |
18 | #include "eeconfig.h" |
19 | #include "mlinfo.h" |
20 | #include "dllimport.h" |
21 | #include "generics.h" |
22 | #include "genericdict.h" |
23 | #include "typedesc.h" |
24 | #include "typestring.h" |
25 | #include "virtualcallstub.h" |
26 | #include "jitinterface.h" |
27 | #include "runtimehandles.h" |
28 | #include "eventtrace.h" |
29 | #include "interoputil.h" |
30 | #include "prettyprintsig.h" |
31 | #include "formattype.h" |
32 | |
33 | #ifdef FEATURE_PREJIT |
34 | #include "compile.h" |
35 | #endif |
36 | |
37 | #ifdef FEATURE_COMINTEROP |
38 | #include "comcallablewrapper.h" |
39 | #include "clrtocomcall.h" |
40 | #endif |
41 | |
42 | #ifdef _MSC_VER |
43 | #pragma warning(push) |
44 | #pragma warning(disable:4244) |
45 | #endif // _MSC_VER |
46 | |
47 | #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
48 | GVAL_IMPL(DWORD, g_MiniMetaDataBuffMaxSize); |
49 | GVAL_IMPL(TADDR, g_MiniMetaDataBuffAddress); |
50 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
51 | |
52 | // forward decl |
53 | bool FixupSignatureContainingInternalTypes( |
54 | DataImage * image, |
55 | PCCOR_SIGNATURE pSig, |
56 | DWORD cSig, |
57 | bool checkOnly = false); |
58 | |
59 | // Alias ComPlusCallMethodDesc to regular MethodDesc to simplify definition of the size table |
60 | #ifndef FEATURE_COMINTEROP |
61 | #define ComPlusCallMethodDesc MethodDesc |
62 | #endif |
63 | |
64 | // Verify that the structure sizes of our MethodDescs support proper |
65 | // aligning for atomic stub replacement. |
66 | // |
67 | static_assert_no_msg((sizeof(MethodDescChunk) & MethodDesc::ALIGNMENT_MASK) == 0); |
68 | static_assert_no_msg((sizeof(MethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0); |
69 | static_assert_no_msg((sizeof(FCallMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0); |
70 | static_assert_no_msg((sizeof(NDirectMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0); |
71 | static_assert_no_msg((sizeof(EEImplMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0); |
72 | static_assert_no_msg((sizeof(ArrayMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0); |
73 | static_assert_no_msg((sizeof(ComPlusCallMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0); |
74 | static_assert_no_msg((sizeof(DynamicMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0); |
75 | |
76 | #define METHOD_DESC_SIZES(adjustment) \ |
77 | adjustment + sizeof(MethodDesc), /* mcIL */ \ |
78 | adjustment + sizeof(FCallMethodDesc), /* mcFCall */ \ |
79 | adjustment + sizeof(NDirectMethodDesc), /* mcNDirect */ \ |
80 | adjustment + sizeof(EEImplMethodDesc), /* mcEEImpl */ \ |
81 | adjustment + sizeof(ArrayMethodDesc), /* mcArray */ \ |
82 | adjustment + sizeof(InstantiatedMethodDesc), /* mcInstantiated */ \ |
83 | adjustment + sizeof(ComPlusCallMethodDesc), /* mcComInterOp */ \ |
84 | adjustment + sizeof(DynamicMethodDesc) /* mcDynamic */ |
85 | |
86 | const SIZE_T MethodDesc::s_ClassificationSizeTable[] = { |
87 | // This is the raw |
88 | METHOD_DESC_SIZES(0), |
89 | |
90 | // This extended part of the table is used for faster MethodDesc size lookup. |
91 | // We index using optional slot flags into it |
92 | METHOD_DESC_SIZES(sizeof(NonVtableSlot)), |
93 | METHOD_DESC_SIZES(sizeof(MethodImpl)), |
94 | METHOD_DESC_SIZES(sizeof(NonVtableSlot) + sizeof(MethodImpl)) |
95 | }; |
96 | |
97 | #ifndef FEATURE_COMINTEROP |
98 | #undef ComPlusCallMethodDesc |
99 | #endif |
100 | |
101 | class ArgIteratorBaseForPInvoke : public ArgIteratorBase |
102 | { |
103 | protected: |
104 | FORCEINLINE BOOL IsRegPassedStruct(MethodTable* pMT) |
105 | { |
106 | return pMT->GetLayoutInfo()->IsNativeStructPassedInRegisters(); |
107 | } |
108 | }; |
109 | |
110 | class PInvokeArgIterator : public ArgIteratorTemplate<ArgIteratorBaseForPInvoke> |
111 | { |
112 | public: |
113 | PInvokeArgIterator(MetaSig* pSig) |
114 | { |
115 | m_pSig = pSig; |
116 | } |
117 | }; |
118 | |
119 | |
120 | //******************************************************************************* |
121 | SIZE_T MethodDesc::SizeOf() |
122 | { |
123 | LIMITED_METHOD_DAC_CONTRACT; |
124 | |
125 | SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot | mdcMethodImpl)]; |
126 | |
127 | if (HasNativeCodeSlot()) |
128 | { |
129 | size += (*dac_cast<PTR_TADDR>(dac_cast<TADDR>(this) + size) & FIXUP_LIST_MASK) ? |
130 | (sizeof(NativeCodeSlot) + sizeof(FixupListSlot)) : sizeof(NativeCodeSlot); |
131 | } |
132 | |
133 | #ifdef FEATURE_COMINTEROP |
134 | if (IsGenericComPlusCall()) |
135 | size += sizeof(ComPlusCallInfo); |
136 | #endif // FEATURE_COMINTEROP |
137 | |
138 | return size; |
139 | } |
140 | |
141 | /*********************************************************************/ |
142 | #ifndef DACCESS_COMPILE |
143 | BOOL NDirectMethodDesc::HasDefaultDllImportSearchPathsAttribute() |
144 | { |
145 | CONTRACTL |
146 | { |
147 | THROWS; |
148 | GC_NOTRIGGER; |
149 | MODE_ANY; |
150 | } |
151 | CONTRACTL_END; |
152 | |
153 | if(IsDefaultDllImportSearchPathsAttributeCached()) |
154 | { |
155 | return (ndirect.m_wFlags & kDefaultDllImportSearchPathsStatus) != 0; |
156 | } |
157 | |
158 | _ASSERTE(!IsZapped()); |
159 | |
160 | BOOL attributeIsFound = GetDefaultDllImportSearchPathsAttributeValue(GetMDImport(),GetMemberDef(),&ndirect.m_DefaultDllImportSearchPathsAttributeValue); |
161 | |
162 | if(attributeIsFound ) |
163 | { |
164 | InterlockedSetNDirectFlags(kDefaultDllImportSearchPathsIsCached | kDefaultDllImportSearchPathsStatus); |
165 | } |
166 | else |
167 | { |
168 | InterlockedSetNDirectFlags(kDefaultDllImportSearchPathsIsCached); |
169 | } |
170 | |
171 | return (ndirect.m_wFlags & kDefaultDllImportSearchPathsStatus) != 0; |
172 | } |
173 | #endif //!DACCESS_COMPILE |
174 | |
175 | //******************************************************************************* |
176 | #ifndef DACCESS_COMPILE |
177 | VOID MethodDesc::EnsureActive() |
178 | { |
179 | CONTRACTL |
180 | { |
181 | THROWS; |
182 | GC_TRIGGERS; |
183 | MODE_ANY; |
184 | } |
185 | CONTRACTL_END; |
186 | GetMethodTable()->EnsureInstanceActive(); |
187 | if (HasMethodInstantiation() && !IsGenericMethodDefinition()) |
188 | { |
189 | Instantiation methodInst = GetMethodInstantiation(); |
190 | for (DWORD i = 0; i < methodInst.GetNumArgs(); ++i) |
191 | { |
192 | MethodTable * pMT = methodInst[i].GetMethodTable(); |
193 | if (pMT) |
194 | pMT->EnsureInstanceActive(); |
195 | } |
196 | } |
197 | } |
198 | #endif //!DACCESS_COMPILE |
199 | |
200 | //******************************************************************************* |
201 | CHECK MethodDesc::CheckActivated() |
202 | { |
203 | WRAPPER_NO_CONTRACT; |
204 | CHECK(GetModule()->CheckActivated()); |
205 | CHECK_OK; |
206 | } |
207 | |
208 | //******************************************************************************* |
209 | BaseDomain *MethodDesc::GetDomain() |
210 | { |
211 | CONTRACTL |
212 | { |
213 | NOTHROW; |
214 | GC_NOTRIGGER; |
215 | FORBID_FAULT; |
216 | SUPPORTS_DAC; |
217 | SO_TOLERANT; |
218 | } |
219 | CONTRACTL_END |
220 | |
221 | return AppDomain::GetCurrentDomain(); |
222 | } |
223 | |
224 | #ifndef DACCESS_COMPILE |
225 | |
226 | //******************************************************************************* |
227 | LoaderAllocator * MethodDesc::GetLoaderAllocatorForCode() |
228 | { |
229 | if (IsLCGMethod()) |
230 | { |
231 | return ::GetAppDomain()->GetLoaderAllocator(); |
232 | } |
233 | else |
234 | { |
235 | return GetLoaderAllocator(); |
236 | } |
237 | } |
238 | |
239 | |
240 | //******************************************************************************* |
241 | LoaderAllocator * MethodDesc::GetDomainSpecificLoaderAllocator() |
242 | { |
243 | if (GetLoaderModule()->IsCollectible()) |
244 | { |
245 | return GetLoaderAllocator(); |
246 | } |
247 | else |
248 | { |
249 | return ::GetAppDomain()->GetLoaderAllocator(); |
250 | } |
251 | |
252 | } |
253 | |
254 | #endif //!DACCESS_COMPILE |
255 | |
256 | //******************************************************************************* |
257 | LPCUTF8 MethodDesc::GetName(USHORT slot) |
258 | { |
259 | // MethodDesc::GetDeclMethodDesc can throw. |
260 | WRAPPER_NO_CONTRACT; |
261 | MethodDesc *pDeclMD = GetDeclMethodDesc((UINT32)slot); |
262 | CONSISTENCY_CHECK(IsInterface() || !pDeclMD->IsInterface()); |
263 | return pDeclMD->GetName(); |
264 | } |
265 | |
266 | //******************************************************************************* |
267 | LPCUTF8 MethodDesc::GetName() |
268 | { |
269 | CONTRACTL |
270 | { |
271 | if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; // MethodImpl::FindMethodDesc can throw. |
272 | GC_NOTRIGGER; |
273 | FORBID_FAULT; |
274 | SO_TOLERANT; |
275 | SUPPORTS_DAC; |
276 | }CONTRACTL_END; |
277 | |
278 | g_IBCLogger.LogMethodDescAccess(this); |
279 | |
280 | if (IsArray()) |
281 | { |
282 | // Array classes don't have metadata tokens |
283 | return dac_cast<PTR_ArrayMethodDesc>(this)->GetMethodName(); |
284 | } |
285 | else if (IsNoMetadata()) |
286 | { |
287 | // LCG methods don't have metadata tokens |
288 | return dac_cast<PTR_DynamicMethodDesc>(this)->GetMethodName(); |
289 | } |
290 | else |
291 | { |
292 | // Get the metadata string name for this method |
293 | LPCUTF8 result = NULL; |
294 | |
295 | // This probes only if we have a thread, in which case it is OK to throw the SO. |
296 | BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(COMPlusThrowSO()); |
297 | |
298 | if (FAILED(GetMDImport()->GetNameOfMethodDef(GetMemberDef(), &result))) |
299 | { |
300 | result = NULL; |
301 | } |
302 | |
303 | END_SO_INTOLERANT_CODE; |
304 | |
305 | return(result); |
306 | } |
307 | } |
308 | |
309 | #ifndef DACCESS_COMPILE |
310 | /* |
311 | * Function to get a method's name, its namespace |
312 | */ |
313 | VOID MethodDesc::GetMethodInfoNoSig(SString &namespaceOrClassName, SString &methodName) |
314 | { |
315 | static LPCWSTR pDynamicClassName = W("dynamicClass" ); |
316 | |
317 | // namespace |
318 | if(IsDynamicMethod()) |
319 | namespaceOrClassName.Append(pDynamicClassName); |
320 | else |
321 | TypeString::AppendType(namespaceOrClassName, TypeHandle(GetMethodTable())); |
322 | |
323 | // name |
324 | methodName.AppendUTF8(GetName()); |
325 | } |
326 | |
327 | /* |
328 | * Function to get a method's name, its namespace and signature (legacy format) |
329 | */ |
330 | VOID MethodDesc::GetMethodInfo(SString &namespaceOrClassName, SString &methodName, SString &methodSignature) |
331 | { |
332 | GetMethodInfoNoSig(namespaceOrClassName, methodName); |
333 | |
334 | // signature |
335 | CQuickBytes qbOut; |
336 | ULONG cSig = 0; |
337 | PCCOR_SIGNATURE pSig; |
338 | |
339 | GetSig(&pSig, &cSig); |
340 | PrettyPrintSigInternalLegacy(pSig, cSig, " " , &qbOut, GetMDImport()); |
341 | methodSignature.AppendUTF8((char *)qbOut.Ptr()); |
342 | } |
343 | |
344 | /* |
345 | * Function to get a method's name, its namespace and signature (new format) |
346 | */ |
347 | VOID MethodDesc::GetMethodInfoWithNewSig(SString &namespaceOrClassName, SString &methodName, SString &methodSignature) |
348 | { |
349 | GetMethodInfoNoSig(namespaceOrClassName, methodName); |
350 | |
351 | // signature |
352 | CQuickBytes qbOut; |
353 | ULONG cSig = 0; |
354 | PCCOR_SIGNATURE pSig; |
355 | |
356 | GetSig(&pSig, &cSig); |
357 | PrettyPrintSig(pSig, (DWORD)cSig, "" , &qbOut, GetMDImport(), NULL); |
358 | methodSignature.AppendUTF8((char *)qbOut.Ptr()); |
359 | } |
360 | |
361 | /* |
362 | * Function to get a method's full name, something like |
363 | * void [mscorlib]System.StubHelpers.BSTRMarshaler::ClearNative(native int) |
364 | */ |
365 | VOID MethodDesc::GetFullMethodInfo(SString& fullMethodSigName) |
366 | { |
367 | SString namespaceOrClassName, methodName; |
368 | GetMethodInfoNoSig(namespaceOrClassName, methodName); |
369 | |
370 | // signature |
371 | CQuickBytes qbOut; |
372 | ULONG cSig = 0; |
373 | PCCOR_SIGNATURE pSig; |
374 | |
375 | SString methodFullName; |
376 | StackScratchBuffer namespaceNameBuffer, methodNameBuffer; |
377 | methodFullName.AppendPrintf( |
378 | (LPCUTF8)"[%s] %s::%s" , |
379 | GetModule()->GetAssembly()->GetSimpleName(), |
380 | namespaceOrClassName.GetUTF8(namespaceNameBuffer), |
381 | methodName.GetUTF8(methodNameBuffer)); |
382 | |
383 | GetSig(&pSig, &cSig); |
384 | |
385 | StackScratchBuffer buffer; |
386 | PrettyPrintSig(pSig, (DWORD)cSig, methodFullName.GetUTF8(buffer), &qbOut, GetMDImport(), NULL); |
387 | fullMethodSigName.AppendUTF8((char *)qbOut.Ptr()); |
388 | } |
389 | |
390 | //******************************************************************************* |
391 | void MethodDesc::PrecomputeNameHash() |
392 | { |
393 | CONTRACTL |
394 | { |
395 | STANDARD_VM_CHECK; |
396 | PRECONDITION(IsCompilationProcess()); |
397 | } |
398 | CONTRACTL_END; |
399 | |
400 | |
401 | // We only have space for a name hash when we can use the packed slot layout |
402 | if (RequiresFullSlotNumber()) |
403 | { |
404 | return; |
405 | } |
406 | |
407 | // Store a case-insensitive hash so that we can use this value for |
408 | // both case-sensitive and case-insensitive name lookups |
409 | SString name(SString::Utf8Literal, GetName()); |
410 | ULONG nameHashValue = (WORD) name.HashCaseInsensitive() & enum_packedSlotLayout_NameHashMask; |
411 | |
412 | // We expect to set the hash once during NGen and not overwrite any existing bits |
413 | _ASSERTE((m_wSlotNumber & enum_packedSlotLayout_NameHashMask) == 0); |
414 | |
415 | m_wSlotNumber |= nameHashValue; |
416 | } |
417 | #endif |
418 | |
419 | //******************************************************************************* |
420 | BOOL MethodDesc::MightHaveName(ULONG nameHashValue) |
421 | { |
422 | LIMITED_METHOD_CONTRACT; |
423 | |
424 | // We only have space for a name hash when we are using the packed slot layout |
425 | if (RequiresFullSlotNumber()) |
426 | { |
427 | return TRUE; |
428 | } |
429 | |
430 | WORD thisHashValue = m_wSlotNumber & enum_packedSlotLayout_NameHashMask; |
431 | |
432 | // A zero value might mean no hash has ever been set |
433 | // (checking this way is better than dedicating a bit to tell us) |
434 | if (thisHashValue == 0) |
435 | { |
436 | return TRUE; |
437 | } |
438 | |
439 | WORD testHashValue = (WORD) nameHashValue & enum_packedSlotLayout_NameHashMask; |
440 | |
441 | return (thisHashValue == testHashValue); |
442 | } |
443 | |
444 | //******************************************************************************* |
445 | void MethodDesc::GetSig(PCCOR_SIGNATURE *ppSig, DWORD *pcSig) |
446 | { |
447 | CONTRACTL |
448 | { |
449 | NOTHROW; |
450 | GC_NOTRIGGER; |
451 | FORBID_FAULT; |
452 | SO_TOLERANT; |
453 | SUPPORTS_DAC; |
454 | } |
455 | CONTRACTL_END |
456 | |
457 | if (HasStoredSig()) |
458 | { |
459 | PTR_StoredSigMethodDesc pSMD = dac_cast<PTR_StoredSigMethodDesc>(this); |
460 | if (pSMD->HasStoredMethodSig() || GetClassification()==mcDynamic) |
461 | { |
462 | *ppSig = pSMD->GetStoredMethodSig(pcSig); |
463 | PREFIX_ASSUME(*ppSig != NULL); |
464 | |
465 | #if defined(FEATURE_PREJIT) && !defined(DACCESS_COMPILE) |
466 | _ASSERTE_MSG((**ppSig & IMAGE_CEE_CS_CALLCONV_NEEDSRESTORE) == 0 || !IsILStub() || (strncmp(m_pszDebugMethodName,"IL_STUB_Array" , 13)==0) , |
467 | "CheckRestore must be called on IL stub MethodDesc" ); |
468 | #endif // FEATURE_PREJIT && !DACCESS_COMPILE |
469 | return; |
470 | } |
471 | } |
472 | |
473 | GetSigFromMetadata(GetMDImport(), ppSig, pcSig); |
474 | PREFIX_ASSUME(*ppSig != NULL); |
475 | } |
476 | |
477 | //******************************************************************************* |
478 | // get a function signature from its metadata |
479 | // Arguments: |
480 | // input: |
481 | // importer the metatdata importer to be used |
482 | // output: |
483 | // ppSig the function signature |
484 | // pcSig number of elements in the signature |
485 | |
486 | |
487 | void MethodDesc::GetSigFromMetadata(IMDInternalImport * importer, |
488 | PCCOR_SIGNATURE * ppSig, |
489 | DWORD * pcSig) |
490 | { |
491 | CONTRACTL |
492 | { |
493 | NOTHROW; |
494 | GC_NOTRIGGER; |
495 | FORBID_FAULT; |
496 | SO_TOLERANT; |
497 | SUPPORTS_DAC; |
498 | } |
499 | CONTRACTL_END |
500 | |
501 | if (FAILED(importer->GetSigOfMethodDef(GetMemberDef(), pcSig, ppSig))) |
502 | { // Class loader already asked for signature, so this should always succeed (unless there's a |
503 | // bug or a new code path) |
504 | _ASSERTE(!"If this ever fires, then this method should return HRESULT" ); |
505 | *ppSig = NULL; |
506 | *pcSig = 0; |
507 | } |
508 | } |
509 | |
510 | //******************************************************************************* |
511 | PCCOR_SIGNATURE MethodDesc::GetSig() |
512 | { |
513 | WRAPPER_NO_CONTRACT; |
514 | |
515 | PCCOR_SIGNATURE pSig; |
516 | DWORD cSig; |
517 | |
518 | GetSig(&pSig, &cSig); |
519 | |
520 | PREFIX_ASSUME(pSig != NULL); |
521 | |
522 | return pSig; |
523 | } |
524 | |
525 | Signature MethodDesc::GetSignature() |
526 | { |
527 | WRAPPER_NO_CONTRACT; |
528 | SUPPORTS_DAC; |
529 | |
530 | PCCOR_SIGNATURE pSig; |
531 | DWORD cSig; |
532 | |
533 | GetSig(&pSig, &cSig); |
534 | |
535 | PREFIX_ASSUME(pSig != NULL); |
536 | |
537 | return Signature(pSig, cSig); |
538 | } |
539 | |
540 | PCODE MethodDesc::GetMethodEntryPoint() |
541 | { |
542 | CONTRACTL |
543 | { |
544 | NOTHROW; |
545 | GC_NOTRIGGER; |
546 | MODE_ANY; |
547 | SO_TOLERANT; |
548 | SUPPORTS_DAC; |
549 | } |
550 | CONTRACTL_END; |
551 | |
552 | // Keep implementations of MethodDesc::GetMethodEntryPoint and MethodDesc::GetAddrOfSlot in sync! |
553 | |
554 | g_IBCLogger.LogMethodDescAccess(this); |
555 | |
556 | if (HasNonVtableSlot()) |
557 | { |
558 | SIZE_T size = GetBaseSize(); |
559 | |
560 | TADDR pSlot = dac_cast<TADDR>(this) + size; |
561 | |
562 | return IsZapped() ? NonVtableSlot::GetValueAtPtr(pSlot) : *PTR_PCODE(pSlot); |
563 | } |
564 | |
565 | _ASSERTE(GetMethodTable()->IsCanonicalMethodTable()); |
566 | return GetMethodTable_NoLogging()->GetSlot(GetSlot()); |
567 | } |
568 | |
569 | TADDR MethodDesc::GetAddrOfSlot() |
570 | { |
571 | CONTRACTL |
572 | { |
573 | NOTHROW; |
574 | GC_NOTRIGGER; |
575 | MODE_ANY; |
576 | SO_TOLERANT; |
577 | SUPPORTS_DAC; |
578 | } |
579 | CONTRACTL_END; |
580 | |
581 | // Keep implementations of MethodDesc::GetMethodEntryPoint and MethodDesc::GetAddrOfSlot in sync! |
582 | |
583 | if (HasNonVtableSlot()) |
584 | { |
585 | // Slots in NGened images are relative pointers |
586 | _ASSERTE(!IsZapped()); |
587 | |
588 | SIZE_T size = GetBaseSize(); |
589 | |
590 | return dac_cast<TADDR>(this) + size; |
591 | } |
592 | |
593 | _ASSERTE(GetMethodTable()->IsCanonicalMethodTable()); |
594 | return GetMethodTable()->GetSlotPtr(GetSlot()); |
595 | } |
596 | |
597 | //******************************************************************************* |
598 | PTR_MethodDesc MethodDesc::GetDeclMethodDesc(UINT32 slotNumber) |
599 | { |
600 | CONTRACTL { |
601 | WRAPPER(THROWS); |
602 | WRAPPER(GC_TRIGGERS); |
603 | INSTANCE_CHECK; |
604 | } CONTRACTL_END; |
605 | |
606 | MethodDesc *pMDResult = this; |
607 | |
608 | // If the MethodDesc is not itself a methodImpl, but it is not in its native |
609 | // slot, then someone (perhaps itself) must have overridden a methodImpl |
610 | // in a parent, which causes the method to get put into all of the methodImpl |
611 | // slots. So, the MethodDesc is implicitly a methodImpl without containing |
612 | // the data. To find the real methodImpl MethodDesc, climb the inheritance |
613 | // hierarchy checking the native slot on the way. |
614 | if ((UINT32)pMDResult->GetSlot() != slotNumber) |
615 | { |
616 | while (!pMDResult->IsMethodImpl()) |
617 | { |
618 | CONSISTENCY_CHECK(CheckPointer(pMDResult->GetMethodTable()->GetParentMethodTable())); |
619 | CONSISTENCY_CHECK(slotNumber < pMDResult->GetMethodTable()->GetParentMethodTable()->GetNumVirtuals()); |
620 | pMDResult = pMDResult->GetMethodTable()->GetParentMethodTable()->GetMethodDescForSlot(slotNumber); |
621 | } |
622 | |
623 | { |
624 | CONSISTENCY_CHECK(pMDResult->IsMethodImpl()); |
625 | MethodImpl *pImpl = pMDResult->GetMethodImpl(); |
626 | pMDResult = pImpl->FindMethodDesc(slotNumber, PTR_MethodDesc(pMDResult)); |
627 | } |
628 | |
629 | // It is possible that a methodImpl'd slot got copied into another slot because |
630 | // of slot unification, for example: |
631 | // C1::A is methodImpled with C2::B |
632 | // C1::B is methodImpled with C2::C |
633 | // this means that through slot unification that A is tied to B and B is tied to C, |
634 | // so A is tied to C even though C does not have a methodImpl entry specifically |
635 | // relating to that slot. In this case, we recurse to the parent type and ask the |
636 | // same question again. |
637 | if (pMDResult->GetSlot() != slotNumber) |
638 | { |
639 | MethodTable * pMTOfMD = pMDResult->GetMethodTable(); |
640 | CONSISTENCY_CHECK(slotNumber < pMTOfMD->GetParentMethodTable()->GetNumVirtuals()); |
641 | pMDResult = pMTOfMD->GetParentMethodTable()->GetMethodDescForSlot(slotNumber); |
642 | pMDResult = pMDResult->GetDeclMethodDesc(slotNumber); |
643 | } |
644 | } |
645 | |
646 | CONSISTENCY_CHECK(CheckPointer(pMDResult)); |
647 | CONSISTENCY_CHECK((UINT32)pMDResult->GetSlot() == slotNumber); |
648 | return PTR_MethodDesc(pMDResult); |
649 | } |
650 | |
651 | //******************************************************************************* |
652 | // Returns a hash for the method. |
653 | // The hash will be the same for the method across multiple process runs. |
654 | COUNT_T MethodDesc::GetStableHash() |
655 | { |
656 | WRAPPER_NO_CONTRACT; |
657 | _ASSERTE(IsRestored_NoLogging()); |
658 | DefineFullyQualifiedNameForClass(); |
659 | |
660 | const char * moduleName = GetModule()->GetSimpleName(); |
661 | const char * className; |
662 | const char * methodName = GetName(); |
663 | |
664 | if (IsLCGMethod()) |
665 | { |
666 | className = "DynamicClass" ; |
667 | } |
668 | else if (IsILStub()) |
669 | { |
670 | className = ILStubResolver::GetStubClassName(this); |
671 | } |
672 | else |
673 | { |
674 | #if defined(_DEBUG) |
675 | // Calling _GetFullyQualifiedNameForClass in chk build is very expensive |
676 | // since it construct the class name everytime we call this method. In chk |
677 | // builds we already have a cheaper way to get the class name - |
678 | // GetDebugClassName - which doesn't calculate the class name everytime. |
679 | // This results in huge saving in Ngen time for checked builds. |
680 | className = m_pszDebugClassName; |
681 | #else // !_DEBUG |
682 | // since this is for diagnostic purposes only, |
683 | // give up on the namespace, as we don't have a buffer to concat it |
684 | // also note this won't show array class names. |
685 | LPCUTF8 nameSpace; |
686 | MethodTable * pMT = GetMethodTable(); |
687 | |
688 | className = pMT->GetFullyQualifiedNameInfo(&nameSpace); |
689 | #endif // !_DEBUG |
690 | } |
691 | |
692 | COUNT_T hash = HashStringA(moduleName); // Start the hash with the Module name |
693 | hash = HashCOUNT_T(hash, HashStringA(className)); // Hash in the name of the Class name |
694 | hash = HashCOUNT_T(hash, HashStringA(methodName)); // Hash in the name of the Method name |
695 | |
696 | // Handle Generic Types and Generic Methods |
697 | // |
698 | if (HasClassInstantiation() && !GetMethodTable()->IsGenericTypeDefinition()) |
699 | { |
700 | Instantiation classInst = GetClassInstantiation(); |
701 | for (DWORD i = 0; i < classInst.GetNumArgs(); i++) |
702 | { |
703 | MethodTable * pMT = classInst[i].GetMethodTable(); |
704 | // pMT can be NULL for TypeVarTypeDesc |
705 | // @TODO: Implement TypeHandle::GetStableHash instead of |
706 | // checking pMT==NULL |
707 | if (pMT) |
708 | hash = HashCOUNT_T(hash, HashStringA(GetFullyQualifiedNameForClass(pMT))); |
709 | } |
710 | } |
711 | |
712 | if (HasMethodInstantiation() && !IsGenericMethodDefinition()) |
713 | { |
714 | Instantiation methodInst = GetMethodInstantiation(); |
715 | for (DWORD i = 0; i < methodInst.GetNumArgs(); i++) |
716 | { |
717 | MethodTable * pMT = methodInst[i].GetMethodTable(); |
718 | // pMT can be NULL for TypeVarTypeDesc |
719 | // @TODO: Implement TypeHandle::GetStableHash instead of |
720 | // checking pMT==NULL |
721 | if (pMT) |
722 | hash = HashCOUNT_T(hash, HashStringA(GetFullyQualifiedNameForClass(pMT))); |
723 | } |
724 | } |
725 | |
726 | return hash; |
727 | } |
728 | |
729 | //******************************************************************************* |
730 | // Get the number of type parameters to a generic method |
731 | DWORD MethodDesc::GetNumGenericMethodArgs() |
732 | { |
733 | CONTRACTL |
734 | { |
735 | NOTHROW; |
736 | GC_NOTRIGGER; |
737 | FORBID_FAULT; |
738 | SO_TOLERANT; |
739 | CANNOT_TAKE_LOCK; |
740 | SUPPORTS_DAC; |
741 | } |
742 | CONTRACTL_END |
743 | |
744 | g_IBCLogger.LogMethodDescAccess(this); |
745 | |
746 | if (GetClassification() == mcInstantiated) |
747 | { |
748 | InstantiatedMethodDesc *pIMD = AsInstantiatedMethodDesc(); |
749 | return pIMD->m_wNumGenericArgs; |
750 | } |
751 | else return 0; |
752 | } |
753 | |
754 | //******************************************************************************* |
755 | MethodTable * MethodDesc::GetExactDeclaringType(MethodTable * ownerOrSubType) |
756 | { |
757 | CONTRACTL |
758 | { |
759 | NOTHROW; |
760 | GC_NOTRIGGER; |
761 | SO_TOLERANT; |
762 | MODE_ANY; |
763 | } |
764 | CONTRACTL_END; |
765 | |
766 | MethodTable * pMT = GetMethodTable(); |
767 | |
768 | // Fast path for typical case. |
769 | if (ownerOrSubType == pMT) |
770 | return pMT; |
771 | |
772 | // If we come here for array method, the typedef tokens inside GetMethodTableMatchingParentClass |
773 | // will match, but the types are actually from unrelated arrays, so the result would be incorrect. |
774 | _ASSERTE(!IsArray()); |
775 | |
776 | return ownerOrSubType->GetMethodTableMatchingParentClass(pMT); |
777 | } |
778 | |
779 | //******************************************************************************* |
780 | Instantiation MethodDesc::GetExactClassInstantiation(TypeHandle possibleObjType) |
781 | { |
782 | CONTRACTL |
783 | { |
784 | NOTHROW; |
785 | GC_NOTRIGGER; |
786 | FORBID_FAULT; |
787 | SUPPORTS_DAC; |
788 | } |
789 | CONTRACTL_END |
790 | |
791 | |
792 | return (possibleObjType.IsNull() |
793 | ? GetClassInstantiation() |
794 | : possibleObjType.GetInstantiationOfParentClass(GetMethodTable())); |
795 | } |
796 | |
797 | //******************************************************************************* |
798 | BOOL MethodDesc::HasSameMethodDefAs(MethodDesc * pMD) |
799 | { |
800 | LIMITED_METHOD_CONTRACT; |
801 | |
802 | if (this == pMD) |
803 | return TRUE; |
804 | |
805 | return (GetMemberDef() == pMD->GetMemberDef()) && (GetModule() == pMD->GetModule()); |
806 | } |
807 | |
808 | //******************************************************************************* |
809 | BOOL MethodDesc::IsTypicalSharedInstantiation() |
810 | { |
811 | WRAPPER_NO_CONTRACT; |
812 | PRECONDITION(IsRestored_NoLogging()); |
813 | |
814 | Instantiation classInst = GetMethodTable()->GetInstantiation(); |
815 | if (!ClassLoader::IsTypicalSharedInstantiation(classInst)) |
816 | return FALSE; |
817 | |
818 | if (IsGenericMethodDefinition()) |
819 | return FALSE; |
820 | |
821 | Instantiation methodInst = GetMethodInstantiation(); |
822 | if (!ClassLoader::IsTypicalSharedInstantiation(methodInst)) |
823 | return FALSE; |
824 | |
825 | return TRUE; |
826 | } |
827 | |
828 | //******************************************************************************* |
829 | Instantiation MethodDesc::LoadMethodInstantiation() |
830 | { |
831 | CONTRACTL |
832 | { |
833 | THROWS; |
834 | GC_TRIGGERS; |
835 | INJECT_FAULT(COMPlusThrowOM();); |
836 | } |
837 | CONTRACTL_END |
838 | |
839 | if (IsGenericMethodDefinition() && !IsTypicalMethodDefinition()) |
840 | { |
841 | return LoadTypicalMethodDefinition()->GetMethodInstantiation(); |
842 | } |
843 | else |
844 | return GetMethodInstantiation(); |
845 | } |
846 | |
847 | //******************************************************************************* |
848 | Module *MethodDesc::GetDefiningModuleForOpenMethod() |
849 | { |
850 | CONTRACTL |
851 | { |
852 | NOTHROW; |
853 | GC_NOTRIGGER; |
854 | FORBID_FAULT; |
855 | } |
856 | CONTRACTL_END |
857 | |
858 | Module *pModule = GetMethodTable()->GetDefiningModuleForOpenType(); |
859 | if (pModule != NULL) |
860 | return pModule; |
861 | |
862 | if (IsGenericMethodDefinition()) |
863 | return GetModule(); |
864 | |
865 | Instantiation inst = GetMethodInstantiation(); |
866 | for (DWORD i = 0; i < inst.GetNumArgs(); i++) |
867 | { |
868 | // Encoded types are never open |
869 | if (!inst[i].IsEncodedFixup()) |
870 | { |
871 | pModule = inst[i].GetDefiningModuleForOpenType(); |
872 | if (pModule != NULL) |
873 | return pModule; |
874 | } |
875 | } |
876 | |
877 | return NULL; |
878 | } |
879 | |
880 | |
881 | //******************************************************************************* |
882 | BOOL MethodDesc::ContainsGenericVariables() |
883 | { |
884 | CONTRACTL |
885 | { |
886 | NOTHROW; |
887 | GC_NOTRIGGER; |
888 | FORBID_FAULT; |
889 | PRECONDITION(IsRestored_NoLogging()); |
890 | } |
891 | CONTRACTL_END |
892 | |
893 | // If this is a method of a generic type, does the type have |
894 | // non-instantiated type arguments |
895 | |
896 | if (TypeHandle(GetMethodTable()).ContainsGenericVariables()) |
897 | return TRUE; |
898 | |
899 | if (IsGenericMethodDefinition()) |
900 | return TRUE; |
901 | |
902 | // If this is an instantiated generic method, are there are any generic type variables |
903 | if (GetNumGenericMethodArgs() != 0) |
904 | { |
905 | Instantiation methodInst = GetMethodInstantiation(); |
906 | for (DWORD i = 0; i < methodInst.GetNumArgs(); i++) |
907 | { |
908 | if (methodInst[i].ContainsGenericVariables()) |
909 | return TRUE; |
910 | } |
911 | } |
912 | |
913 | return FALSE; |
914 | } |
915 | |
916 | //******************************************************************************* |
917 | BOOL MethodDesc::IsTightlyBoundToMethodTable() |
918 | { |
919 | WRAPPER_NO_CONTRACT; |
920 | SUPPORTS_DAC; |
921 | |
922 | // Anything with the real vtable slot is tightly bound |
923 | if (!HasNonVtableSlot()) |
924 | return TRUE; |
925 | |
926 | // All instantiations of generic methods are stored in the InstMethHashTable. |
927 | if (HasMethodInstantiation()) |
928 | { |
929 | if (IsGenericMethodDefinition()) |
930 | return TRUE; |
931 | else |
932 | return FALSE; |
933 | } |
934 | |
935 | // Wrapper stubs are stored in the InstMethHashTable, e.g. for static methods in generic classes |
936 | if (IsWrapperStub()) |
937 | return FALSE; |
938 | |
939 | return TRUE; |
940 | } |
941 | |
942 | #ifndef DACCESS_COMPILE |
943 | |
944 | //******************************************************************************* |
945 | // Update flags in a thread safe manner. |
946 | WORD MethodDesc::InterlockedUpdateFlags(WORD wMask, BOOL fSet) |
947 | { |
948 | CONTRACTL |
949 | { |
950 | THROWS; |
951 | GC_NOTRIGGER; |
952 | MODE_ANY; |
953 | } |
954 | CONTRACTL_END; |
955 | |
956 | WORD wOldState = m_wFlags; |
957 | DWORD dwMask = wMask; |
958 | |
959 | // We need to make this operation atomic (multiple threads can play with the flags field at the same time). But the flags field |
960 | // is a word and we only have interlock operations over dwords. So we round down the flags field address to the nearest aligned |
961 | // dword (along with the intended bitfield mask). Note that we make the assumption that the flags word is aligned itself, so we |
962 | // only have two possibilites: the field already lies on a dword boundary or it's precisely one word out. |
963 | DWORD* pdwFlags = (DWORD*)((ULONG_PTR)&m_wFlags - (offsetof(MethodDesc, m_wFlags) & 0x3)); |
964 | |
965 | #ifdef _PREFAST_ |
966 | #pragma warning(push) |
967 | #pragma warning(disable:6326) // "Suppress PREFast warning about comparing two constants" |
968 | #endif // _PREFAST_ |
969 | |
970 | #if BIGENDIAN |
971 | if ((offsetof(MethodDesc, m_wFlags) & 0x3) == 0) { |
972 | #else // !BIGENDIAN |
973 | if ((offsetof(MethodDesc, m_wFlags) & 0x3) != 0) { |
974 | #endif // !BIGENDIAN |
975 | static_assert_no_msg(sizeof(m_wFlags) == 2); |
976 | dwMask <<= 16; |
977 | } |
978 | #ifdef _PREFAST_ |
979 | #pragma warning(pop) |
980 | #endif |
981 | |
982 | g_IBCLogger.LogMethodDescWriteAccess(this); |
983 | EnsureWritablePages(pdwFlags); |
984 | |
985 | if (fSet) |
986 | FastInterlockOr(pdwFlags, dwMask); |
987 | else |
988 | FastInterlockAnd(pdwFlags, ~dwMask); |
989 | |
990 | return wOldState; |
991 | } |
992 | |
993 | WORD MethodDesc::InterlockedUpdateFlags3(WORD wMask, BOOL fSet) |
994 | { |
995 | LIMITED_METHOD_CONTRACT; |
996 | |
997 | WORD wOldState = m_wFlags3AndTokenRemainder; |
998 | DWORD dwMask = wMask; |
999 | |
1000 | // We need to make this operation atomic (multiple threads can play with the flags field at the same time). But the flags field |
1001 | // is a word and we only have interlock operations over dwords. So we round down the flags field address to the nearest aligned |
1002 | // dword (along with the intended bitfield mask). Note that we make the assumption that the flags word is aligned itself, so we |
1003 | // only have two possibilites: the field already lies on a dword boundary or it's precisely one word out. |
1004 | DWORD* pdwFlags = (DWORD*)((ULONG_PTR)&m_wFlags3AndTokenRemainder - (offsetof(MethodDesc, m_wFlags3AndTokenRemainder) & 0x3)); |
1005 | |
1006 | #ifdef _PREFAST_ |
1007 | #pragma warning(push) |
1008 | #pragma warning(disable:6326) // "Suppress PREFast warning about comparing two constants" |
1009 | #endif // _PREFAST_ |
1010 | |
1011 | #if BIGENDIAN |
1012 | if ((offsetof(MethodDesc, m_wFlags3AndTokenRemainder) & 0x3) == 0) { |
1013 | #else // !BIGENDIAN |
1014 | if ((offsetof(MethodDesc, m_wFlags3AndTokenRemainder) & 0x3) != 0) { |
1015 | #endif // !BIGENDIAN |
1016 | static_assert_no_msg(sizeof(m_wFlags3AndTokenRemainder) == 2); |
1017 | dwMask <<= 16; |
1018 | } |
1019 | #ifdef _PREFAST_ |
1020 | #pragma warning(pop) |
1021 | #endif |
1022 | |
1023 | g_IBCLogger.LogMethodDescWriteAccess(this); |
1024 | |
1025 | if (fSet) |
1026 | FastInterlockOr(pdwFlags, dwMask); |
1027 | else |
1028 | FastInterlockAnd(pdwFlags, ~dwMask); |
1029 | |
1030 | return wOldState; |
1031 | } |
1032 | |
1033 | #endif // !DACCESS_COMPILE |
1034 | |
1035 | //******************************************************************************* |
1036 | // Returns the address of the native code. The native code can be one of: |
1037 | // - jitted code if !IsPreImplemented() |
1038 | // - ngened code if IsPreImplemented() |
1039 | // |
1040 | // Methods which have no native code are either implemented by stubs or not jitted yet. |
1041 | // For example, NDirectMethodDesc's have no native code. They are treated as |
1042 | // implemented by stubs. On WIN64, these stubs are IL stubs, which DO have native code. |
1043 | // |
1044 | // This function returns null if the method has no native code. |
1045 | PCODE MethodDesc::GetNativeCode() |
1046 | { |
1047 | WRAPPER_NO_CONTRACT; |
1048 | SUPPORTS_DAC; |
1049 | |
1050 | g_IBCLogger.LogMethodDescAccess(this); |
1051 | |
1052 | if (HasNativeCodeSlot()) |
1053 | { |
1054 | // When profiler is enabled, profiler may ask to rejit a code even though we |
1055 | // we have ngen code for this MethodDesc. (See MethodDesc::DoPrestub). |
1056 | // This means that NativeCodeSlot::GetValueMaybeNullAtPtr(GetAddrOfNativeCodeSlot()) |
1057 | // is not stable. It can turn from non-zero to zero. |
1058 | PCODE pCode = PCODE(NativeCodeSlot::GetValueMaybeNullAtPtr(GetAddrOfNativeCodeSlot()) & ~FIXUP_LIST_MASK); |
1059 | #ifdef _TARGET_ARM_ |
1060 | if (pCode != NULL) |
1061 | pCode |= THUMB_CODE; |
1062 | #endif |
1063 | return pCode; |
1064 | } |
1065 | |
1066 | if (!HasStableEntryPoint() || HasPrecode()) |
1067 | return NULL; |
1068 | |
1069 | return GetStableEntryPoint(); |
1070 | } |
1071 | |
1072 | //******************************************************************************* |
1073 | TADDR MethodDesc::GetAddrOfNativeCodeSlot() |
1074 | { |
1075 | WRAPPER_NO_CONTRACT; |
1076 | |
1077 | _ASSERTE(HasNativeCodeSlot()); |
1078 | |
1079 | SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot | mdcMethodImpl)]; |
1080 | |
1081 | return dac_cast<TADDR>(this) + size; |
1082 | } |
1083 | |
1084 | //******************************************************************************* |
1085 | PCODE MethodDesc::GetPreImplementedCode() |
1086 | { |
1087 | CONTRACTL |
1088 | { |
1089 | NOTHROW; |
1090 | GC_NOTRIGGER; |
1091 | MODE_ANY; |
1092 | SUPPORTS_DAC; |
1093 | } |
1094 | CONTRACTL_END; |
1095 | |
1096 | #ifdef FEATURE_PREJIT |
1097 | PCODE pNativeCode = GetNativeCode(); |
1098 | if (pNativeCode == NULL) |
1099 | return NULL; |
1100 | |
1101 | Module* pZapModule = GetZapModule(); |
1102 | if (pZapModule == NULL) |
1103 | return NULL; |
1104 | |
1105 | if (!pZapModule->IsZappedCode(pNativeCode)) |
1106 | return NULL; |
1107 | |
1108 | return pNativeCode; |
1109 | #else // !FEATURE_PREJIT |
1110 | return NULL; |
1111 | #endif // !FEATURE_PREJIT |
1112 | } |
1113 | |
1114 | //******************************************************************************* |
1115 | BOOL MethodDesc::IsVoid() |
1116 | { |
1117 | WRAPPER_NO_CONTRACT; |
1118 | |
1119 | MetaSig sig(this); |
1120 | return sig.IsReturnTypeVoid(); |
1121 | } |
1122 | |
1123 | //******************************************************************************* |
1124 | BOOL MethodDesc::HasRetBuffArg() |
1125 | { |
1126 | WRAPPER_NO_CONTRACT; |
1127 | |
1128 | MetaSig sig(this); |
1129 | ArgIterator argit(&sig); |
1130 | return argit.HasRetBuffArg(); |
1131 | } |
1132 | |
1133 | //******************************************************************************* |
1134 | // This returns the offset of the IL. |
1135 | // The offset is relative to the base of the IL image. |
1136 | ULONG MethodDesc::GetRVA() |
1137 | { |
1138 | CONTRACTL |
1139 | { |
1140 | NOTHROW; |
1141 | GC_NOTRIGGER; |
1142 | FORBID_FAULT; |
1143 | SO_TOLERANT; |
1144 | SUPPORTS_DAC; |
1145 | } |
1146 | CONTRACTL_END |
1147 | |
1148 | if (IsRuntimeSupplied()) |
1149 | { |
1150 | return 0; |
1151 | } |
1152 | |
1153 | // Methods without metadata don't have an RVA. Examples are IL stubs and LCG methods. |
1154 | if (IsNoMetadata()) |
1155 | { |
1156 | return 0; |
1157 | } |
1158 | |
1159 | if (GetMemberDef() & 0x00FFFFFF) |
1160 | { |
1161 | Module *pModule = GetModule(); |
1162 | PREFIX_ASSUME(pModule != NULL); |
1163 | |
1164 | DWORD dwDescrOffset; |
1165 | DWORD dwImplFlags; |
1166 | if (FAILED(pModule->GetMDImport()->GetMethodImplProps(GetMemberDef(), &dwDescrOffset, &dwImplFlags))) |
1167 | { // Class loader already asked for MethodImpls, so this should always succeed (unless there's a |
1168 | // bug or a new code path) |
1169 | _ASSERTE(!"If this ever fires, then this method should return HRESULT" ); |
1170 | return 0; |
1171 | } |
1172 | BAD_FORMAT_NOTHROW_ASSERT(IsNDirect() || IsMiIL(dwImplFlags) || IsMiOPTIL(dwImplFlags) || dwDescrOffset == 0); |
1173 | return dwDescrOffset; |
1174 | } |
1175 | |
1176 | return 0; |
1177 | } |
1178 | |
1179 | //******************************************************************************* |
1180 | BOOL MethodDesc::IsVarArg() |
1181 | { |
1182 | CONTRACTL |
1183 | { |
1184 | NOTHROW; |
1185 | GC_NOTRIGGER; |
1186 | MODE_ANY; |
1187 | } |
1188 | CONTRACTL_END; |
1189 | SUPPORTS_DAC; |
1190 | |
1191 | Signature signature = GetSignature(); |
1192 | _ASSERTE(!signature.IsEmpty()); |
1193 | return MetaSig::IsVarArg(GetModule(), signature); |
1194 | } |
1195 | |
1196 | //******************************************************************************* |
1197 | COR_ILMETHOD* MethodDesc::(BOOL fAllowOverrides /*=FALSE*/) |
1198 | { |
1199 | CONTRACTL |
1200 | { |
1201 | THROWS; |
1202 | GC_NOTRIGGER; |
1203 | PRECONDITION(IsIL()); |
1204 | PRECONDITION(!IsUnboxingStub()); |
1205 | } |
1206 | CONTRACTL_END |
1207 | |
1208 | Module *pModule = GetModule(); |
1209 | |
1210 | // Always pickup 'permanent' overrides like reflection emit, EnC, etc. |
1211 | // but only grab temporary overrides (like profiler rewrites) if asked to |
1212 | TADDR pIL = pModule->GetDynamicIL(GetMemberDef(), fAllowOverrides); |
1213 | |
1214 | if (pIL == NULL) |
1215 | { |
1216 | pIL = pModule->GetIL(GetRVA()); |
1217 | } |
1218 | |
1219 | #ifdef _DEBUG_IMPL |
1220 | if (pIL != NULL) |
1221 | { |
1222 | // |
1223 | // This is convenient place to verify that COR_ILMETHOD_DECODER::GetOnDiskSize is in sync |
1224 | // with our private DACized copy in PEDecoder::ComputeILMethodSize |
1225 | // |
1226 | COR_ILMETHOD_DECODER header((COR_ILMETHOD *)pIL); |
1227 | SIZE_T size1 = header.GetOnDiskSize((COR_ILMETHOD *)pIL); |
1228 | SIZE_T size2 = PEDecoder::ComputeILMethodSize(pIL); |
1229 | _ASSERTE(size1 == size2); |
1230 | } |
1231 | #endif |
1232 | |
1233 | #ifdef DACCESS_COMPILE |
1234 | return (pIL != NULL) ? DacGetIlMethod(pIL) : NULL; |
1235 | #else // !DACCESS_COMPILE |
1236 | return PTR_COR_ILMETHOD(pIL); |
1237 | #endif // !DACCESS_COMPILE |
1238 | } |
1239 | |
1240 | //******************************************************************************* |
1241 | MetaSig::RETURNTYPE MethodDesc::ReturnsObject( |
1242 | #ifdef _DEBUG |
1243 | bool supportStringConstructors, |
1244 | #endif |
1245 | MethodTable** pMT |
1246 | ) |
1247 | { |
1248 | CONTRACTL |
1249 | { |
1250 | if (FORBIDGC_LOADER_USE_ENABLED()) NOTHROW; else THROWS; |
1251 | GC_NOTRIGGER; |
1252 | FORBID_FAULT; |
1253 | SO_TOLERANT; |
1254 | } |
1255 | CONTRACTL_END |
1256 | |
1257 | ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE(); |
1258 | |
1259 | TypeHandle thValueType; |
1260 | |
1261 | MetaSig sig(this); |
1262 | CorElementType et = sig.GetReturnTypeNormalized(&thValueType); |
1263 | |
1264 | switch (et) |
1265 | { |
1266 | case ELEMENT_TYPE_STRING: |
1267 | case ELEMENT_TYPE_CLASS: |
1268 | case ELEMENT_TYPE_SZARRAY: |
1269 | case ELEMENT_TYPE_ARRAY: |
1270 | case ELEMENT_TYPE_OBJECT: |
1271 | case ELEMENT_TYPE_VAR: |
1272 | return(MetaSig::RETOBJ); |
1273 | |
1274 | #ifdef ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE |
1275 | case ELEMENT_TYPE_VALUETYPE: |
1276 | // We return value types in registers if they fit in ENREGISTERED_RETURNTYPE_MAXSIZE |
1277 | // These valuetypes could contain gc refs. |
1278 | { |
1279 | ArgIterator argit(&sig); |
1280 | if (!argit.HasRetBuffArg()) |
1281 | { |
1282 | // the type must already be loaded |
1283 | _ASSERTE(!thValueType.IsNull()); |
1284 | if (!thValueType.IsTypeDesc()) |
1285 | { |
1286 | MethodTable * pReturnTypeMT = thValueType.AsMethodTable(); |
1287 | if (pMT != NULL) |
1288 | { |
1289 | *pMT = pReturnTypeMT; |
1290 | } |
1291 | |
1292 | #ifdef UNIX_AMD64_ABI |
1293 | if (pReturnTypeMT->IsRegPassedStruct()) |
1294 | { |
1295 | return MetaSig::RETVALUETYPE; |
1296 | } |
1297 | #endif // !UNIX_AMD64_ABI |
1298 | |
1299 | if (pReturnTypeMT->ContainsPointers()) |
1300 | { |
1301 | _ASSERTE(pReturnTypeMT->GetNumInstanceFieldBytes() == sizeof(void*)); |
1302 | return MetaSig::RETOBJ; |
1303 | } |
1304 | } |
1305 | } |
1306 | } |
1307 | break; |
1308 | #endif // ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE |
1309 | |
1310 | #ifdef _DEBUG |
1311 | case ELEMENT_TYPE_VOID: |
1312 | // String constructors return objects. We should not have any ecall string |
1313 | // constructors, except when called from gc coverage codes (which is only |
1314 | // done under debug). We will therefore optimize the retail version of this |
1315 | // method to not support string constructors. |
1316 | if (IsCtor() && GetMethodTable()->HasComponentSize()) |
1317 | { |
1318 | _ASSERTE(supportStringConstructors); |
1319 | return MetaSig::RETOBJ; |
1320 | } |
1321 | break; |
1322 | #endif // _DEBUG |
1323 | |
1324 | case ELEMENT_TYPE_BYREF: |
1325 | return(MetaSig::RETBYREF); |
1326 | |
1327 | default: |
1328 | break; |
1329 | } |
1330 | |
1331 | return(MetaSig::RETNONOBJ); |
1332 | } |
1333 | |
1334 | #ifdef FEATURE_COMINTEROP |
1335 | |
1336 | #ifndef DACCESS_COMPILE |
1337 | |
1338 | //******************************************************************************* |
1339 | LONG MethodDesc::GetComDispid() |
1340 | { |
1341 | CONTRACTL |
1342 | { |
1343 | NOTHROW; |
1344 | GC_NOTRIGGER; |
1345 | MODE_ANY; |
1346 | } |
1347 | CONTRACTL_END |
1348 | |
1349 | ULONG dispid = -1; |
1350 | HRESULT hr = GetMDImport()->GetDispIdOfMemberDef( |
1351 | GetMemberDef(), // The member for which to get props. |
1352 | &dispid // return dispid. |
1353 | ); |
1354 | if (FAILED(hr)) |
1355 | return -1; |
1356 | |
1357 | return (LONG)dispid; |
1358 | } |
1359 | |
1360 | //******************************************************************************* |
1361 | WORD MethodDesc::GetComSlot() |
1362 | { |
1363 | CONTRACTL |
1364 | { |
1365 | THROWS; |
1366 | GC_NOTRIGGER; |
1367 | FORBID_FAULT; |
1368 | PRECONDITION(IsRestored_NoLogging()); |
1369 | } |
1370 | CONTRACTL_END |
1371 | |
1372 | MethodTable * pMT = GetMethodTable(); |
1373 | |
1374 | _ASSERTE(pMT->IsInterface()); |
1375 | |
1376 | // COM slots are biased from MethodTable slots depending on interface type |
1377 | WORD numExtraSlots = ComMethodTable::GetNumExtraSlots(pMT->GetComInterfaceType()); |
1378 | |
1379 | // Normal interfaces are layed out the same way as in the MethodTable, while |
1380 | // sparse interfaces need to go through an extra layer of mapping. |
1381 | WORD slot; |
1382 | |
1383 | if (pMT->IsSparseForCOMInterop()) |
1384 | slot = numExtraSlots + pMT->GetClass()->GetSparseCOMInteropVTableMap()->LookupVTSlot(GetSlot()); |
1385 | else |
1386 | slot = numExtraSlots + GetSlot(); |
1387 | |
1388 | return slot; |
1389 | } |
1390 | |
1391 | #endif // !DACCESS_COMPILE |
1392 | |
1393 | #endif // FEATURE_COMINTEROP |
1394 | |
1395 | //******************************************************************************* |
1396 | DWORD MethodDesc::GetAttrs() const |
1397 | { |
1398 | CONTRACTL |
1399 | { |
1400 | NOTHROW; |
1401 | GC_NOTRIGGER; |
1402 | MODE_ANY; |
1403 | SO_TOLERANT; |
1404 | } |
1405 | CONTRACTL_END |
1406 | |
1407 | if (IsArray()) |
1408 | return dac_cast<PTR_ArrayMethodDesc>(this)->GetAttrs(); |
1409 | else if (IsNoMetadata()) |
1410 | return dac_cast<PTR_DynamicMethodDesc>(this)->GetAttrs();; |
1411 | |
1412 | DWORD dwAttributes; |
1413 | if (FAILED(GetMDImport()->GetMethodDefProps(GetMemberDef(), &dwAttributes))) |
1414 | { // Class loader already asked for attributes, so this should always succeed (unless there's a |
1415 | // bug or a new code path) |
1416 | _ASSERTE(!"If this ever fires, then this method should return HRESULT" ); |
1417 | return 0; |
1418 | } |
1419 | return dwAttributes; |
1420 | } |
1421 | |
1422 | //******************************************************************************* |
1423 | DWORD MethodDesc::GetImplAttrs() |
1424 | { |
1425 | CONTRACTL |
1426 | { |
1427 | NOTHROW; |
1428 | GC_NOTRIGGER; |
1429 | MODE_ANY; |
1430 | } |
1431 | CONTRACTL_END |
1432 | |
1433 | DWORD props; |
1434 | if (FAILED(GetMDImport()->GetMethodImplProps(GetMemberDef(), NULL, &props))) |
1435 | { // Class loader already asked for MethodImpls, so this should always succeed (unless there's a |
1436 | // bug or a new code path) |
1437 | _ASSERTE(!"If this ever fires, then this method should return HRESULT" ); |
1438 | return 0; |
1439 | } |
1440 | return props; |
1441 | } |
1442 | |
1443 | //******************************************************************************* |
1444 | Module* MethodDesc::GetZapModule() |
1445 | { |
1446 | CONTRACTL |
1447 | { |
1448 | NOTHROW; |
1449 | GC_NOTRIGGER; |
1450 | FORBID_FAULT; |
1451 | SO_TOLERANT; |
1452 | SUPPORTS_DAC; |
1453 | } |
1454 | CONTRACTL_END |
1455 | |
1456 | #ifdef FEATURE_PREJIT |
1457 | if (!IsZapped()) |
1458 | { |
1459 | return NULL; |
1460 | } |
1461 | else |
1462 | if (!IsTightlyBoundToMethodTable()) |
1463 | { |
1464 | return ExecutionManager::FindZapModule(dac_cast<TADDR>(this)); |
1465 | } |
1466 | else |
1467 | { |
1468 | return GetMethodTable()->GetLoaderModule(); |
1469 | } |
1470 | #else |
1471 | return NULL; |
1472 | #endif |
1473 | } |
1474 | |
1475 | //******************************************************************************* |
1476 | Module* MethodDesc::GetLoaderModule() |
1477 | { |
1478 | CONTRACTL |
1479 | { |
1480 | NOTHROW; |
1481 | GC_NOTRIGGER; |
1482 | MODE_ANY; |
1483 | } |
1484 | CONTRACTL_END; |
1485 | |
1486 | if (IsZapped()) |
1487 | { |
1488 | return GetZapModule(); |
1489 | } |
1490 | else |
1491 | if (HasMethodInstantiation() && !IsGenericMethodDefinition()) |
1492 | { |
1493 | Module *retVal = ClassLoader::ComputeLoaderModule(GetMethodTable(), |
1494 | GetMemberDef(), |
1495 | GetMethodInstantiation()); |
1496 | return retVal; |
1497 | } |
1498 | else |
1499 | { |
1500 | return GetMethodTable()->GetLoaderModule(); |
1501 | } |
1502 | } |
1503 | |
1504 | //******************************************************************************* |
1505 | Module *MethodDesc::GetModule() const |
1506 | { |
1507 | STATIC_CONTRACT_NOTHROW; |
1508 | STATIC_CONTRACT_GC_NOTRIGGER; |
1509 | STATIC_CONTRACT_FORBID_FAULT; |
1510 | STATIC_CONTRACT_SO_TOLERANT; |
1511 | SUPPORTS_DAC; |
1512 | |
1513 | g_IBCLogger.LogMethodDescAccess(this); |
1514 | Module *pModule = GetModule_NoLogging(); |
1515 | |
1516 | return pModule; |
1517 | } |
1518 | |
1519 | //******************************************************************************* |
1520 | Module *MethodDesc::GetModule_NoLogging() const |
1521 | { |
1522 | STATIC_CONTRACT_NOTHROW; |
1523 | STATIC_CONTRACT_GC_NOTRIGGER; |
1524 | STATIC_CONTRACT_FORBID_FAULT; |
1525 | STATIC_CONTRACT_SO_TOLERANT; |
1526 | SUPPORTS_DAC; |
1527 | |
1528 | MethodTable* pMT = GetMethodDescChunk()->GetMethodTable(); |
1529 | return pMT->GetModule(); |
1530 | } |
1531 | |
1532 | //******************************************************************************* |
1533 | // Is this an instantiating stub for generics? This does not include those |
1534 | // BoxedEntryPointStubs which call an instantiating stub. |
1535 | BOOL MethodDesc::IsInstantiatingStub() |
1536 | { |
1537 | WRAPPER_NO_CONTRACT; |
1538 | SUPPORTS_DAC; |
1539 | |
1540 | return |
1541 | (GetClassification() == mcInstantiated) |
1542 | && !IsUnboxingStub() |
1543 | && AsInstantiatedMethodDesc()->IMD_IsWrapperStubWithInstantiations(); |
1544 | } |
1545 | |
1546 | //******************************************************************************* |
1547 | BOOL MethodDesc::IsWrapperStub() |
1548 | { |
1549 | WRAPPER_NO_CONTRACT; |
1550 | SUPPORTS_DAC; |
1551 | |
1552 | return (IsUnboxingStub() || IsInstantiatingStub()); |
1553 | } |
1554 | |
1555 | #ifndef DACCESS_COMPILE |
1556 | //******************************************************************************* |
1557 | |
1558 | MethodDesc *MethodDesc::GetWrappedMethodDesc() |
1559 | { |
1560 | WRAPPER_NO_CONTRACT; |
1561 | |
1562 | _ASSERTE(IsWrapperStub()); |
1563 | |
1564 | if (IsUnboxingStub()) |
1565 | { |
1566 | return this->GetMethodTable()->GetUnboxedEntryPointMD(this); |
1567 | } |
1568 | |
1569 | if (IsInstantiatingStub()) |
1570 | { |
1571 | MethodDesc *pRet = AsInstantiatedMethodDesc()->IMD_GetWrappedMethodDesc(); |
1572 | #ifdef _DEBUG |
1573 | MethodDesc *pAltMD = |
1574 | MethodDesc::FindOrCreateAssociatedMethodDesc(this, |
1575 | this->GetMethodTable(), |
1576 | FALSE, /* no unboxing entrypoint */ |
1577 | this->GetMethodInstantiation(), |
1578 | TRUE /* get shared code */ ); |
1579 | _ASSERTE(pAltMD == pRet); |
1580 | #endif // _DEBUG |
1581 | return pRet; |
1582 | } |
1583 | return NULL; |
1584 | } |
1585 | |
1586 | |
1587 | MethodDesc *MethodDesc::GetExistingWrappedMethodDesc() |
1588 | { |
1589 | CONTRACTL |
1590 | { |
1591 | THROWS; |
1592 | GC_NOTRIGGER; |
1593 | MODE_ANY; |
1594 | } |
1595 | CONTRACTL_END; |
1596 | |
1597 | _ASSERTE(IsWrapperStub()); |
1598 | |
1599 | if (IsUnboxingStub()) |
1600 | { |
1601 | return this->GetMethodTable()->GetExistingUnboxedEntryPointMD(this); |
1602 | } |
1603 | |
1604 | if (IsInstantiatingStub()) |
1605 | { |
1606 | MethodDesc *pRet = AsInstantiatedMethodDesc()->IMD_GetWrappedMethodDesc(); |
1607 | return pRet; |
1608 | } |
1609 | return NULL; |
1610 | } |
1611 | |
1612 | |
1613 | |
1614 | #endif // !DACCESS_COMPILE |
1615 | |
1616 | //******************************************************************************* |
1617 | BOOL MethodDesc::IsSharedByGenericInstantiations() |
1618 | { |
1619 | LIMITED_METHOD_DAC_CONTRACT; |
1620 | |
1621 | if (IsWrapperStub()) |
1622 | return FALSE; |
1623 | else if (GetMethodTable()->IsSharedByGenericInstantiations()) |
1624 | return TRUE; |
1625 | else return IsSharedByGenericMethodInstantiations(); |
1626 | } |
1627 | |
1628 | //******************************************************************************* |
1629 | BOOL MethodDesc::IsSharedByGenericMethodInstantiations() |
1630 | { |
1631 | LIMITED_METHOD_DAC_CONTRACT; |
1632 | |
1633 | if (GetClassification() == mcInstantiated) |
1634 | return AsInstantiatedMethodDesc()->IMD_IsSharedByGenericMethodInstantiations(); |
1635 | else return FALSE; |
1636 | } |
1637 | |
1638 | //******************************************************************************* |
1639 | // Does this method require an extra MethodTable argument for instantiation information? |
1640 | // This is the case for |
1641 | // * per-inst static methods in shared-code instantiated generic classes (e.g. static void MyClass<string>::m()) |
1642 | // - there is no this pointer providing generic dictionary info |
1643 | // * shared-code instance methods in instantiated generic structs (e.g. void MyValueType<string>::m()) |
1644 | // - unboxed 'this' pointer in value-type instance methods don't have MethodTable pointer by definition |
1645 | // * shared instance and default interface methods called via interface dispatch (e. g. IFoo<string>.Foo calling into IFoo<object>::Foo()) |
1646 | // - this pointer is ambiguous as it can implement more than one IFoo<T> |
1647 | BOOL MethodDesc::RequiresInstMethodTableArg() |
1648 | { |
1649 | LIMITED_METHOD_DAC_CONTRACT; |
1650 | |
1651 | return |
1652 | IsSharedByGenericInstantiations() && |
1653 | !HasMethodInstantiation() && |
1654 | (IsStatic() || GetMethodTable()->IsValueType() || (GetMethodTable()->IsInterface() && !IsAbstract())); |
1655 | |
1656 | } |
1657 | |
1658 | //******************************************************************************* |
1659 | // Does this method require an extra InstantiatedMethodDesc argument for instantiation information? |
1660 | // This is the case for |
1661 | // * shared-code instantiated generic methods |
1662 | BOOL MethodDesc::RequiresInstMethodDescArg() |
1663 | { |
1664 | LIMITED_METHOD_DAC_CONTRACT; |
1665 | |
1666 | return IsSharedByGenericInstantiations() && |
1667 | HasMethodInstantiation(); |
1668 | } |
1669 | |
1670 | //******************************************************************************* |
1671 | // Does this method require any kind of extra argument for instantiation information? |
1672 | BOOL MethodDesc::RequiresInstArg() |
1673 | { |
1674 | LIMITED_METHOD_DAC_CONTRACT; |
1675 | |
1676 | BOOL fRet = IsSharedByGenericInstantiations() && |
1677 | (HasMethodInstantiation() || IsStatic() || GetMethodTable()->IsValueType() || (GetMethodTable()->IsInterface() && !IsAbstract())); |
1678 | |
1679 | _ASSERT(fRet == (RequiresInstMethodTableArg() || RequiresInstMethodDescArg())); |
1680 | return fRet; |
1681 | } |
1682 | |
1683 | //******************************************************************************* |
1684 | BOOL MethodDesc::IsRuntimeMethodHandle() |
1685 | { |
1686 | WRAPPER_NO_CONTRACT; |
1687 | |
1688 | // <TODO> Refine this check further for BoxedEntryPointStubs </TODO> |
1689 | return (!HasMethodInstantiation() || !IsSharedByGenericMethodInstantiations()); |
1690 | } |
1691 | |
1692 | //******************************************************************************* |
1693 | // Strip off method and class instantiation if present e.g. |
1694 | // C1<int>.m1<string> -> C1.m1 |
1695 | // C1<int>.m2 -> C1.m2 |
1696 | // C2.m2<int> -> C2.m2 |
1697 | // C2.m2 -> C2.m2 |
1698 | MethodDesc* MethodDesc::LoadTypicalMethodDefinition() |
1699 | { |
1700 | CONTRACT(MethodDesc*) |
1701 | { |
1702 | THROWS; |
1703 | GC_TRIGGERS; |
1704 | INJECT_FAULT(COMPlusThrowOM();); |
1705 | POSTCONDITION(CheckPointer(RETVAL)); |
1706 | POSTCONDITION(RETVAL->IsTypicalMethodDefinition()); |
1707 | } |
1708 | CONTRACT_END |
1709 | |
1710 | #ifndef DACCESS_COMPILE |
1711 | if (HasClassOrMethodInstantiation()) |
1712 | { |
1713 | MethodTable *pMT = GetMethodTable(); |
1714 | if (!pMT->IsTypicalTypeDefinition()) |
1715 | pMT = ClassLoader::LoadTypeDefThrowing(pMT->GetModule(), |
1716 | pMT->GetCl(), |
1717 | ClassLoader::ThrowIfNotFound, |
1718 | ClassLoader::PermitUninstDefOrRef).GetMethodTable(); |
1719 | CONSISTENCY_CHECK(TypeHandle(pMT).CheckFullyLoaded()); |
1720 | MethodDesc *resultMD = pMT->GetParallelMethodDesc(this); |
1721 | PREFIX_ASSUME(resultMD != NULL); |
1722 | resultMD->CheckRestore(); |
1723 | RETURN (resultMD); |
1724 | } |
1725 | else |
1726 | #endif // !DACCESS_COMPILE |
1727 | RETURN(this); |
1728 | } |
1729 | |
1730 | //******************************************************************************* |
1731 | BOOL MethodDesc::IsTypicalMethodDefinition() const |
1732 | { |
1733 | LIMITED_METHOD_CONTRACT; |
1734 | |
1735 | if (HasMethodInstantiation() && !IsGenericMethodDefinition()) |
1736 | return FALSE; |
1737 | |
1738 | if (HasClassInstantiation() && !GetMethodTable()->IsGenericTypeDefinition()) |
1739 | return FALSE; |
1740 | |
1741 | return TRUE; |
1742 | } |
1743 | |
1744 | //******************************************************************************* |
1745 | BOOL MethodDesc::AcquiresInstMethodTableFromThis() { |
1746 | LIMITED_METHOD_CONTRACT; |
1747 | SUPPORTS_DAC; |
1748 | |
1749 | return |
1750 | IsSharedByGenericInstantiations() && |
1751 | !HasMethodInstantiation() && |
1752 | !IsStatic() && |
1753 | !GetMethodTable()->IsValueType() && |
1754 | !(GetMethodTable()->IsInterface() && !IsAbstract()); |
1755 | } |
1756 | |
1757 | //******************************************************************************* |
1758 | UINT MethodDesc::SizeOfArgStack() |
1759 | { |
1760 | WRAPPER_NO_CONTRACT; |
1761 | MetaSig msig(this); |
1762 | ArgIterator argit(&msig); |
1763 | return argit.SizeOfArgStack(); |
1764 | } |
1765 | |
1766 | |
1767 | UINT MethodDesc::SizeOfNativeArgStack() |
1768 | { |
1769 | #ifndef UNIX_AMD64_ABI |
1770 | return SizeOfArgStack(); |
1771 | #else |
1772 | WRAPPER_NO_CONTRACT; |
1773 | MetaSig msig(this); |
1774 | PInvokeArgIterator argit(&msig); |
1775 | return argit.SizeOfArgStack(); |
1776 | #endif |
1777 | } |
1778 | |
1779 | #ifdef _TARGET_X86_ |
1780 | //******************************************************************************* |
1781 | UINT MethodDesc::CbStackPop() |
1782 | { |
1783 | WRAPPER_NO_CONTRACT; |
1784 | SUPPORTS_DAC; |
1785 | MetaSig msig(this); |
1786 | ArgIterator argit(&msig); |
1787 | return argit.CbStackPop(); |
1788 | } |
1789 | #endif // _TARGET_X86_ |
1790 | |
1791 | #ifndef DACCESS_COMPILE |
1792 | |
1793 | //******************************************************************************* |
1794 | // Strip off the method instantiation (if present) e.g. |
1795 | // C<int>.m<string> -> C<int>.m |
1796 | // D.m<string> -> D.m |
1797 | // Note that this also canonicalizes the owning method table |
1798 | // @todo check uses and clean this up |
1799 | MethodDesc* MethodDesc::StripMethodInstantiation() |
1800 | { |
1801 | CONTRACT(MethodDesc*) |
1802 | { |
1803 | NOTHROW; |
1804 | GC_NOTRIGGER; |
1805 | FORBID_FAULT; |
1806 | SO_TOLERANT; |
1807 | POSTCONDITION(CheckPointer(RETVAL)); |
1808 | } |
1809 | CONTRACT_END |
1810 | |
1811 | if (!HasClassOrMethodInstantiation()) |
1812 | RETURN(this); |
1813 | |
1814 | MethodTable *pMT = GetMethodTable()->GetCanonicalMethodTable(); |
1815 | MethodDesc *resultMD = pMT->GetParallelMethodDesc(this); |
1816 | _ASSERTE(resultMD->IsGenericMethodDefinition() || !resultMD->HasMethodInstantiation()); |
1817 | RETURN(resultMD); |
1818 | } |
1819 | |
1820 | //******************************************************************************* |
1821 | MethodDescChunk *MethodDescChunk::CreateChunk(LoaderHeap *pHeap, DWORD methodDescCount, |
1822 | DWORD classification, BOOL fNonVtableSlot, BOOL fNativeCodeSlot, BOOL fComPlusCallInfo, MethodTable *pInitialMT, AllocMemTracker *pamTracker) |
1823 | { |
1824 | CONTRACT(MethodDescChunk *) |
1825 | { |
1826 | THROWS; |
1827 | GC_NOTRIGGER; |
1828 | INJECT_FAULT(ThrowOutOfMemory()); |
1829 | |
1830 | PRECONDITION(CheckPointer(pHeap)); |
1831 | PRECONDITION(CheckPointer(pInitialMT)); |
1832 | PRECONDITION(CheckPointer(pamTracker)); |
1833 | |
1834 | POSTCONDITION(CheckPointer(RETVAL)); |
1835 | } |
1836 | CONTRACT_END; |
1837 | |
1838 | SIZE_T oneSize = MethodDesc::GetBaseSize(classification); |
1839 | |
1840 | if (fNonVtableSlot) |
1841 | oneSize += sizeof(MethodDesc::NonVtableSlot); |
1842 | |
1843 | if (fNativeCodeSlot) |
1844 | oneSize += sizeof(MethodDesc::NativeCodeSlot); |
1845 | |
1846 | #ifdef FEATURE_COMINTEROP |
1847 | if (fComPlusCallInfo) |
1848 | oneSize += sizeof(ComPlusCallInfo); |
1849 | #else // FEATURE_COMINTEROP |
1850 | _ASSERTE(!fComPlusCallInfo); |
1851 | #endif // FEATURE_COMINTEROP |
1852 | |
1853 | _ASSERTE((oneSize & MethodDesc::ALIGNMENT_MASK) == 0); |
1854 | |
1855 | DWORD maxMethodDescsPerChunk = MethodDescChunk::MaxSizeOfMethodDescs / oneSize; |
1856 | |
1857 | if (methodDescCount == 0) |
1858 | methodDescCount = maxMethodDescsPerChunk; |
1859 | |
1860 | MethodDescChunk * pFirstChunk = NULL; |
1861 | |
1862 | do |
1863 | { |
1864 | DWORD count = min(methodDescCount, maxMethodDescsPerChunk); |
1865 | |
1866 | void * pMem = pamTracker->Track( |
1867 | pHeap->AllocMem(S_SIZE_T(sizeof(TADDR) + sizeof(MethodDescChunk) + oneSize * count))); |
1868 | |
1869 | // Skip pointer to temporary entrypoints |
1870 | MethodDescChunk * pChunk = (MethodDescChunk *)((BYTE*)pMem + sizeof(TADDR)); |
1871 | |
1872 | pChunk->SetSizeAndCount(oneSize * count, count); |
1873 | pChunk->SetMethodTable(pInitialMT); |
1874 | |
1875 | MethodDesc * pMD = pChunk->GetFirstMethodDesc(); |
1876 | for (DWORD i = 0; i < count; i++) |
1877 | { |
1878 | pMD->SetChunkIndex(pChunk); |
1879 | |
1880 | pMD->SetClassification(classification); |
1881 | if (fNonVtableSlot) |
1882 | pMD->SetHasNonVtableSlot(); |
1883 | if (fNativeCodeSlot) |
1884 | pMD->SetHasNativeCodeSlot(); |
1885 | #ifdef FEATURE_COMINTEROP |
1886 | if (fComPlusCallInfo) |
1887 | pMD->SetupGenericComPlusCall(); |
1888 | #endif // FEATURE_COMINTEROP |
1889 | |
1890 | _ASSERTE(pMD->SizeOf() == oneSize); |
1891 | |
1892 | pMD = (MethodDesc *)((BYTE *)pMD + oneSize); |
1893 | } |
1894 | |
1895 | pChunk->m_next.SetValueMaybeNull(pFirstChunk); |
1896 | pFirstChunk = pChunk; |
1897 | |
1898 | methodDescCount -= count; |
1899 | } |
1900 | while (methodDescCount > 0); |
1901 | |
1902 | RETURN pFirstChunk; |
1903 | } |
1904 | |
1905 | #ifndef CROSSGEN_COMPILE |
1906 | //-------------------------------------------------------------------- |
1907 | // Virtual Resolution on Objects |
1908 | // |
1909 | // Given a MethodDesc and an Object, return the target address |
1910 | // and/or the target MethodDesc and/or make a call. |
1911 | // |
1912 | // Some of the implementation of this logic is in |
1913 | // MethodTable::GetMethodDescForInterfaceMethodAndServer. |
1914 | // Those functions should really be moved here. |
1915 | //-------------------------------------------------------------------- |
1916 | |
1917 | //******************************************************************************* |
1918 | // The following resolve virtual dispatch for the given method on the given |
1919 | // object down to an actual address to call, including any |
1920 | // handling of context proxies and other thunking layers. |
1921 | MethodDesc* MethodDesc::ResolveGenericVirtualMethod(OBJECTREF *orThis) |
1922 | { |
1923 | CONTRACT(MethodDesc *) |
1924 | { |
1925 | THROWS; |
1926 | GC_TRIGGERS; |
1927 | |
1928 | PRECONDITION(IsVtableMethod()); |
1929 | PRECONDITION(IsRestored_NoLogging()); |
1930 | PRECONDITION(HasMethodInstantiation()); |
1931 | PRECONDITION(!ContainsGenericVariables()); |
1932 | POSTCONDITION(CheckPointer(RETVAL)); |
1933 | POSTCONDITION(RETVAL->HasMethodInstantiation()); |
1934 | } |
1935 | CONTRACT_END; |
1936 | |
1937 | // Method table of target (might be instantiated) |
1938 | MethodTable *pObjMT = (*orThis)->GetMethodTable(); |
1939 | |
1940 | // This is the static method descriptor describing the call. |
1941 | // It is not the destination of the call, which we must compute. |
1942 | MethodDesc* pStaticMD = this; |
1943 | |
1944 | // Strip off the method instantiation if present |
1945 | MethodDesc* pStaticMDWithoutGenericMethodArgs = pStaticMD->StripMethodInstantiation(); |
1946 | |
1947 | // Compute the target, though we have not yet applied the type arguments. |
1948 | MethodDesc *pTargetMDBeforeGenericMethodArgs = |
1949 | pStaticMD->IsInterface() |
1950 | ? MethodTable::GetMethodDescForInterfaceMethodAndServer(TypeHandle(pStaticMD->GetMethodTable()), |
1951 | pStaticMDWithoutGenericMethodArgs,orThis) |
1952 | : pObjMT->GetMethodDescForSlot(pStaticMDWithoutGenericMethodArgs->GetSlot()); |
1953 | |
1954 | pTargetMDBeforeGenericMethodArgs->CheckRestore(); |
1955 | |
1956 | // The actual destination may lie anywhere in the inheritance hierarchy. |
1957 | // between the static descriptor and the target object. |
1958 | // So now compute where we are really going! This may be an instantiated |
1959 | // class type if the generic virtual lies in a generic class. |
1960 | MethodTable *pTargetMT = pTargetMDBeforeGenericMethodArgs->GetMethodTable(); |
1961 | |
1962 | // No need to find/create a new generic instantiation if the target is the |
1963 | // same as the static, i.e. the virtual method has not been overriden. |
1964 | if (!pTargetMT->IsSharedByGenericInstantiations() && !pTargetMT->IsValueType() && |
1965 | pTargetMDBeforeGenericMethodArgs == pStaticMDWithoutGenericMethodArgs) |
1966 | RETURN(pStaticMD); |
1967 | |
1968 | if (pTargetMT->IsSharedByGenericInstantiations()) |
1969 | { |
1970 | pTargetMT = ClassLoader::LoadGenericInstantiationThrowing(pTargetMT->GetModule(), |
1971 | pTargetMT->GetCl(), |
1972 | pTargetMDBeforeGenericMethodArgs->GetExactClassInstantiation(TypeHandle(pObjMT))).GetMethodTable(); |
1973 | } |
1974 | |
1975 | RETURN(MethodDesc::FindOrCreateAssociatedMethodDesc( |
1976 | pTargetMDBeforeGenericMethodArgs, |
1977 | pTargetMT, |
1978 | (pTargetMT->IsValueType()), /* get unboxing entry point if a struct*/ |
1979 | pStaticMD->GetMethodInstantiation(), |
1980 | FALSE /* no allowInstParam */ )); |
1981 | } |
1982 | |
1983 | //******************************************************************************* |
1984 | PCODE MethodDesc::GetSingleCallableAddrOfVirtualizedCode(OBJECTREF *orThis, TypeHandle staticTH) |
1985 | { |
1986 | WRAPPER_NO_CONTRACT; |
1987 | PRECONDITION(IsVtableMethod()); |
1988 | |
1989 | MethodTable *pObjMT = (*orThis)->GetMethodTable(); |
1990 | |
1991 | if (HasMethodInstantiation()) |
1992 | { |
1993 | CheckRestore(); |
1994 | MethodDesc *pResultMD = ResolveGenericVirtualMethod(orThis); |
1995 | |
1996 | // If we're remoting this call we can't call directly on the returned |
1997 | // method desc, we need to go through a stub that guarantees we end up |
1998 | // in the remoting handler. The stub we use below is normally just for |
1999 | // non-virtual calls on virtual methods (that have the same problem |
2000 | // where we could end up bypassing the remoting system), but it serves |
2001 | // our purpose here (basically pushes our correctly instantiated, |
2002 | // resolved method desc on the stack and calls the remoting code). |
2003 | |
2004 | return pResultMD->GetSingleCallableAddrOfCode(); |
2005 | } |
2006 | |
2007 | if (IsInterface()) |
2008 | { |
2009 | MethodDesc * pTargetMD = MethodTable::GetMethodDescForInterfaceMethodAndServer(staticTH,this,orThis); |
2010 | return pTargetMD->GetSingleCallableAddrOfCode(); |
2011 | } |
2012 | |
2013 | return pObjMT->GetRestoredSlot(GetSlot()); |
2014 | } |
2015 | |
2016 | //******************************************************************************* |
2017 | // The following resolve virtual dispatch for the given method on the given |
2018 | // object down to an actual address to call, including any |
2019 | // handling of context proxies and other thunking layers. |
2020 | PCODE MethodDesc::GetMultiCallableAddrOfVirtualizedCode(OBJECTREF *orThis, TypeHandle staticTH) |
2021 | { |
2022 | CONTRACT(PCODE) |
2023 | { |
2024 | THROWS; |
2025 | GC_TRIGGERS; |
2026 | |
2027 | PRECONDITION(IsRestored_NoLogging()); |
2028 | PRECONDITION(IsVtableMethod()); |
2029 | POSTCONDITION(RETVAL != NULL); |
2030 | } |
2031 | CONTRACT_END; |
2032 | |
2033 | // Method table of target (might be instantiated) |
2034 | MethodTable *pObjMT = (*orThis)->GetMethodTable(); |
2035 | |
2036 | // This is the static method descriptor describing the call. |
2037 | // It is not the destination of the call, which we must compute. |
2038 | MethodDesc* pStaticMD = this; |
2039 | MethodDesc *pTargetMD; |
2040 | |
2041 | if (pStaticMD->HasMethodInstantiation()) |
2042 | { |
2043 | CheckRestore(); |
2044 | pTargetMD = ResolveGenericVirtualMethod(orThis); |
2045 | |
2046 | // If we're remoting this call we can't call directly on the returned |
2047 | // method desc, we need to go through a stub that guarantees we end up |
2048 | // in the remoting handler. The stub we use below is normally just for |
2049 | // non-virtual calls on virtual methods (that have the same problem |
2050 | // where we could end up bypassing the remoting system), but it serves |
2051 | // our purpose here (basically pushes our correctly instantiated, |
2052 | // resolved method desc on the stack and calls the remoting code). |
2053 | |
2054 | RETURN(pTargetMD->GetMultiCallableAddrOfCode()); |
2055 | } |
2056 | |
2057 | if (pStaticMD->IsInterface()) |
2058 | { |
2059 | pTargetMD = MethodTable::GetMethodDescForInterfaceMethodAndServer(staticTH,pStaticMD,orThis); |
2060 | RETURN(pTargetMD->GetMultiCallableAddrOfCode()); |
2061 | } |
2062 | |
2063 | |
2064 | pTargetMD = pObjMT->GetMethodDescForSlot(pStaticMD->GetSlot()); |
2065 | |
2066 | RETURN (pTargetMD->GetMultiCallableAddrOfCode()); |
2067 | } |
2068 | |
2069 | //******************************************************************************* |
2070 | PCODE MethodDesc::GetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags /*=CORINFO_ACCESS_LDFTN*/) |
2071 | { |
2072 | CONTRACTL |
2073 | { |
2074 | THROWS; |
2075 | GC_TRIGGERS; |
2076 | INJECT_FAULT(COMPlusThrowOM()); |
2077 | } |
2078 | CONTRACTL_END |
2079 | |
2080 | PCODE ret = TryGetMultiCallableAddrOfCode(accessFlags); |
2081 | |
2082 | if (ret == NULL) |
2083 | { |
2084 | GCX_COOP(); |
2085 | |
2086 | // We have to allocate funcptr stub |
2087 | ret = GetLoaderAllocator()->GetFuncPtrStubs()->GetFuncPtrStub(this); |
2088 | } |
2089 | |
2090 | return ret; |
2091 | } |
2092 | |
2093 | //******************************************************************************* |
2094 | // |
2095 | // Returns a callable entry point for a function. |
2096 | // Multiple entry points could be used for a single function. |
2097 | // ie. this function is not idempotent |
2098 | // |
2099 | |
2100 | // We must ensure that GetMultiCallableAddrOfCode works |
2101 | // correctly for all of the following cases: |
2102 | // 1. shared generic method instantiations |
2103 | // 2. unshared generic method instantiations |
2104 | // 3. instance methods in shared generic classes |
2105 | // 4. instance methods in unshared generic classes |
2106 | // 5. static methods in shared generic classes. |
2107 | // 6. static methods in unshared generic classes. |
2108 | // |
2109 | // For case 1 and 5 the methods are implemented using |
2110 | // an instantiating stub (i.e. IsInstantiatingStub() |
2111 | // should be true). These stubs pass on to |
2112 | // shared-generic-code-which-requires-an-extra-type-context-parameter. |
2113 | // So whenever we use LDFTN on these we need to give out |
2114 | // the address of an instantiating stub. |
2115 | // |
2116 | // For cases 2, 3, 4 and 6 we can just use the standard technique for LdFtn: |
2117 | // (for 2 we give out the address of the fake "slot" in InstantiatedMethodDescs) |
2118 | // (for 3 it doesn't matter if the code is shared between instantiations |
2119 | // because the instantiation context is picked up from the "this" parameter.) |
2120 | |
2121 | PCODE MethodDesc::TryGetMultiCallableAddrOfCode(CORINFO_ACCESS_FLAGS accessFlags) |
2122 | { |
2123 | CONTRACTL |
2124 | { |
2125 | THROWS; |
2126 | GC_TRIGGERS; |
2127 | INJECT_FAULT(COMPlusThrowOM()); |
2128 | } |
2129 | CONTRACTL_END |
2130 | |
2131 | // Record this method desc if required |
2132 | g_IBCLogger.LogMethodDescAccess(this); |
2133 | |
2134 | if (IsGenericMethodDefinition()) |
2135 | { |
2136 | _ASSERTE(!"Cannot take the address of an uninstantiated generic method." ); |
2137 | COMPlusThrow(kInvalidProgramException); |
2138 | } |
2139 | |
2140 | if (accessFlags & CORINFO_ACCESS_LDFTN) |
2141 | { |
2142 | // Whenever we use LDFTN on shared-generic-code-which-requires-an-extra-parameter |
2143 | // we need to give out the address of an instantiating stub. This is why we give |
2144 | // out GetStableEntryPoint() for the IsInstantiatingStub() case: this is |
2145 | // safe. But first we assert that we only use GetMultiCallableAddrOfCode on |
2146 | // the instantiating stubs and not on the shared code itself. |
2147 | _ASSERTE(!RequiresInstArg()); |
2148 | _ASSERTE(!IsSharedByGenericMethodInstantiations()); |
2149 | |
2150 | // No other access flags are valid with CORINFO_ACCESS_LDFTN |
2151 | _ASSERTE((accessFlags & ~CORINFO_ACCESS_LDFTN) == 0); |
2152 | } |
2153 | |
2154 | // We create stable entrypoints for these upfront |
2155 | if (IsWrapperStub() || IsEnCAddedMethod()) |
2156 | return GetStableEntryPoint(); |
2157 | |
2158 | |
2159 | // For EnC always just return the stable entrypoint so we can update the code |
2160 | if (IsEnCMethod()) |
2161 | return GetStableEntryPoint(); |
2162 | |
2163 | // If the method has already been jitted, we can give out the direct address |
2164 | // Note that we may have previously created a FuncPtrStubEntry, but |
2165 | // GetMultiCallableAddrOfCode() does not need to be idempotent. |
2166 | |
2167 | if (IsFCall()) |
2168 | { |
2169 | // Call FCalls directly when possible |
2170 | if (!IsInterface() && !GetMethodTable()->ContainsGenericVariables()) |
2171 | { |
2172 | BOOL fSharedOrDynamicFCallImpl; |
2173 | PCODE pFCallImpl = ECall::GetFCallImpl(this, &fSharedOrDynamicFCallImpl); |
2174 | |
2175 | if (!fSharedOrDynamicFCallImpl) |
2176 | return pFCallImpl; |
2177 | |
2178 | // Fake ctors share one implementation that has to be wrapped by prestub |
2179 | GetOrCreatePrecode(); |
2180 | } |
2181 | } |
2182 | else |
2183 | { |
2184 | if (IsPointingToStableNativeCode()) |
2185 | return GetNativeCode(); |
2186 | } |
2187 | |
2188 | if (HasStableEntryPoint()) |
2189 | return GetStableEntryPoint(); |
2190 | |
2191 | // Force the creation of the precode if we would eventually got one anyway |
2192 | if (MayHavePrecode()) |
2193 | return GetOrCreatePrecode()->GetEntryPoint(); |
2194 | |
2195 | #ifdef HAS_COMPACT_ENTRYPOINTS |
2196 | // Caller has to call via slot or allocate funcptr stub |
2197 | return NULL; |
2198 | #else // HAS_COMPACT_ENTRYPOINTS |
2199 | // |
2200 | // Embed call to the temporary entrypoint into the code. It will be patched |
2201 | // to point to the actual code later. |
2202 | // |
2203 | return GetTemporaryEntryPoint(); |
2204 | #endif // HAS_COMPACT_ENTRYPOINTS |
2205 | } |
2206 | |
2207 | //******************************************************************************* |
2208 | PCODE MethodDesc::GetCallTarget(OBJECTREF* pThisObj, TypeHandle ownerType) |
2209 | { |
2210 | CONTRACTL |
2211 | { |
2212 | THROWS; // Resolving a generic virtual method can throw |
2213 | GC_TRIGGERS; |
2214 | MODE_COOPERATIVE; |
2215 | } |
2216 | CONTRACTL_END |
2217 | |
2218 | PCODE pTarget; |
2219 | |
2220 | if (IsVtableMethod() && !GetMethodTable()->IsValueType()) |
2221 | { |
2222 | CONSISTENCY_CHECK(NULL != pThisObj); |
2223 | if (ownerType.IsNull()) |
2224 | ownerType = GetMethodTable(); |
2225 | pTarget = GetSingleCallableAddrOfVirtualizedCode(pThisObj, ownerType); |
2226 | } |
2227 | else |
2228 | { |
2229 | pTarget = GetSingleCallableAddrOfCode(); |
2230 | } |
2231 | |
2232 | return pTarget; |
2233 | } |
2234 | |
2235 | //******************************************************************************* |
2236 | // convert an entry point into a method desc |
2237 | MethodDesc* Entry2MethodDesc(PCODE entryPoint, MethodTable *pMT) |
2238 | { |
2239 | CONTRACT(MethodDesc*) |
2240 | { |
2241 | THROWS; |
2242 | GC_TRIGGERS; |
2243 | MODE_ANY; |
2244 | POSTCONDITION(RETVAL->SanityCheck()); |
2245 | } |
2246 | CONTRACT_END |
2247 | |
2248 | MethodDesc * pMD; |
2249 | |
2250 | RangeSection * pRS = ExecutionManager::FindCodeRange(entryPoint, ExecutionManager::GetScanFlags()); |
2251 | if (pRS != NULL) |
2252 | { |
2253 | if (pRS->pjit->JitCodeToMethodInfo(pRS, entryPoint, &pMD, NULL)) |
2254 | RETURN(pMD); |
2255 | |
2256 | if (pRS->pjit->GetStubCodeBlockKind(pRS, entryPoint) == STUB_CODE_BLOCK_PRECODE) |
2257 | RETURN(MethodDesc::GetMethodDescFromStubAddr(entryPoint)); |
2258 | |
2259 | // We should never get here |
2260 | _ASSERTE(!"Entry2MethodDesc failed for RangeSection" ); |
2261 | RETURN (NULL); |
2262 | } |
2263 | |
2264 | pMD = VirtualCallStubManagerManager::Entry2MethodDesc(entryPoint, pMT); |
2265 | if (pMD != NULL) |
2266 | RETURN(pMD); |
2267 | |
2268 | |
2269 | // Is it an FCALL? |
2270 | pMD = ECall::MapTargetBackToMethod(entryPoint); |
2271 | if (pMD != NULL) |
2272 | RETURN(pMD); |
2273 | |
2274 | // We should never get here |
2275 | _ASSERTE(!"Entry2MethodDesc failed" ); |
2276 | RETURN (NULL); |
2277 | } |
2278 | #endif // CROSSGEN_COMPILE |
2279 | |
2280 | //******************************************************************************* |
2281 | BOOL MethodDesc::IsFCallOrIntrinsic() |
2282 | { |
2283 | WRAPPER_NO_CONTRACT; |
2284 | |
2285 | if (IsFCall() || IsArray()) |
2286 | return TRUE; |
2287 | |
2288 | // Intrinsic methods on ByReference<T>, Span<T>, or ReadOnlySpan<T> |
2289 | MethodTable * pMT = GetMethodTable(); |
2290 | if (pMT->IsByRefLike() && pMT->GetModule()->IsSystem()) |
2291 | return TRUE; |
2292 | |
2293 | return FALSE; |
2294 | } |
2295 | |
2296 | //******************************************************************************* |
2297 | BOOL MethodDesc::IsPointingToPrestub() |
2298 | { |
2299 | CONTRACTL |
2300 | { |
2301 | NOTHROW; |
2302 | GC_NOTRIGGER; |
2303 | SO_TOLERANT; |
2304 | MODE_ANY; |
2305 | } |
2306 | CONTRACTL_END; |
2307 | |
2308 | if (!HasStableEntryPoint()) |
2309 | return TRUE; |
2310 | |
2311 | if (!HasPrecode()) |
2312 | return FALSE; |
2313 | |
2314 | if (!IsRestored()) |
2315 | return TRUE; |
2316 | |
2317 | return GetPrecode()->IsPointingToPrestub(); |
2318 | } |
2319 | |
2320 | //******************************************************************************* |
2321 | void MethodDesc::Reset() |
2322 | { |
2323 | WRAPPER_NO_CONTRACT; |
2324 | |
2325 | // This method is not thread-safe since we are updating |
2326 | // different pieces of data non-atomically. |
2327 | // Use this only if you can guarantee thread-safety somehow. |
2328 | |
2329 | _ASSERTE(IsEnCMethod() || // The process is frozen by the debugger |
2330 | IsDynamicMethod() || // These are used in a very restricted way |
2331 | GetLoaderModule()->IsReflection()); // Rental methods |
2332 | |
2333 | // Reset any flags relevant to the old code |
2334 | ClearFlagsOnUpdate(); |
2335 | |
2336 | if (HasPrecode()) |
2337 | { |
2338 | GetPrecode()->Reset(); |
2339 | } |
2340 | else |
2341 | { |
2342 | // We should go here only for the rental methods |
2343 | _ASSERTE(GetLoaderModule()->IsReflection()); |
2344 | |
2345 | InterlockedUpdateFlags2(enum_flag2_HasStableEntryPoint | enum_flag2_HasPrecode, FALSE); |
2346 | |
2347 | TADDR slot = GetAddrOfSlot(); |
2348 | if (IsVtableSlot()) |
2349 | { |
2350 | ((MethodTable::VTableIndir2_t *) slot)->SetValue(GetTemporaryEntryPoint()); |
2351 | } |
2352 | else |
2353 | { |
2354 | *((PCODE *) slot) = GetTemporaryEntryPoint(); |
2355 | } |
2356 | } |
2357 | |
2358 | if (HasNativeCodeSlot()) |
2359 | { |
2360 | RelativePointer<TADDR> *pRelPtr = (RelativePointer<TADDR> *)GetAddrOfNativeCodeSlot(); |
2361 | pRelPtr->SetValueMaybeNull(NULL); |
2362 | } |
2363 | _ASSERTE(!HasNativeCode()); |
2364 | } |
2365 | |
2366 | //******************************************************************************* |
2367 | Dictionary* MethodDesc::GetMethodDictionary() |
2368 | { |
2369 | WRAPPER_NO_CONTRACT; |
2370 | |
2371 | return |
2372 | (GetClassification() == mcInstantiated) |
2373 | ? (Dictionary*) (AsInstantiatedMethodDesc()->IMD_GetMethodDictionary()) |
2374 | : NULL; |
2375 | } |
2376 | |
2377 | //******************************************************************************* |
2378 | DictionaryLayout* MethodDesc::GetDictionaryLayout() |
2379 | { |
2380 | WRAPPER_NO_CONTRACT; |
2381 | |
2382 | return |
2383 | ((GetClassification() == mcInstantiated) && !IsUnboxingStub()) |
2384 | ? AsInstantiatedMethodDesc()->IMD_GetDictionaryLayout() |
2385 | : NULL; |
2386 | } |
2387 | |
2388 | #endif // !DACCESS_COMPILE |
2389 | |
2390 | //******************************************************************************* |
2391 | MethodImpl *MethodDesc::GetMethodImpl() |
2392 | { |
2393 | CONTRACTL |
2394 | { |
2395 | NOTHROW; |
2396 | GC_NOTRIGGER; |
2397 | FORBID_FAULT; |
2398 | PRECONDITION(HasMethodImplSlot()); |
2399 | SUPPORTS_DAC; |
2400 | } |
2401 | CONTRACTL_END |
2402 | |
2403 | SIZE_T size = s_ClassificationSizeTable[m_wFlags & (mdcClassification | mdcHasNonVtableSlot)]; |
2404 | |
2405 | return PTR_MethodImpl(dac_cast<TADDR>(this) + size); |
2406 | } |
2407 | |
2408 | #ifndef DACCESS_COMPILE |
2409 | |
2410 | //******************************************************************************* |
2411 | BOOL MethodDesc::RequiresMethodDescCallingConvention(BOOL fEstimateForChunk /*=FALSE*/) |
2412 | { |
2413 | LIMITED_METHOD_CONTRACT; |
2414 | |
2415 | // Interop marshaling is implemented using shared stubs |
2416 | if (IsNDirect() || IsComPlusCall() || IsGenericComPlusCall()) |
2417 | return TRUE; |
2418 | |
2419 | |
2420 | return FALSE; |
2421 | } |
2422 | |
2423 | //******************************************************************************* |
2424 | BOOL MethodDesc::RequiresStableEntryPoint(BOOL fEstimateForChunk /*=FALSE*/) |
2425 | { |
2426 | LIMITED_METHOD_CONTRACT; |
2427 | |
2428 | // Create precodes for versionable methods |
2429 | if (IsVersionableWithPrecode()) |
2430 | return TRUE; |
2431 | |
2432 | // Create precodes for edit and continue to make methods updateable |
2433 | if (IsEnCMethod() || IsEnCAddedMethod()) |
2434 | return TRUE; |
2435 | |
2436 | // Precreate precodes for LCG methods so we do not leak memory when the method descs are recycled |
2437 | if (IsLCGMethod()) |
2438 | return TRUE; |
2439 | |
2440 | if (fEstimateForChunk) |
2441 | { |
2442 | // Make a best guess based on the method table of the chunk. |
2443 | if (IsInterface()) |
2444 | return TRUE; |
2445 | } |
2446 | else |
2447 | { |
2448 | // Wrapper stubs are stored in generic dictionary that's not backpatched |
2449 | if (IsWrapperStub()) |
2450 | return TRUE; |
2451 | |
2452 | // TODO: Can we avoid early allocation of precodes for interfaces and cominterop? |
2453 | if ((IsInterface() && !IsStatic() && IsVirtual()) || IsComPlusCall()) |
2454 | return TRUE; |
2455 | } |
2456 | |
2457 | return FALSE; |
2458 | } |
2459 | |
2460 | #endif // !DACCESS_COMPILE |
2461 | |
2462 | //******************************************************************************* |
2463 | BOOL MethodDesc::MayHaveNativeCode() |
2464 | { |
2465 | CONTRACTL |
2466 | { |
2467 | THROWS; |
2468 | GC_TRIGGERS; |
2469 | MODE_ANY; |
2470 | PRECONDITION(IsRestored_NoLogging()); |
2471 | } |
2472 | CONTRACTL_END |
2473 | |
2474 | // This code flow of this method should roughly match the code flow of MethodDesc::DoPrestub. |
2475 | |
2476 | switch (GetClassification()) |
2477 | { |
2478 | case mcIL: // IsIL() case. Handled below. |
2479 | break; |
2480 | case mcFCall: // FCalls do not have real native code. |
2481 | return FALSE; |
2482 | case mcNDirect: // NDirect never have native code (note that the NDirect method |
2483 | return FALSE; // does not appear as having a native code even for stubs as IL) |
2484 | case mcEEImpl: // Runtime provided implementation. No native code. |
2485 | return FALSE; |
2486 | case mcArray: // Runtime provided implementation. No native code. |
2487 | return FALSE; |
2488 | case mcInstantiated: // IsIL() case. Handled below. |
2489 | break; |
2490 | #ifdef FEATURE_COMINTEROP |
2491 | case mcComInterop: // Generated stub. No native code. |
2492 | return FALSE; |
2493 | #endif // FEATURE_COMINTEROP |
2494 | case mcDynamic: // LCG or stub-as-il. |
2495 | return TRUE; |
2496 | default: |
2497 | _ASSERTE(!"Unknown classification" ); |
2498 | } |
2499 | |
2500 | _ASSERTE(IsIL()); |
2501 | |
2502 | if ((IsInterface() && !IsStatic() && IsVirtual() && IsAbstract()) || IsWrapperStub() || ContainsGenericVariables() || IsAbstract()) |
2503 | { |
2504 | return FALSE; |
2505 | } |
2506 | |
2507 | return TRUE; |
2508 | } |
2509 | |
2510 | #ifndef DACCESS_COMPILE |
2511 | |
2512 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
2513 | //******************************************************************************* |
2514 | void MethodDesc::Save(DataImage *image) |
2515 | { |
2516 | STANDARD_VM_CONTRACT; |
2517 | |
2518 | // Initialize the DoesNotHaveEquivalentValuetypeParameters flag. |
2519 | // If we fail to determine whether there is a type-equivalent struct parameter (eg. because there is a struct parameter |
2520 | // defined in a missing dependency), then just continue. The reason we run this method is to initialize a flag that is |
2521 | // only an optimization in any case, so it doesn't really matter if it fails. |
2522 | EX_TRY |
2523 | { |
2524 | HasTypeEquivalentStructParameters(); |
2525 | } |
2526 | EX_CATCH |
2527 | { |
2528 | } |
2529 | EX_END_CATCH(RethrowTerminalExceptions); |
2530 | |
2531 | _ASSERTE(image->GetModule()->GetAssembly() == |
2532 | GetAppDomain()->ToCompilationDomain()->GetTargetAssembly()); |
2533 | |
2534 | #ifdef _DEBUG |
2535 | SString s; |
2536 | if (LoggingOn(LF_ZAP, LL_INFO10000)) |
2537 | { |
2538 | TypeString::AppendMethodDebug(s, this); |
2539 | LOG((LF_ZAP, LL_INFO10000, " MethodDesc::Save %S (%p)\n" , s.GetUnicode(), this)); |
2540 | } |
2541 | |
2542 | if (m_pszDebugMethodName && !image->IsStored((void*) m_pszDebugMethodName)) |
2543 | image->StoreStructure((void *) m_pszDebugMethodName, |
2544 | (ULONG)(strlen(m_pszDebugMethodName) + 1), |
2545 | DataImage::ITEM_DEBUG, |
2546 | 1); |
2547 | if (m_pszDebugClassName && !image->IsStored(m_pszDebugClassName)) |
2548 | image->StoreStructure((void *) m_pszDebugClassName, |
2549 | (ULONG)(strlen(m_pszDebugClassName) + 1), |
2550 | DataImage::ITEM_DEBUG, |
2551 | 1); |
2552 | if (m_pszDebugMethodSignature && !image->IsStored(m_pszDebugMethodSignature)) |
2553 | image->StoreStructure((void *) m_pszDebugMethodSignature, |
2554 | (ULONG)(strlen(m_pszDebugMethodSignature) + 1), |
2555 | DataImage::ITEM_DEBUG, |
2556 | 1); |
2557 | #endif // _DEBUG |
2558 | |
2559 | if (IsMethodImpl()) |
2560 | { |
2561 | MethodImpl *pImpl = GetMethodImpl(); |
2562 | |
2563 | pImpl->Save(image); |
2564 | } |
2565 | |
2566 | if (IsNDirect()) |
2567 | { |
2568 | EX_TRY |
2569 | { |
2570 | PInvokeStaticSigInfo sigInfo; |
2571 | NDirect::PopulateNDirectMethodDesc((NDirectMethodDesc*)this, &sigInfo); |
2572 | } |
2573 | EX_CATCH |
2574 | { |
2575 | } |
2576 | EX_END_CATCH(RethrowTerminalExceptions); |
2577 | } |
2578 | |
2579 | if (HasStoredSig()) |
2580 | { |
2581 | StoredSigMethodDesc *pNewSMD = (StoredSigMethodDesc*) this; |
2582 | |
2583 | if (pNewSMD->HasStoredMethodSig()) |
2584 | { |
2585 | if (!image->IsStored((void *) pNewSMD->m_pSig.GetValueMaybeNull())) |
2586 | { |
2587 | // Store signatures that doesn't need restore into a read only section. |
2588 | DataImage::ItemKind sigItemKind = DataImage::ITEM_STORED_METHOD_SIG_READONLY; |
2589 | // Place the signatures for stubs-as-il into hot/cold or writeable section |
2590 | // here since Module::Arrange won't place them for us. |
2591 | if (IsILStub()) |
2592 | { |
2593 | PTR_DynamicMethodDesc pDynamicMD = AsDynamicMethodDesc(); |
2594 | // Forward PInvoke never touches the signature at runtime, only reverse pinvoke does. |
2595 | if (pDynamicMD->IsReverseStub()) |
2596 | { |
2597 | sigItemKind = DataImage::ITEM_STORED_METHOD_SIG_READONLY_WARM; |
2598 | } |
2599 | |
2600 | if (FixupSignatureContainingInternalTypes(image, |
2601 | (PCCOR_SIGNATURE) pNewSMD->m_pSig.GetValueMaybeNull(), |
2602 | pNewSMD->m_cSig, |
2603 | true /*checkOnly if we will need to restore the signature without doing fixup*/)) |
2604 | { |
2605 | sigItemKind = DataImage::ITEM_STORED_METHOD_SIG; |
2606 | } |
2607 | } |
2608 | |
2609 | image->StoreInternedStructure((void *) pNewSMD->m_pSig.GetValueMaybeNull(), |
2610 | pNewSMD->m_cSig, |
2611 | sigItemKind, |
2612 | 1); |
2613 | } |
2614 | } |
2615 | } |
2616 | |
2617 | if (GetMethodDictionary()) |
2618 | { |
2619 | DWORD cBytes = DictionaryLayout::GetFirstDictionaryBucketSize(GetNumGenericMethodArgs(), GetDictionaryLayout()); |
2620 | void* pBytes = GetMethodDictionary()->AsPtr(); |
2621 | |
2622 | LOG((LF_ZAP, LL_INFO10000, " MethodDesc::Save dictionary size %d\n" , cBytes)); |
2623 | image->StoreStructure(pBytes, cBytes, |
2624 | DataImage::ITEM_DICTIONARY_WRITEABLE); |
2625 | } |
2626 | |
2627 | if (HasMethodInstantiation()) |
2628 | { |
2629 | InstantiatedMethodDesc* pIMD = AsInstantiatedMethodDesc(); |
2630 | if (pIMD->IMD_IsSharedByGenericMethodInstantiations() && !pIMD->m_pDictLayout.IsNull()) |
2631 | { |
2632 | pIMD->m_pDictLayout.GetValue()->Save(image); |
2633 | } |
2634 | } |
2635 | if (IsNDirect()) |
2636 | { |
2637 | NDirectMethodDesc *pNMD = (NDirectMethodDesc *)this; |
2638 | |
2639 | // Make sure that the marshaling required flag is computed |
2640 | pNMD->MarshalingRequired(); |
2641 | |
2642 | if (!pNMD->IsQCall()) |
2643 | { |
2644 | //Cache DefaultImportDllImportSearchPaths attribute. |
2645 | pNMD->HasDefaultDllImportSearchPathsAttribute(); |
2646 | } |
2647 | |
2648 | image->StoreStructure(pNMD->GetWriteableData(), |
2649 | sizeof(NDirectWriteableData), |
2650 | DataImage::ITEM_METHOD_DESC_COLD_WRITEABLE); |
2651 | |
2652 | #ifdef HAS_NDIRECT_IMPORT_PRECODE |
2653 | if (!pNMD->MarshalingRequired()) |
2654 | { |
2655 | // import thunk is only needed if the P/Invoke is inlinable |
2656 | #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) |
2657 | image->SavePrecode(pNMD->GetNDirectImportThunkGlue(), pNMD, PRECODE_NDIRECT_IMPORT, DataImage::ITEM_METHOD_PRECODE_COLD); |
2658 | #else |
2659 | image->StoreStructure(pNMD->GetNDirectImportThunkGlue(), sizeof(NDirectImportThunkGlue), DataImage::ITEM_METHOD_PRECODE_COLD); |
2660 | #endif |
2661 | } |
2662 | #endif |
2663 | |
2664 | if (pNMD->IsQCall()) |
2665 | { |
2666 | // Make sure QCall id is cached |
2667 | ECall::GetQCallImpl(this); |
2668 | _ASSERTE(pNMD->GetECallID() != 0); |
2669 | } |
2670 | else |
2671 | { |
2672 | LPCUTF8 pszLibName = pNMD->GetLibName(); |
2673 | if (pszLibName && !image->IsStored(pszLibName)) |
2674 | { |
2675 | image->StoreStructure(pszLibName, |
2676 | (ULONG)strlen(pszLibName) + 1, |
2677 | DataImage::ITEM_STORED_METHOD_NAME, |
2678 | 1); |
2679 | } |
2680 | |
2681 | LPCUTF8 pszEntrypointName = pNMD->GetEntrypointName(); |
2682 | if (pszEntrypointName != NULL && !image->IsStored(pszEntrypointName)) |
2683 | { |
2684 | image->StoreStructure(pszEntrypointName, |
2685 | (ULONG)strlen(pszEntrypointName) + 1, |
2686 | DataImage::ITEM_STORED_METHOD_NAME, |
2687 | 1); |
2688 | } |
2689 | } |
2690 | } |
2691 | |
2692 | // ContainsGenericVariables() check is required to support generic FCalls |
2693 | // (only instance methods on generic types constrained to "class" are allowed) |
2694 | if(!IsUnboxingStub() && IsFCall() && !GetMethodTable()->ContainsGenericVariables()) |
2695 | { |
2696 | // Make sure that ECall::GetFCallImpl is called for all methods. It has the |
2697 | // side effect of adding the methoddesc to the reverse fcall hash table. |
2698 | // MethodDesc::Save would eventually return to Module::Save which is where |
2699 | // we would save the reverse fcall table also. Thus this call is effectively populating |
2700 | // that reverse fcall table. |
2701 | |
2702 | ECall::GetFCallImpl(this); |
2703 | } |
2704 | |
2705 | if (IsDynamicMethod()) |
2706 | { |
2707 | DynamicMethodDesc *pDynMeth = AsDynamicMethodDesc(); |
2708 | if (!pDynMeth->m_pszMethodName.IsNull() |
2709 | && !image->IsStored(pDynMeth->m_pszMethodName.GetValue())) |
2710 | image->StoreStructure((void *) pDynMeth->m_pszMethodName.GetValue(), |
2711 | (ULONG)(strlen(pDynMeth->m_pszMethodName.GetValue()) + 1), |
2712 | DataImage::ITEM_STORED_METHOD_NAME, |
2713 | 1); |
2714 | } |
2715 | |
2716 | #ifdef FEATURE_COMINTEROP |
2717 | if (IsComPlusCall()) |
2718 | { |
2719 | ComPlusCallMethodDesc *pCMD = (ComPlusCallMethodDesc *)this; |
2720 | ComPlusCallInfo *pComInfo = pCMD->m_pComPlusCallInfo; |
2721 | |
2722 | if (pComInfo != NULL && pComInfo->ShouldSave(image)) |
2723 | { |
2724 | image->StoreStructure(pCMD->m_pComPlusCallInfo, |
2725 | sizeof(ComPlusCallInfo), |
2726 | DataImage::ITEM_METHOD_DESC_COLD_WRITEABLE); |
2727 | } |
2728 | } |
2729 | #endif // FEATURE_COMINTEROP |
2730 | |
2731 | LOG((LF_ZAP, LL_INFO10000, " MethodDesc::Save %S (%p) complete\n" , s.GetUnicode(), this)); |
2732 | |
2733 | } |
2734 | |
2735 | //******************************************************************************* |
2736 | bool MethodDesc::CanSkipDoPrestub ( |
2737 | MethodDesc * callerMD, |
2738 | CorInfoIndirectCallReason *pReason, |
2739 | CORINFO_ACCESS_FLAGS accessFlags/*=CORINFO_ACCESS_ANY*/) |
2740 | { |
2741 | STANDARD_VM_CONTRACT; |
2742 | |
2743 | CorInfoIndirectCallReason dummy; |
2744 | if (pReason == NULL) |
2745 | pReason = &dummy; |
2746 | *pReason = CORINFO_INDIRECT_CALL_UNKNOWN; |
2747 | |
2748 | // Only IL can be called directly |
2749 | if (!IsIL()) |
2750 | { |
2751 | // Pretend that IL stubs can be called directly. It allows us to not have |
2752 | // useless precode for IL stubs |
2753 | if (IsILStub()) |
2754 | return true; |
2755 | |
2756 | if (IsNDirect()) |
2757 | { |
2758 | *pReason = CORINFO_INDIRECT_CALL_PINVOKE; |
2759 | return false; |
2760 | } |
2761 | |
2762 | *pReason = CORINFO_INDIRECT_CALL_EXOTIC; |
2763 | return false; |
2764 | } |
2765 | |
2766 | // @todo generics: Until we fix the RVA map in zapper.cpp to be instantiation-aware, this must remain |
2767 | CheckRestore(); |
2768 | |
2769 | // The wrapper stubs cannot be called directly (like any other stubs) |
2770 | if (IsWrapperStub()) |
2771 | { |
2772 | *pReason = CORINFO_INDIRECT_CALL_STUB; |
2773 | return false; |
2774 | } |
2775 | |
2776 | |
2777 | // Check whether our methoddesc needs restore |
2778 | if (NeedsRestore(GetAppDomain()->ToCompilationDomain()->GetTargetImage(), TRUE)) |
2779 | { |
2780 | // The speculative method instantiations are restored by the time we call them via indirection. |
2781 | if (!IsTightlyBoundToMethodTable() && |
2782 | GetLoaderModule() != Module::GetPreferredZapModuleForMethodDesc(this)) |
2783 | { |
2784 | // We should only take this codepath to determine whether method needs prestub. |
2785 | // Cross module calls should be filtered out by CanEmbedMethodHandle earlier. |
2786 | _ASSERTE(GetLoaderModule() == GetAppDomain()->ToCompilationDomain()->GetTargetModule()); |
2787 | |
2788 | return true; |
2789 | } |
2790 | |
2791 | *pReason = CORINFO_INDIRECT_CALL_RESTORE_METHOD; |
2792 | return false; |
2793 | } |
2794 | |
2795 | ///////////////////////////////////////////////////////////////////////////////// |
2796 | // The method looks OK. Check class restore. |
2797 | MethodTable * calleeMT = GetMethodTable(); |
2798 | |
2799 | // If no need for restore, we can call direct. |
2800 | if (!calleeMT->NeedsRestore(GetAppDomain()->ToCompilationDomain()->GetTargetImage())) |
2801 | return true; |
2802 | |
2803 | // We will override this with more specific reason if we find one |
2804 | *pReason = CORINFO_INDIRECT_CALL_RESTORE; |
2805 | |
2806 | ///////////////////////////////////////////////////////////////////////////////// |
2807 | // Try to prove that we have done the restore already. |
2808 | |
2809 | // If we're calling the same class, we can assume already initialized. |
2810 | if (callerMD != NULL) |
2811 | { |
2812 | MethodTable * callerMT = callerMD->GetMethodTable(); |
2813 | if (calleeMT == callerMT) |
2814 | return true; |
2815 | } |
2816 | |
2817 | // If we are called on non-NULL this pointer, we can assume that class is initialized. |
2818 | if (accessFlags & CORINFO_ACCESS_NONNULL) |
2819 | { |
2820 | // Static methods may be first time call on the class |
2821 | if (IsStatic()) |
2822 | { |
2823 | *pReason = CORINFO_INDIRECT_CALL_RESTORE_FIRST_CALL; |
2824 | } |
2825 | else |
2826 | // In some cases, instance value type methods may be called before an instance initializer |
2827 | if (calleeMT->IsValueType()) |
2828 | { |
2829 | *pReason = CORINFO_INDIRECT_CALL_RESTORE_VALUE_TYPE; |
2830 | } |
2831 | else |
2832 | { |
2833 | // Otherwise, we conclude that there must have been at least one call on the class already. |
2834 | return true; |
2835 | } |
2836 | } |
2837 | |
2838 | // If child calls its parent class, we can assume already restored. |
2839 | if (callerMD != NULL) |
2840 | { |
2841 | MethodTable * parentMT = callerMD->GetMethodTable()->GetParentMethodTable(); |
2842 | while (parentMT != NULL) |
2843 | { |
2844 | if (calleeMT == parentMT) |
2845 | return true; |
2846 | parentMT = parentMT->GetParentMethodTable(); |
2847 | } |
2848 | } |
2849 | |
2850 | // The speculative method table instantiations are restored by the time we call methods on them via indirection. |
2851 | if (IsTightlyBoundToMethodTable() && |
2852 | calleeMT->GetLoaderModule() != Module::GetPreferredZapModuleForMethodTable(calleeMT)) |
2853 | { |
2854 | // We should only take this codepath to determine whether method needs prestub. |
2855 | // Cross module calls should be filtered out by CanEmbedMethodHandle earlier. |
2856 | _ASSERTE(calleeMT->GetLoaderModule() == GetAppDomain()->ToCompilationDomain()->GetTargetModule()); |
2857 | |
2858 | return true; |
2859 | } |
2860 | |
2861 | // Note: Reason for restore has been initialized earlier |
2862 | return false; |
2863 | } |
2864 | |
2865 | //******************************************************************************* |
2866 | BOOL MethodDesc::ComputeNeedsRestore(DataImage *image, TypeHandleList *pVisited, BOOL fAssumeMethodTableRestored/*=FALSE*/) |
2867 | { |
2868 | STATIC_STANDARD_VM_CONTRACT; |
2869 | |
2870 | _ASSERTE(GetAppDomain()->IsCompilationDomain()); |
2871 | |
2872 | MethodTable * pMT = GetMethodTable(); |
2873 | |
2874 | if (!IsTightlyBoundToMethodTable()) |
2875 | { |
2876 | if (!image->CanEagerBindToMethodTable(pMT)) |
2877 | return TRUE; |
2878 | } |
2879 | |
2880 | if (!fAssumeMethodTableRestored) |
2881 | { |
2882 | if (pMT->ComputeNeedsRestore(image, pVisited)) |
2883 | return TRUE; |
2884 | } |
2885 | |
2886 | if (GetClassification() == mcInstantiated) |
2887 | { |
2888 | InstantiatedMethodDesc* pIMD = AsInstantiatedMethodDesc(); |
2889 | |
2890 | if (pIMD->IMD_IsWrapperStubWithInstantiations()) |
2891 | { |
2892 | if (!image->CanPrerestoreEagerBindToMethodDesc(pIMD->m_pWrappedMethodDesc.GetValue(), pVisited)) |
2893 | return TRUE; |
2894 | |
2895 | if (!image->CanHardBindToZapModule(pIMD->m_pWrappedMethodDesc.GetValue()->GetLoaderModule())) |
2896 | return TRUE; |
2897 | } |
2898 | |
2899 | if (GetMethodDictionary()) |
2900 | { |
2901 | if (GetMethodDictionary()->ComputeNeedsRestore(image, pVisited, GetNumGenericMethodArgs())) |
2902 | return TRUE; |
2903 | } |
2904 | } |
2905 | |
2906 | return FALSE; |
2907 | } |
2908 | |
2909 | |
2910 | //--------------------------------------------------------------------------------------- |
2911 | // |
2912 | // Fixes up ET_INTERNAL TypeHandles in an IL stub signature. If at least one type is fixed up |
2913 | // marks the signature as "needs restore". Also handles probing through generic instantiations |
2914 | // to find ET_INTERNAL TypeHandles used as the generic type or its parameters. |
2915 | // |
2916 | // This function will parse one type and expects psig to be pointing to the element type. If |
2917 | // the type is a generic instantiation, we will recursively parse it. |
2918 | // |
2919 | bool |
2920 | FixupSignatureContainingInternalTypesParseType( |
2921 | DataImage * image, |
2922 | PCCOR_SIGNATURE pOriginalSig, |
2923 | SigPointer & psig, |
2924 | bool checkOnly) |
2925 | { |
2926 | CONTRACTL |
2927 | { |
2928 | THROWS; |
2929 | GC_TRIGGERS; |
2930 | } |
2931 | CONTRACTL_END; |
2932 | |
2933 | SigPointer sigOrig = psig; |
2934 | |
2935 | CorElementType eType; |
2936 | IfFailThrow(psig.GetElemType(&eType)); |
2937 | |
2938 | switch (eType) |
2939 | { |
2940 | case ELEMENT_TYPE_INTERNAL: |
2941 | { |
2942 | TypeHandle * pTypeHandle = (TypeHandle *)psig.GetPtr(); |
2943 | |
2944 | void * ptr; |
2945 | IfFailThrow(psig.GetPointer(&ptr)); |
2946 | |
2947 | if (!checkOnly) |
2948 | { |
2949 | // Always force creation of fixup to avoid unaligned relocation entries. Unaligned |
2950 | // relocations entries are perf hit for ASLR, and they even disable ASLR on ARM. |
2951 | image->FixupTypeHandlePointerInPlace((BYTE *)pOriginalSig, (BYTE *)pTypeHandle - (BYTE *)pOriginalSig, TRUE); |
2952 | |
2953 | // mark the signature so we know we'll need to restore it |
2954 | BYTE *pImageSig = (BYTE *)image->GetImagePointer((PVOID)pOriginalSig); |
2955 | *pImageSig |= IMAGE_CEE_CS_CALLCONV_NEEDSRESTORE; |
2956 | } |
2957 | } |
2958 | return true; |
2959 | |
2960 | case ELEMENT_TYPE_GENERICINST: |
2961 | { |
2962 | bool needsRestore = FixupSignatureContainingInternalTypesParseType(image, pOriginalSig, psig, checkOnly); |
2963 | |
2964 | // Get generic arg count |
2965 | ULONG nArgs; |
2966 | IfFailThrow(psig.GetData(&nArgs)); |
2967 | |
2968 | for (ULONG i = 0; i < nArgs; i++) |
2969 | { |
2970 | if (FixupSignatureContainingInternalTypesParseType(image, pOriginalSig, psig, checkOnly)) |
2971 | { |
2972 | needsRestore = true; |
2973 | } |
2974 | } |
2975 | |
2976 | // Return. We don't want to call psig.SkipExactlyOne in this case since we've manually |
2977 | // parsed through the generic inst type. |
2978 | return needsRestore; |
2979 | } |
2980 | |
2981 | case ELEMENT_TYPE_BYREF: |
2982 | case ELEMENT_TYPE_PTR: |
2983 | case ELEMENT_TYPE_PINNED: |
2984 | case ELEMENT_TYPE_SZARRAY: |
2985 | // Call recursively |
2986 | return FixupSignatureContainingInternalTypesParseType(image, pOriginalSig, psig, checkOnly); |
2987 | |
2988 | default: |
2989 | IfFailThrow(sigOrig.SkipExactlyOne()); |
2990 | psig = sigOrig; |
2991 | break; |
2992 | } |
2993 | |
2994 | return false; |
2995 | } |
2996 | |
2997 | //--------------------------------------------------------------------------------------- |
2998 | // |
2999 | // Fixes up ET_INTERNAL TypeHandles in an IL stub signature. If at least one type is fixed up |
3000 | // marks the signature as "needs restore". |
3001 | // |
3002 | bool |
3003 | FixupSignatureContainingInternalTypes( |
3004 | DataImage * image, |
3005 | PCCOR_SIGNATURE pSig, |
3006 | DWORD cSig, |
3007 | bool checkOnly) |
3008 | { |
3009 | CONTRACTL |
3010 | { |
3011 | THROWS; |
3012 | GC_TRIGGERS; |
3013 | } |
3014 | CONTRACTL_END; |
3015 | |
3016 | ULONG nArgs; |
3017 | bool needsRestore = false; |
3018 | |
3019 | SigPointer psig(pSig, cSig); |
3020 | |
3021 | // Skip calling convention |
3022 | BYTE uCallConv; |
3023 | IfFailThrow(psig.GetByte(&uCallConv)); |
3024 | |
3025 | if ((uCallConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_FIELD) |
3026 | { |
3027 | ThrowHR(META_E_BAD_SIGNATURE); |
3028 | } |
3029 | |
3030 | // Skip type parameter count |
3031 | if (uCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) |
3032 | { |
3033 | IfFailThrow(psig.GetData(NULL)); |
3034 | } |
3035 | |
3036 | // Get arg count |
3037 | IfFailThrow(psig.GetData(&nArgs)); |
3038 | |
3039 | nArgs++; // be sure to handle the return type |
3040 | |
3041 | for (ULONG i = 0; i < nArgs; i++) |
3042 | { |
3043 | if (FixupSignatureContainingInternalTypesParseType(image, pSig, psig, checkOnly)) |
3044 | { |
3045 | needsRestore = true; |
3046 | } |
3047 | } |
3048 | return needsRestore; |
3049 | } // FixupSignatureContainingInternalTypes |
3050 | #endif // FEATURE_NATIVE_IMAGE_GENERATION |
3051 | |
3052 | #ifdef FEATURE_PREJIT |
3053 | //--------------------------------------------------------------------------------------- |
3054 | // |
3055 | // Restores ET_INTERNAL TypeHandles in an IL stub signature. |
3056 | // This function will parse one type and expects psig to be pointing to the element type. If |
3057 | // the type is a generic instantiation, we will recursively parse it. |
3058 | // |
3059 | void |
3060 | RestoreSignatureContainingInternalTypesParseType( |
3061 | SigPointer & psig) |
3062 | { |
3063 | CONTRACTL |
3064 | { |
3065 | THROWS; |
3066 | GC_TRIGGERS; |
3067 | } |
3068 | CONTRACTL_END; |
3069 | |
3070 | SigPointer sigOrig = psig; |
3071 | |
3072 | CorElementType eType; |
3073 | IfFailThrow(psig.GetElemType(&eType)); |
3074 | |
3075 | switch (eType) |
3076 | { |
3077 | case ELEMENT_TYPE_INTERNAL: |
3078 | { |
3079 | TypeHandle * pTypeHandle = (TypeHandle *)psig.GetPtr(); |
3080 | |
3081 | void * ptr; |
3082 | IfFailThrow(psig.GetPointer(&ptr)); |
3083 | |
3084 | Module::RestoreTypeHandlePointerRaw(pTypeHandle); |
3085 | } |
3086 | break; |
3087 | |
3088 | case ELEMENT_TYPE_GENERICINST: |
3089 | { |
3090 | RestoreSignatureContainingInternalTypesParseType(psig); |
3091 | |
3092 | // Get generic arg count |
3093 | ULONG nArgs; |
3094 | IfFailThrow(psig.GetData(&nArgs)); |
3095 | |
3096 | for (ULONG i = 0; i < nArgs; i++) |
3097 | { |
3098 | RestoreSignatureContainingInternalTypesParseType(psig); |
3099 | } |
3100 | } |
3101 | break; |
3102 | |
3103 | case ELEMENT_TYPE_BYREF: |
3104 | case ELEMENT_TYPE_PTR: |
3105 | case ELEMENT_TYPE_PINNED: |
3106 | case ELEMENT_TYPE_SZARRAY: |
3107 | // Call recursively |
3108 | RestoreSignatureContainingInternalTypesParseType(psig); |
3109 | break; |
3110 | |
3111 | default: |
3112 | IfFailThrow(sigOrig.SkipExactlyOne()); |
3113 | psig = sigOrig; |
3114 | break; |
3115 | } |
3116 | } |
3117 | |
3118 | //--------------------------------------------------------------------------------------- |
3119 | // |
3120 | // Restores ET_INTERNAL TypeHandles in an IL stub signature. |
3121 | // |
3122 | static |
3123 | void |
3124 | RestoreSignatureContainingInternalTypes( |
3125 | PCCOR_SIGNATURE pSig, |
3126 | DWORD cSig) |
3127 | { |
3128 | CONTRACTL |
3129 | { |
3130 | THROWS; |
3131 | GC_TRIGGERS; |
3132 | } |
3133 | CONTRACTL_END; |
3134 | |
3135 | Volatile<BYTE> * pVolatileSig = (Volatile<BYTE> *)pSig; |
3136 | if (*pVolatileSig & IMAGE_CEE_CS_CALLCONV_NEEDSRESTORE) |
3137 | { |
3138 | EnsureWritablePages(dac_cast<void*>(pSig), cSig); |
3139 | |
3140 | ULONG nArgs; |
3141 | SigPointer psig(pSig, cSig); |
3142 | |
3143 | // Skip calling convention |
3144 | BYTE uCallConv; |
3145 | IfFailThrow(psig.GetByte(&uCallConv)); |
3146 | |
3147 | if ((uCallConv & IMAGE_CEE_CS_CALLCONV_MASK) == IMAGE_CEE_CS_CALLCONV_FIELD) |
3148 | { |
3149 | ThrowHR(META_E_BAD_SIGNATURE); |
3150 | } |
3151 | |
3152 | // Skip type parameter count |
3153 | if (uCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC) |
3154 | { |
3155 | IfFailThrow(psig.GetData(NULL)); |
3156 | } |
3157 | |
3158 | // Get arg count |
3159 | IfFailThrow(psig.GetData(&nArgs)); |
3160 | |
3161 | nArgs++; // be sure to handle the return type |
3162 | |
3163 | for (ULONG i = 0; i < nArgs; i++) |
3164 | { |
3165 | RestoreSignatureContainingInternalTypesParseType(psig); |
3166 | } |
3167 | |
3168 | // clear the needs-restore bit |
3169 | *pVolatileSig &= (BYTE)~IMAGE_CEE_CS_CALLCONV_NEEDSRESTORE; |
3170 | } |
3171 | } // RestoreSignatureContainingInternalTypes |
3172 | |
3173 | void DynamicMethodDesc::Restore() |
3174 | { |
3175 | CONTRACTL |
3176 | { |
3177 | THROWS; |
3178 | GC_TRIGGERS; |
3179 | } |
3180 | CONTRACTL_END; |
3181 | |
3182 | if (IsSignatureNeedsRestore()) |
3183 | { |
3184 | _ASSERTE(IsILStub()); |
3185 | |
3186 | DWORD cSigLen; |
3187 | PCCOR_SIGNATURE pSig = GetStoredMethodSig(&cSigLen); |
3188 | |
3189 | RestoreSignatureContainingInternalTypes(pSig, cSigLen); |
3190 | } |
3191 | } |
3192 | #endif // FEATURE_PREJIT |
3193 | |
3194 | #ifdef FEATURE_NATIVE_IMAGE_GENERATION |
3195 | void DynamicMethodDesc::Fixup(DataImage* image) |
3196 | { |
3197 | STANDARD_VM_CONTRACT; |
3198 | |
3199 | DWORD cSigLen; |
3200 | PCCOR_SIGNATURE pSig = GetStoredMethodSig(&cSigLen); |
3201 | |
3202 | bool needsRestore = FixupSignatureContainingInternalTypes(image, pSig, cSigLen); |
3203 | |
3204 | DynamicMethodDesc* pDynamicImageMD = (DynamicMethodDesc*)image->GetImagePointer(this); |
3205 | pDynamicImageMD->SetSignatureNeedsRestore(needsRestore); |
3206 | } |
3207 | |
3208 | //--------------------------------------------------------------------------------------- |
3209 | // |
3210 | void |
3211 | MethodDesc::Fixup( |
3212 | DataImage * image) |
3213 | { |
3214 | STANDARD_VM_CONTRACT; |
3215 | |
3216 | #ifdef _DEBUG |
3217 | SString s; |
3218 | if (LoggingOn(LF_ZAP, LL_INFO10000)) |
3219 | { |
3220 | TypeString::AppendMethodDebug(s, this); |
3221 | LOG((LF_ZAP, LL_INFO10000, " MethodDesc::Fixup %S (%p)\n" , s.GetUnicode(), this)); |
3222 | } |
3223 | #endif // _DEBUG |
3224 | |
3225 | #ifdef HAVE_GCCOVER |
3226 | image->ZeroPointerField(this, offsetof(MethodDesc, m_GcCover)); |
3227 | #endif // HAVE_GCCOVER |
3228 | |
3229 | #if _DEBUG |
3230 | image->ZeroPointerField(this, offsetof(MethodDesc, m_pszDebugMethodName)); |
3231 | image->FixupPointerField(this, offsetof(MethodDesc, m_pszDebugMethodName)); |
3232 | image->FixupPointerField(this, offsetof(MethodDesc, m_pszDebugClassName)); |
3233 | image->FixupPointerField(this, offsetof(MethodDesc, m_pszDebugMethodSignature)); |
3234 | if (IsTightlyBoundToMethodTable()) |
3235 | { |
3236 | image->FixupPointerField(this, offsetof(MethodDesc, m_pDebugMethodTable)); |
3237 | } |
3238 | else |
3239 | { |
3240 | image->FixupMethodTablePointer(this, &m_pDebugMethodTable); |
3241 | } |
3242 | #endif // _DEBUG |
3243 | |
3244 | MethodDesc *pNewMD = (MethodDesc*) image->GetImagePointer(this); |
3245 | PREFIX_ASSUME(pNewMD != NULL); |
3246 | |
3247 | // Fixup the chunk header as part of the first MethodDesc in the chunk |
3248 | if (pNewMD->m_chunkIndex == 0) |
3249 | { |
3250 | MethodDescChunk * pNewChunk = pNewMD->GetMethodDescChunk(); |
3251 | |
3252 | // For most MethodDescs we can always directly bind to the method table, because |
3253 | // the MT is guaranteed to be in the same image. In other words the MethodDescs and the |
3254 | // MethodTable are guaranteed to be "tightly-bound", i.e. if one is present in |
3255 | // an NGEN image then then other will be, and if one is used at runtime then |
3256 | // the other will be too. In these cases we always want to hardbind the pointer. |
3257 | // |
3258 | // However for generic method instantiations and other funky MDs managed by the InstMethHashTable |
3259 | // the method table might be saved another module. Whether these get "used" at runtime |
3260 | // is a decision taken by the MethodDesc loading code in genmeth.cpp (FindOrCreateAssociatedMethodDesc), |
3261 | // and is independent of the decision of whether the method table gets used. |
3262 | |
3263 | if (IsTightlyBoundToMethodTable()) |
3264 | { |
3265 | image->FixupRelativePointerField(pNewChunk, offsetof(MethodDescChunk, m_methodTable)); |
3266 | } |
3267 | else |
3268 | { |
3269 | image->FixupMethodTablePointer(pNewChunk, &pNewChunk->m_methodTable); |
3270 | } |
3271 | |
3272 | if (!pNewChunk->m_next.IsNull()) |
3273 | { |
3274 | image->FixupRelativePointerField(pNewChunk, offsetof(MethodDescChunk, m_next)); |
3275 | } |
3276 | } |
3277 | |
3278 | if (pNewMD->HasPrecode()) |
3279 | { |
3280 | Precode* pPrecode = GetSavedPrecode(image); |
3281 | |
3282 | // Fixup the precode if we have stored it |
3283 | pPrecode->Fixup(image, this); |
3284 | } |
3285 | |
3286 | if (IsDynamicMethod()) |
3287 | { |
3288 | image->ZeroPointerField(this, offsetof(DynamicMethodDesc, m_pResolver)); |
3289 | image->FixupRelativePointerField(this, offsetof(DynamicMethodDesc, m_pszMethodName)); |
3290 | } |
3291 | |
3292 | if (GetClassification() == mcInstantiated) |
3293 | { |
3294 | InstantiatedMethodDesc* pIMD = AsInstantiatedMethodDesc(); |
3295 | BOOL needsRestore = NeedsRestore(image); |
3296 | |
3297 | if (pIMD->IMD_IsWrapperStubWithInstantiations()) |
3298 | { |
3299 | image->FixupMethodDescPointer(pIMD, &pIMD->m_pWrappedMethodDesc); |
3300 | } |
3301 | else |
3302 | { |
3303 | if (pIMD->IMD_IsSharedByGenericMethodInstantiations()) |
3304 | { |
3305 | pIMD->m_pDictLayout.GetValue()->Fixup(image, TRUE); |
3306 | image->FixupRelativePointerField(this, offsetof(InstantiatedMethodDesc, m_pDictLayout)); |
3307 | } |
3308 | } |
3309 | |
3310 | image->FixupPlainOrRelativePointerField((InstantiatedMethodDesc*) this, &InstantiatedMethodDesc::m_pPerInstInfo); |
3311 | |
3312 | // Generic methods are dealt with specially to avoid encoding the formal method type parameters |
3313 | if (IsTypicalMethodDefinition()) |
3314 | { |
3315 | Instantiation inst = GetMethodInstantiation(); |
3316 | FixupPointer<TypeHandle> * pInst = inst.GetRawArgs(); |
3317 | for (DWORD j = 0; j < inst.GetNumArgs(); j++) |
3318 | { |
3319 | image->FixupTypeHandlePointer(pInst, &pInst[j]); |
3320 | } |
3321 | } |
3322 | else if (GetMethodDictionary()) |
3323 | { |
3324 | LOG((LF_JIT, LL_INFO10000, "GENERICS: Fixup dictionary for MD %s\n" , |
3325 | m_pszDebugMethodName ? m_pszDebugMethodName : "<no-name>" )); |
3326 | BOOL canSaveInstantiation = TRUE; |
3327 | if (IsGenericMethodDefinition() && !IsTypicalMethodDefinition()) |
3328 | { |
3329 | if (GetMethodDictionary()->ComputeNeedsRestore(image, NULL, GetNumGenericMethodArgs())) |
3330 | { |
3331 | _ASSERTE(needsRestore); |
3332 | canSaveInstantiation = FALSE; |
3333 | } |
3334 | else |
3335 | { |
3336 | Instantiation inst = GetMethodInstantiation(); |
3337 | FixupPointer<TypeHandle> * pInst = inst.GetRawArgs(); |
3338 | for (DWORD j = 0; j < inst.GetNumArgs(); j++) |
3339 | { |
3340 | TypeHandle th = pInst[j].GetValue(); |
3341 | if (!th.IsNull()) |
3342 | { |
3343 | if (!(image->CanEagerBindToTypeHandle(th) && image->CanHardBindToZapModule(th.GetLoaderModule()))) |
3344 | { |
3345 | canSaveInstantiation = FALSE; |
3346 | needsRestore = TRUE; |
3347 | break; |
3348 | } |
3349 | } |
3350 | } |
3351 | } |
3352 | } |
3353 | // We can only save the (non-instantiation) slots of |
3354 | // the dictionary if we are compiling against a known and fixed |
3355 | // dictionary layout. That will only be the case if we can hardbind |
3356 | // to the shared method desc (which owns the dictionary layout). |
3357 | // If we are not a wrapper stub then |
3358 | // there won't be any (non-instantiation) slots in the dictionary. |
3359 | BOOL canSaveSlots = |
3360 | pIMD->IMD_IsWrapperStubWithInstantiations() && |
3361 | image->CanEagerBindToMethodDesc(pIMD->IMD_GetWrappedMethodDesc()); |
3362 | |
3363 | GetMethodDictionary()->Fixup(image, |
3364 | canSaveInstantiation, |
3365 | canSaveSlots, |
3366 | GetNumGenericMethodArgs(), |
3367 | GetModule(), |
3368 | GetDictionaryLayout()); |
3369 | } |
3370 | |
3371 | if (needsRestore) |
3372 | { |
3373 | InstantiatedMethodDesc* pNewIMD = (InstantiatedMethodDesc *) image->GetImagePointer(this); |
3374 | if (pNewIMD == NULL) |
3375 | COMPlusThrowHR(E_POINTER); |
3376 | |
3377 | pNewIMD->m_wFlags2 |= InstantiatedMethodDesc::Unrestored; |
3378 | } |
3379 | } |
3380 | |
3381 | if (IsNDirect()) |
3382 | { |
3383 | // |
3384 | // For now, set method desc back into its pristine uninitialized state. |
3385 | // |
3386 | |
3387 | NDirectMethodDesc *pNMD = (NDirectMethodDesc *)this; |
3388 | |
3389 | image->FixupPlainOrRelativePointerField(pNMD, &NDirectMethodDesc::ndirect, &decltype(NDirectMethodDesc::ndirect)::m_pWriteableData); |
3390 | |
3391 | NDirectWriteableData *pWriteableData = pNMD->GetWriteableData(); |
3392 | NDirectImportThunkGlue *pImportThunkGlue = pNMD->GetNDirectImportThunkGlue(); |
3393 | |
3394 | #ifdef HAS_NDIRECT_IMPORT_PRECODE |
3395 | if (!pNMD->MarshalingRequired()) |
3396 | { |
3397 | image->FixupField(pWriteableData, offsetof(NDirectWriteableData, m_pNDirectTarget), |
3398 | pImportThunkGlue, Precode::GetEntryPointOffset()); |
3399 | } |
3400 | else |
3401 | { |
3402 | image->ZeroPointerField(pWriteableData, offsetof(NDirectWriteableData, m_pNDirectTarget)); |
3403 | } |
3404 | #else // HAS_NDIRECT_IMPORT_PRECODE |
3405 | PORTABILITY_WARNING("NDirectImportThunkGlue" ); |
3406 | #endif // HAS_NDIRECT_IMPORT_PRECODE |
3407 | |
3408 | image->ZeroPointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pNativeNDirectTarget)); |
3409 | |
3410 | #ifdef HAS_NDIRECT_IMPORT_PRECODE |
3411 | if (!pNMD->MarshalingRequired()) |
3412 | { |
3413 | // import thunk is only needed if the P/Invoke is inlinable |
3414 | image->FixupRelativePointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pImportThunkGlue)); |
3415 | ((Precode*)pImportThunkGlue)->Fixup(image, this); |
3416 | } |
3417 | else |
3418 | { |
3419 | image->ZeroPointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pImportThunkGlue)); |
3420 | } |
3421 | #else // HAS_NDIRECT_IMPORT_PRECODE |
3422 | PORTABILITY_WARNING("NDirectImportThunkGlue" ); |
3423 | #endif // HAS_NDIRECT_IMPORT_PRECODE |
3424 | |
3425 | if (!IsQCall()) |
3426 | { |
3427 | image->FixupRelativePointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pszLibName)); |
3428 | image->FixupRelativePointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pszEntrypointName)); |
3429 | } |
3430 | |
3431 | if (image->IsStored(pNMD->ndirect.m_pStubMD.GetValueMaybeNull())) |
3432 | image->FixupRelativePointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pStubMD)); |
3433 | else |
3434 | image->ZeroPointerField(this, offsetof(NDirectMethodDesc, ndirect.m_pStubMD)); |
3435 | } |
3436 | |
3437 | if (HasStoredSig()) |
3438 | { |
3439 | image->FixupRelativePointerField(this, offsetof(StoredSigMethodDesc, m_pSig)); |
3440 | |
3441 | // The DynamicMethodDescs used for IL stubs may have a signature that refers to |
3442 | // runtime types using ELEMENT_TYPE_INTERNAL. We need to fixup these types here. |
3443 | if (IsILStub()) |
3444 | { |
3445 | PTR_DynamicMethodDesc pDynamicMD = AsDynamicMethodDesc(); |
3446 | pDynamicMD->Fixup(image); |
3447 | } |
3448 | } |
3449 | |
3450 | #ifdef FEATURE_COMINTEROP |
3451 | if (IsComPlusCall()) |
3452 | { |
3453 | ComPlusCallMethodDesc *pComPlusMD = (ComPlusCallMethodDesc*)this; |
3454 | ComPlusCallInfo *pComInfo = pComPlusMD->m_pComPlusCallInfo; |
3455 | |
3456 | if (image->IsStored(pComInfo)) |
3457 | { |
3458 | image->FixupPointerField(pComPlusMD, offsetof(ComPlusCallMethodDesc, m_pComPlusCallInfo)); |
3459 | pComInfo->Fixup(image); |
3460 | } |
3461 | else |
3462 | { |
3463 | image->ZeroPointerField(pComPlusMD, offsetof(ComPlusCallMethodDesc, m_pComPlusCallInfo)); |
3464 | } |
3465 | } |
3466 | else if (IsGenericComPlusCall()) |
3467 | { |
3468 | ComPlusCallInfo *pComInfo = AsInstantiatedMethodDesc()->IMD_GetComPlusCallInfo(); |
3469 | pComInfo->Fixup(image); |
3470 | } |
3471 | #endif // FEATURE_COMINTEROP |
3472 | |
3473 | SIZE_T currentSize = GetBaseSize(); |
3474 | |
3475 | // |
3476 | // Save all optional members |
3477 | // |
3478 | |
3479 | if (HasNonVtableSlot()) |
3480 | { |
3481 | FixupSlot(image, this, currentSize, IMAGE_REL_BASED_RelativePointer); |
3482 | |
3483 | currentSize += sizeof(NonVtableSlot); |
3484 | } |
3485 | |
3486 | if (IsMethodImpl()) |
3487 | { |
3488 | MethodImpl *pImpl = GetMethodImpl(); |
3489 | |
3490 | pImpl->Fixup(image, this, currentSize); |
3491 | |
3492 | currentSize += sizeof(MethodImpl); |
3493 | } |
3494 | |
3495 | if (pNewMD->HasNativeCodeSlot()) |
3496 | { |
3497 | ZapNode * pCodeNode = image->GetCodeAddress(this); |
3498 | ZapNode * pFixupList = image->GetFixupList(this); |
3499 | |
3500 | if (pCodeNode != NULL) |
3501 | image->FixupFieldToNode(this, currentSize, pCodeNode, (pFixupList != NULL) ? 1 : 0, IMAGE_REL_BASED_RelativePointer); |
3502 | currentSize += sizeof(NativeCodeSlot); |
3503 | |
3504 | if (pFixupList != NULL) |
3505 | { |
3506 | image->FixupFieldToNode(this, currentSize, pFixupList, 0, IMAGE_REL_BASED_RelativePointer); |
3507 | currentSize += sizeof(FixupListSlot); |
3508 | } |
3509 | } |
3510 | } // MethodDesc::Fixup |
3511 | |
3512 | //******************************************************************************* |
3513 | Precode* MethodDesc::GetSavedPrecode(DataImage *image) |
3514 | { |
3515 | STANDARD_VM_CONTRACT; |
3516 | |
3517 | Precode * pPrecode = (Precode *)image->LookupSurrogate(this); |
3518 | _ASSERTE(pPrecode != NULL); |
3519 | _ASSERTE(pPrecode->IsCorrectMethodDesc(this)); |
3520 | |
3521 | return pPrecode; |
3522 | } |
3523 | |
3524 | Precode* MethodDesc::GetSavedPrecodeOrNull(DataImage *image) |
3525 | { |
3526 | STANDARD_VM_CONTRACT; |
3527 | |
3528 | Precode * pPrecode = (Precode *)image->LookupSurrogate(this); |
3529 | if (pPrecode == NULL) |
3530 | { |
3531 | return NULL; |
3532 | } |
3533 | |
3534 | _ASSERTE(pPrecode->IsCorrectMethodDesc(this)); |
3535 | |
3536 | return pPrecode; |
3537 | } |
3538 | |
3539 | //******************************************************************************* |
3540 | void MethodDesc::FixupSlot(DataImage *image, PVOID p, SSIZE_T offset, ZapRelocationType type) |
3541 | { |
3542 | STANDARD_VM_CONTRACT; |
3543 | |
3544 | |
3545 | Precode* pPrecode = GetSavedPrecodeOrNull(image); |
3546 | if (pPrecode != NULL) |
3547 | { |
3548 | // Use the precode if we have decided to store it |
3549 | image->FixupField(p, offset, pPrecode, Precode::GetEntryPointOffset(), type); |
3550 | } |
3551 | else |
3552 | { |
3553 | _ASSERTE(MayHaveNativeCode()); |
3554 | ZapNode *code = image->GetCodeAddress(this); |
3555 | _ASSERTE(code != 0); |
3556 | image->FixupFieldToNode(p, offset, code, Precode::GetEntryPointOffset(), type); |
3557 | } |
3558 | } |
3559 | |
3560 | //******************************************************************************* |
3561 | SIZE_T MethodDesc::SaveChunk::GetSavedMethodDescSize(MethodInfo * pMethodInfo) |
3562 | { |
3563 | LIMITED_METHOD_CONTRACT; |
3564 | MethodDesc * pMD = pMethodInfo->m_pMD; |
3565 | |
3566 | SIZE_T size = pMD->GetBaseSize(); |
3567 | |
3568 | if (pMD->HasNonVtableSlot()) |
3569 | size += sizeof(NonVtableSlot); |
3570 | |
3571 | if (pMD->IsMethodImpl()) |
3572 | size += sizeof(MethodImpl); |
3573 | |
3574 | if (pMethodInfo->m_fHasNativeCodeSlot) |
3575 | { |
3576 | size += sizeof(NativeCodeSlot); |
3577 | |
3578 | if (pMethodInfo->m_fHasFixupList) |
3579 | size += sizeof(FixupListSlot); |
3580 | } |
3581 | |
3582 | #ifdef FEATURE_COMINTEROP |
3583 | if (pMD->IsGenericComPlusCall()) |
3584 | size += sizeof(ComPlusCallInfo); |
3585 | #endif // FEATURE_COMINTEROP |
3586 | |
3587 | _ASSERTE(size % MethodDesc::ALIGNMENT == 0); |
3588 | |
3589 | return size; |
3590 | } |
3591 | |
3592 | //******************************************************************************* |
3593 | void MethodDesc::SaveChunk::SaveOneChunk(COUNT_T start, COUNT_T count, ULONG sizeOfMethodDescs, DWORD priority) |
3594 | { |
3595 | STANDARD_VM_CONTRACT; |
3596 | DataImage::ItemKind kind; |
3597 | |
3598 | switch (priority) |
3599 | { |
3600 | case HotMethodDesc: |
3601 | kind = DataImage::ITEM_METHOD_DESC_HOT; |
3602 | break; |
3603 | case WriteableMethodDesc: |
3604 | kind = DataImage::ITEM_METHOD_DESC_HOT_WRITEABLE; |
3605 | break; |
3606 | case ColdMethodDesc: |
3607 | kind = DataImage::ITEM_METHOD_DESC_COLD; |
3608 | break; |
3609 | case ColdWriteableMethodDesc: |
3610 | kind = DataImage::ITEM_METHOD_DESC_COLD_WRITEABLE; |
3611 | break; |
3612 | default: |
3613 | UNREACHABLE(); |
3614 | } |
3615 | |
3616 | ULONG size = sizeOfMethodDescs + sizeof(MethodDescChunk); |
3617 | ZapStoredStructure * pNode = m_pImage->StoreStructure(NULL, size, kind); |
3618 | |
3619 | BYTE * pData = (BYTE *)m_pImage->GetImagePointer(pNode); |
3620 | |
3621 | MethodDescChunk * pNewChunk = (MethodDescChunk *)pData; |
3622 | |
3623 | // Bind the image space so we can use the regular fixup helpers |
3624 | m_pImage->BindPointer(pNewChunk, pNode, 0); |
3625 | |
3626 | pNewChunk->SetMethodTable(m_methodInfos[start].m_pMD->GetMethodTable()); |
3627 | |
3628 | pNewChunk->SetIsZapped(); |
3629 | pNewChunk->SetTokenRange(GetTokenRange(m_methodInfos[start].m_pMD->GetMemberDef())); |
3630 | |
3631 | pNewChunk->SetSizeAndCount(sizeOfMethodDescs, count); |
3632 | |
3633 | Precode::SaveChunk precodeSaveChunk; // Helper for saving precodes in chunks |
3634 | |
3635 | ULONG offset = sizeof(MethodDescChunk); |
3636 | for (COUNT_T i = 0; i < count; i++) |
3637 | { |
3638 | MethodInfo * pMethodInfo = &(m_methodInfos[start + i]); |
3639 | MethodDesc * pMD = pMethodInfo->m_pMD; |
3640 | |
3641 | m_pImage->BindPointer(pMD, pNode, offset); |
3642 | |
3643 | pMD->Save(m_pImage); |
3644 | |
3645 | MethodDesc * pNewMD = (MethodDesc *)(pData + offset); |
3646 | |
3647 | CopyMemory(pNewMD, pMD, pMD->GetBaseSize()); |
3648 | |
3649 | if (pMD->IsMethodImpl()) |
3650 | CopyMemory(pNewMD->GetMethodImpl(), pMD->GetMethodImpl(), sizeof(MethodImpl)); |
3651 | else |
3652 | pNewMD->m_wFlags &= ~mdcMethodImpl; |
3653 | |
3654 | pNewMD->m_chunkIndex = (BYTE) ((offset - sizeof(MethodDescChunk)) / MethodDesc::ALIGNMENT); |
3655 | _ASSERTE(pNewMD->GetMethodDescChunk() == pNewChunk); |
3656 | |
3657 | pNewMD->m_bFlags2 |= enum_flag2_HasStableEntryPoint; |
3658 | if (pMethodInfo->m_fHasPrecode) |
3659 | { |
3660 | precodeSaveChunk.Save(m_pImage, pMD); |
3661 | pNewMD->m_bFlags2 |= enum_flag2_HasPrecode; |
3662 | } |
3663 | else |
3664 | { |
3665 | pNewMD->m_bFlags2 &= ~enum_flag2_HasPrecode; |
3666 | } |
3667 | |
3668 | if (pMethodInfo->m_fHasNativeCodeSlot) |
3669 | { |
3670 | pNewMD->m_bFlags2 |= enum_flag2_HasNativeCodeSlot; |
3671 | } |
3672 | else |
3673 | { |
3674 | pNewMD->m_bFlags2 &= ~enum_flag2_HasNativeCodeSlot; |
3675 | } |
3676 | |
3677 | #ifdef FEATURE_COMINTEROP |
3678 | if (pMD->IsGenericComPlusCall()) |
3679 | { |
3680 | ComPlusCallInfo *pComInfo = pMD->AsInstantiatedMethodDesc()->IMD_GetComPlusCallInfo(); |
3681 | |
3682 | CopyMemory(pNewMD->AsInstantiatedMethodDesc()->IMD_GetComPlusCallInfo(), pComInfo, sizeof(ComPlusCallInfo)); |
3683 | |
3684 | m_pImage->BindPointer(pComInfo, pNode, offset + ((BYTE *)pComInfo - (BYTE *)pMD)); |
3685 | } |
3686 | #endif // FEATURE_COMINTEROP |
3687 | |
3688 | pNewMD->PrecomputeNameHash(); |
3689 | |
3690 | offset += GetSavedMethodDescSize(pMethodInfo); |
3691 | } |
3692 | _ASSERTE(offset == sizeOfMethodDescs + sizeof(MethodDescChunk)); |
3693 | |
3694 | precodeSaveChunk.Flush(m_pImage); |
3695 | |
3696 | if (m_methodInfos[start].m_pMD->IsTightlyBoundToMethodTable()) |
3697 | { |
3698 | if (m_pLastChunk != NULL) |
3699 | { |
3700 | m_pLastChunk->m_next.SetValue(pNewChunk); |
3701 | } |
3702 | else |
3703 | { |
3704 | _ASSERTE(m_pFirstNode == NULL); |
3705 | m_pFirstNode = pNode; |
3706 | } |
3707 | m_pLastChunk = pNewChunk; |
3708 | } |
3709 | } |
3710 | |
3711 | //******************************************************************************* |
3712 | void MethodDesc::SaveChunk::Append(MethodDesc * pMD) |
3713 | { |
3714 | STANDARD_VM_CONTRACT; |
3715 | #ifdef _DEBUG |
3716 | if (!m_methodInfos.IsEmpty()) |
3717 | { |
3718 | // Verify that all MethodDescs in the chunk are alike |
3719 | MethodDesc * pFirstMD = m_methodInfos[0].m_pMD; |
3720 | |
3721 | _ASSERTE(pFirstMD->GetMethodTable() == pMD->GetMethodTable()); |
3722 | _ASSERTE(pFirstMD->IsTightlyBoundToMethodTable() == pMD->IsTightlyBoundToMethodTable()); |
3723 | } |
3724 | _ASSERTE(!m_pImage->IsStored(pMD)); |
3725 | #endif |
3726 | |
3727 | MethodInfo method; |
3728 | method.m_pMD = pMD; |
3729 | |
3730 | BYTE priority = HotMethodDesc; |
3731 | |
3732 | // We only write into mcInstantiated methoddescs to mark them as restored |
3733 | if (pMD->NeedsRestore(m_pImage, TRUE) && pMD->GetClassification() == mcInstantiated) |
3734 | priority |= WriteableMethodDesc; // writeable |
3735 | |
3736 | // |
3737 | // Determines whether the method desc should be considered hot, based |
3738 | // on a bitmap that contains entries for hot method descs. At this |
3739 | // point the only cold method descs are those not in the bitmap. |
3740 | // |
3741 | if ((m_pImage->GetMethodProfilingFlags(pMD) & (1 << ReadMethodDesc)) == 0) |
3742 | priority |= ColdMethodDesc; // cold |
3743 | |
3744 | // We can have more priorities here in the future to scale well |
3745 | // for many IBC training scenarios. |
3746 | |
3747 | method.m_priority = priority; |
3748 | |
3749 | // Save the precode if we have no directly callable code |
3750 | method.m_fHasPrecode = !m_pImage->CanDirectCall(pMD); |
3751 | |
3752 | // Determine optional slots that are going to be saved |
3753 | if (method.m_fHasPrecode) |
3754 | { |
3755 | method.m_fHasNativeCodeSlot = pMD->MayHaveNativeCode(); |
3756 | |
3757 | if (method.m_fHasNativeCodeSlot) |
3758 | { |
3759 | method.m_fHasFixupList = (m_pImage->GetFixupList(pMD) != NULL); |
3760 | } |
3761 | else |
3762 | { |
3763 | _ASSERTE(m_pImage->GetFixupList(pMD) == NULL); |
3764 | method.m_fHasFixupList = FALSE; |
3765 | } |
3766 | } |
3767 | else |
3768 | { |
3769 | method.m_fHasNativeCodeSlot = FALSE; |
3770 | |
3771 | _ASSERTE(m_pImage->GetFixupList(pMD) == NULL); |
3772 | method.m_fHasFixupList = FALSE; |
3773 | } |
3774 | |
3775 | m_methodInfos.Append(method); |
3776 | } |
3777 | |
3778 | //******************************************************************************* |
3779 | int __cdecl MethodDesc::SaveChunk::MethodInfoCmp(const void* a_, const void* b_) |
3780 | { |
3781 | LIMITED_METHOD_CONTRACT; |
3782 | // Sort by priority as primary key and token as secondary key |
3783 | MethodInfo * a = (MethodInfo *)a_; |
3784 | MethodInfo * b = (MethodInfo *)b_; |
3785 | |
3786 | int priorityDiff = (int)(a->m_priority - b->m_priority); |
3787 | if (priorityDiff != 0) |
3788 | return priorityDiff; |
3789 | |
3790 | int tokenDiff = (int)(a->m_pMD->GetMemberDef_NoLogging() - b->m_pMD->GetMemberDef_NoLogging()); |
3791 | if (tokenDiff != 0) |
3792 | return tokenDiff; |
3793 | |
3794 | // Place unboxing stubs first, code:MethodDesc::FindOrCreateAssociatedMethodDesc depends on this invariant |
3795 | int unboxingDiff = (int)(b->m_pMD->IsUnboxingStub() - a->m_pMD->IsUnboxingStub()); |
3796 | return unboxingDiff; |
3797 | } |
3798 | |
3799 | //******************************************************************************* |
3800 | ZapStoredStructure * MethodDesc::SaveChunk::Save() |
3801 | { |
3802 | // Sort by priority as primary key and token as secondary key |
3803 | qsort (&m_methodInfos[0], // start of array |
3804 | m_methodInfos.GetCount(), // array size in elements |
3805 | sizeof(MethodInfo), // element size in bytes |
3806 | MethodInfoCmp); // comparer function |
3807 | |
3808 | DWORD currentPriority = NoFlags; |
3809 | int currentTokenRange = -1; |
3810 | int nextStart = 0; |
3811 | SIZE_T sizeOfMethodDescs = 0; |
3812 | |
3813 | // |
3814 | // Go over all MethodDescs and create smallest number of chunks possible |
3815 | // |
3816 | |
3817 | for (COUNT_T i = 0; i < m_methodInfos.GetCount(); i++) |
3818 | { |
3819 | MethodInfo * pMethodInfo = &(m_methodInfos[i]); |
3820 | MethodDesc * pMD = pMethodInfo->m_pMD; |
3821 | |
3822 | DWORD priority = pMethodInfo->m_priority; |
3823 | int tokenRange = GetTokenRange(pMD->GetMemberDef()); |
3824 | |
3825 | SIZE_T size = GetSavedMethodDescSize(pMethodInfo); |
3826 | |
3827 | // Bundle that has to be in same chunk |
3828 | SIZE_T bundleSize = size; |
3829 | |
3830 | if (pMD->IsUnboxingStub() && pMD->IsTightlyBoundToMethodTable()) |
3831 | { |
3832 | // Wrapped method desc has to immediately follow unboxing stub, and both has to be in one chunk |
3833 | _ASSERTE(m_methodInfos[i+1].m_pMD->GetMemberDef() == m_methodInfos[i].m_pMD->GetMemberDef()); |
3834 | |
3835 | // Make sure that both wrapped method desc and unboxing stub will fit into same chunk |
3836 | bundleSize += GetSavedMethodDescSize(&m_methodInfos[i+1]); |
3837 | } |
3838 | |
3839 | if (priority != currentPriority || |
3840 | tokenRange != currentTokenRange || |
3841 | sizeOfMethodDescs + bundleSize > MethodDescChunk::MaxSizeOfMethodDescs) |
3842 | { |
3843 | if (sizeOfMethodDescs != 0) |
3844 | { |
3845 | SaveOneChunk(nextStart, i - nextStart, sizeOfMethodDescs, currentPriority); |
3846 | nextStart = i; |
3847 | } |
3848 | |
3849 | currentPriority = priority; |
3850 | currentTokenRange = tokenRange; |
3851 | sizeOfMethodDescs = 0; |
3852 | } |
3853 | |
3854 | sizeOfMethodDescs += size; |
3855 | } |
3856 | |
3857 | if (sizeOfMethodDescs != 0) |
3858 | SaveOneChunk(nextStart, m_methodInfos.GetCount() - nextStart, sizeOfMethodDescs, currentPriority); |
3859 | |
3860 | return m_pFirstNode; |
3861 | } |
3862 | |
3863 | #ifdef FEATURE_COMINTEROP |
3864 | BOOL ComPlusCallInfo::ShouldSave(DataImage *image) |
3865 | { |
3866 | STANDARD_VM_CONTRACT; |
3867 | |
3868 | MethodDesc * pStubMD = m_pStubMD.GetValueMaybeNull(); |
3869 | |
3870 | // Note that pStubMD can be regular IL methods desc for stubs implemented by IL |
3871 | return (pStubMD != NULL) && image->CanEagerBindToMethodDesc(pStubMD) && image->CanHardBindToZapModule(pStubMD->GetLoaderModule()); |
3872 | } |
3873 | |
3874 | void ComPlusCallInfo::Fixup(DataImage *image) |
3875 | { |
3876 | STANDARD_VM_CONTRACT; |
3877 | |
3878 | // It is not worth the complexity to do full pre-initialization for WinRT delegates |
3879 | if (m_pInterfaceMT != NULL && m_pInterfaceMT->IsDelegate()) |
3880 | { |
3881 | if (!m_pStubMD.IsNull()) |
3882 | { |
3883 | image->FixupRelativePointerField(this, offsetof(ComPlusCallInfo, m_pStubMD)); |
3884 | } |
3885 | else |
3886 | { |
3887 | image->ZeroPointerField(this, offsetof(ComPlusCallInfo, m_pStubMD)); |
3888 | } |
3889 | |
3890 | image->ZeroPointerField(this, offsetof(ComPlusCallInfo, m_pInterfaceMT)); |
3891 | image->ZeroPointerField(this, offsetof(ComPlusCallInfo, m_pILStub)); |
3892 | return; |
3893 | } |
3894 | |
3895 | if (m_pInterfaceMT != NULL) |
3896 | { |
3897 | if (image->CanEagerBindToTypeHandle(m_pInterfaceMT) && image->CanHardBindToZapModule(m_pInterfaceMT->GetLoaderModule())) |
3898 | { |
3899 | image->FixupPointerField(this, offsetof(ComPlusCallInfo, m_pInterfaceMT)); |
3900 | } |
3901 | else |
3902 | { |
3903 | image->ZeroPointerField(this, offsetof(ComPlusCallInfo, m_pInterfaceMT)); |
3904 | } |
3905 | } |
3906 | |
3907 | if (!m_pStubMD.IsNull()) |
3908 | { |
3909 | image->FixupRelativePointerField(this, offsetof(ComPlusCallInfo, m_pStubMD)); |
3910 | |
3911 | MethodDesc * pStubMD = m_pStubMD.GetValue(); |
3912 | ZapNode * pCode = pStubMD->IsDynamicMethod() ? image->GetCodeAddress(pStubMD) : NULL; |
3913 | if (pCode != NULL) |
3914 | { |
3915 | image->FixupFieldToNode(this, offsetof(ComPlusCallInfo, m_pILStub), pCode ARM_ARG(THUMB_CODE)); |
3916 | } |
3917 | else |
3918 | { |
3919 | image->ZeroPointerField(this, offsetof(ComPlusCallInfo, m_pILStub)); |
3920 | } |
3921 | } |
3922 | else |
3923 | { |
3924 | image->ZeroPointerField(this, offsetof(ComPlusCallInfo, m_pStubMD)); |
3925 | |
3926 | image->ZeroPointerField(this, offsetof(ComPlusCallInfo, m_pILStub)); |
3927 | } |
3928 | } |
3929 | #endif // FEATURE_COMINTEROP |
3930 | |
3931 | #endif // FEATURE_NATIVE_IMAGE_GENERATION |
3932 | |
3933 | #endif // !DACCESS_COMPILE |
3934 | |
3935 | #ifdef FEATURE_PREJIT |
3936 | //******************************************************************************* |
3937 | void MethodDesc::CheckRestore(ClassLoadLevel level) |
3938 | { |
3939 | STATIC_CONTRACT_THROWS; |
3940 | STATIC_CONTRACT_GC_TRIGGERS; |
3941 | STATIC_CONTRACT_FAULT; |
3942 | |
3943 | if (!IsRestored() || !GetMethodTable()->IsFullyLoaded()) |
3944 | { |
3945 | g_IBCLogger.LogMethodDescAccess(this); |
3946 | |
3947 | if (GetClassification() == mcInstantiated) |
3948 | { |
3949 | #ifndef DACCESS_COMPILE |
3950 | InstantiatedMethodDesc *pIMD = AsInstantiatedMethodDesc(); |
3951 | EnsureWritablePages(pIMD); |
3952 | |
3953 | // First restore method table pointer in singleton chunk; |
3954 | // it might be out-of-module |
3955 | GetMethodDescChunk()->RestoreMTPointer(level); |
3956 | #ifdef _DEBUG |
3957 | Module::RestoreMethodTablePointer(&m_pDebugMethodTable, NULL, level); |
3958 | #endif |
3959 | |
3960 | // Now restore wrapped method desc if present; we need this for the dictionary layout too |
3961 | if (pIMD->IMD_IsWrapperStubWithInstantiations()) |
3962 | Module::RestoreMethodDescPointer(&pIMD->m_pWrappedMethodDesc); |
3963 | |
3964 | // Finally restore the dictionary itself (including instantiation) |
3965 | if (GetMethodDictionary()) |
3966 | { |
3967 | GetMethodDictionary()->Restore(GetNumGenericMethodArgs(), level); |
3968 | } |
3969 | |
3970 | g_IBCLogger.LogMethodDescWriteAccess(this); |
3971 | |
3972 | // If this function had already been requested for rejit, then give the rejit |
3973 | // manager a chance to jump-stamp the code we are restoring. This ensures the |
3974 | // first thread entering the function will jump to the prestub and trigger the |
3975 | // rejit. Note that the PublishMethodHolder may take a lock to avoid a rejit race. |
3976 | // See code:ReJitManager::PublishMethodHolder::PublishMethodHolder#PublishCode |
3977 | // for details on the race. |
3978 | // |
3979 | { |
3980 | PublishMethodHolder publishWorker(this, GetNativeCode()); |
3981 | pIMD->m_wFlags2 = pIMD->m_wFlags2 & ~InstantiatedMethodDesc::Unrestored; |
3982 | } |
3983 | |
3984 | if (ETW_PROVIDER_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER)) |
3985 | { |
3986 | ETW::MethodLog::MethodRestored(this); |
3987 | } |
3988 | |
3989 | #else // DACCESS_COMPILE |
3990 | DacNotImpl(); |
3991 | #endif // DACCESS_COMPILE |
3992 | } |
3993 | else if (IsILStub()) // the only stored-sig MD type that uses ET_INTERNAL |
3994 | { |
3995 | ClassLoader::EnsureLoaded(TypeHandle(GetMethodTable()), level); |
3996 | |
3997 | #ifndef DACCESS_COMPILE |
3998 | PTR_DynamicMethodDesc pDynamicMD = AsDynamicMethodDesc(); |
3999 | pDynamicMD->Restore(); |
4000 | |
4001 | if (ETW_PROVIDER_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER)) |
4002 | { |
4003 | ETW::MethodLog::MethodRestored(this); |
4004 | } |
4005 | #else // DACCESS_COMPILE |
4006 | DacNotImpl(); |
4007 | #endif // DACCESS_COMPILE |
4008 | } |
4009 | else |
4010 | { |
4011 | ClassLoader::EnsureLoaded(TypeHandle(GetMethodTable()), level); |
4012 | } |
4013 | } |
4014 | } |
4015 | #else // FEATURE_PREJIT |
4016 | //******************************************************************************* |
4017 | void MethodDesc::CheckRestore(ClassLoadLevel level) |
4018 | { |
4019 | LIMITED_METHOD_CONTRACT; |
4020 | } |
4021 | #endif // !FEATURE_PREJIT |
4022 | |
4023 | // static |
4024 | MethodDesc* MethodDesc::GetMethodDescFromStubAddr(PCODE addr, BOOL fSpeculative /*=FALSE*/) |
4025 | { |
4026 | CONTRACT(MethodDesc *) |
4027 | { |
4028 | GC_NOTRIGGER; |
4029 | NOTHROW; |
4030 | SO_TOLERANT; |
4031 | } |
4032 | CONTRACT_END; |
4033 | |
4034 | MethodDesc * pMD = NULL; |
4035 | |
4036 | #ifdef HAS_COMPACT_ENTRYPOINTS |
4037 | if (MethodDescChunk::IsCompactEntryPointAtAddress(addr)) |
4038 | { |
4039 | pMD = MethodDescChunk::GetMethodDescFromCompactEntryPoint(addr, fSpeculative); |
4040 | RETURN(pMD); |
4041 | } |
4042 | #endif // HAS_COMPACT_ENTRYPOINTS |
4043 | |
4044 | // Otherwise this must be some kind of precode |
4045 | // |
4046 | Precode* pPrecode = Precode::GetPrecodeFromEntryPoint(addr, fSpeculative); |
4047 | PREFIX_ASSUME(fSpeculative || (pPrecode != NULL)); |
4048 | if (pPrecode != NULL) |
4049 | { |
4050 | pMD = pPrecode->GetMethodDesc(fSpeculative); |
4051 | RETURN(pMD); |
4052 | } |
4053 | |
4054 | RETURN(NULL); // Not found |
4055 | } |
4056 | |
4057 | #ifdef FEATURE_PREJIT |
4058 | //******************************************************************************* |
4059 | TADDR MethodDesc::GetFixupList() |
4060 | { |
4061 | LIMITED_METHOD_CONTRACT; |
4062 | |
4063 | if (HasNativeCodeSlot()) |
4064 | { |
4065 | TADDR pSlot = GetAddrOfNativeCodeSlot(); |
4066 | if (*dac_cast<PTR_TADDR>(pSlot) & FIXUP_LIST_MASK) |
4067 | return FixupListSlot::GetValueAtPtr(pSlot + sizeof(NativeCodeSlot)); |
4068 | } |
4069 | |
4070 | return NULL; |
4071 | } |
4072 | |
4073 | //******************************************************************************* |
4074 | BOOL MethodDesc::IsRestored_NoLogging() |
4075 | { |
4076 | STATIC_CONTRACT_SO_TOLERANT; |
4077 | STATIC_CONTRACT_NOTHROW; |
4078 | STATIC_CONTRACT_GC_NOTRIGGER; |
4079 | STATIC_CONTRACT_FORBID_FAULT; |
4080 | STATIC_CONTRACT_SUPPORTS_DAC; |
4081 | |
4082 | DPTR(RelativeFixupPointer<PTR_MethodTable>) ppMT = GetMethodTablePtr(); |
4083 | |
4084 | if (ppMT->IsTagged(dac_cast<TADDR>(ppMT))) |
4085 | return FALSE; |
4086 | |
4087 | if (!ppMT->GetValue(dac_cast<TADDR>(ppMT))->IsRestored_NoLogging()) |
4088 | return FALSE; |
4089 | |
4090 | if (GetClassification() == mcInstantiated) |
4091 | { |
4092 | InstantiatedMethodDesc *pIMD = AsInstantiatedMethodDesc(); |
4093 | return (pIMD->m_wFlags2 & InstantiatedMethodDesc::Unrestored) == 0; |
4094 | } |
4095 | |
4096 | if (IsILStub()) // the only stored-sig MD type that uses ET_INTERNAL |
4097 | { |
4098 | PTR_DynamicMethodDesc pDynamicMD = AsDynamicMethodDesc(); |
4099 | return pDynamicMD->IsRestored(); |
4100 | } |
4101 | |
4102 | return TRUE; |
4103 | } |
4104 | |
4105 | BOOL MethodDesc::IsRestored() |
4106 | { |
4107 | STATIC_CONTRACT_SO_TOLERANT; |
4108 | STATIC_CONTRACT_NOTHROW; |
4109 | STATIC_CONTRACT_GC_NOTRIGGER; |
4110 | STATIC_CONTRACT_FORBID_FAULT; |
4111 | STATIC_CONTRACT_SUPPORTS_DAC; |
4112 | |
4113 | #ifdef DACCESS_COMPILE |
4114 | |
4115 | return IsRestored_NoLogging(); |
4116 | |
4117 | #else // not DACCESS_COMPILE |
4118 | |
4119 | DPTR(RelativeFixupPointer<PTR_MethodTable>) ppMT = GetMethodTablePtr(); |
4120 | |
4121 | if (ppMT->IsTagged(dac_cast<TADDR>(ppMT))) |
4122 | return FALSE; |
4123 | |
4124 | if (!ppMT->GetValue(dac_cast<TADDR>(ppMT))->IsRestored()) |
4125 | return FALSE; |
4126 | |
4127 | if (GetClassification() == mcInstantiated) |
4128 | { |
4129 | InstantiatedMethodDesc *pIMD = AsInstantiatedMethodDesc(); |
4130 | return (pIMD->m_wFlags2 & InstantiatedMethodDesc::Unrestored) == 0; |
4131 | } |
4132 | |
4133 | if (IsILStub()) // the only stored-sig MD type that uses ET_INTERNAL |
4134 | { |
4135 | PTR_DynamicMethodDesc pDynamicMD = AsDynamicMethodDesc(); |
4136 | return pDynamicMD->IsRestored(); |
4137 | } |
4138 | |
4139 | return TRUE; |
4140 | |
4141 | #endif // DACCESS_COMPILE |
4142 | |
4143 | } |
4144 | |
4145 | #else // !FEATURE_PREJIT |
4146 | //******************************************************************************* |
4147 | BOOL MethodDesc::IsRestored_NoLogging() |
4148 | { |
4149 | LIMITED_METHOD_CONTRACT; |
4150 | return TRUE; |
4151 | } |
4152 | //******************************************************************************* |
4153 | BOOL MethodDesc::IsRestored() |
4154 | { |
4155 | LIMITED_METHOD_CONTRACT; |
4156 | SUPPORTS_DAC; |
4157 | return TRUE; |
4158 | } |
4159 | #endif // !FEATURE_PREJIT |
4160 | |
4161 | #ifdef HAS_COMPACT_ENTRYPOINTS |
4162 | |
4163 | #if defined(_TARGET_X86_) |
4164 | |
4165 | #include <pshpack1.h> |
4166 | static const struct CentralJumpCode { |
4167 | BYTE m_movzxEAX[3]; |
4168 | BYTE m_shlEAX[3]; |
4169 | BYTE m_addEAX[1]; |
4170 | MethodDesc* m_pBaseMD; |
4171 | BYTE m_jmp[1]; |
4172 | INT32 m_rel32; |
4173 | |
4174 | inline void Setup(MethodDesc* pMD, PCODE target, LoaderAllocator *pLoaderAllocator) { |
4175 | WRAPPER_NO_CONTRACT; |
4176 | m_pBaseMD = pMD; |
4177 | m_rel32 = rel32UsingJumpStub(&m_rel32, target, pMD, pLoaderAllocator); |
4178 | } |
4179 | |
4180 | inline BOOL CheckTarget(TADDR target) { |
4181 | LIMITED_METHOD_CONTRACT; |
4182 | TADDR addr = rel32Decode(PTR_HOST_MEMBER_TADDR(CentralJumpCode, this, m_rel32)); |
4183 | return (addr == target); |
4184 | } |
4185 | } |
4186 | c_CentralJumpCode = { |
4187 | { 0x0F, 0xB6, 0xC0 }, // movzx eax,al |
4188 | { 0xC1, 0xE0, MethodDesc::ALIGNMENT_SHIFT }, // shl eax, MethodDesc::ALIGNMENT_SHIFT |
4189 | { 0x05 }, NULL, // add eax, pBaseMD |
4190 | { 0xE9 }, 0 // jmp PreStub |
4191 | }; |
4192 | #include <poppack.h> |
4193 | |
4194 | #elif defined(_TARGET_AMD64_) |
4195 | |
4196 | #include <pshpack1.h> |
4197 | static const struct CentralJumpCode { |
4198 | BYTE m_movzxRAX[4]; |
4199 | BYTE m_shlEAX[4]; |
4200 | BYTE m_movRAX[2]; |
4201 | MethodDesc* m_pBaseMD; |
4202 | BYTE m_addR10RAX[3]; |
4203 | BYTE m_jmp[1]; |
4204 | INT32 m_rel32; |
4205 | |
4206 | inline void Setup(MethodDesc* pMD, PCODE target, LoaderAllocator *pLoaderAllocator) { |
4207 | WRAPPER_NO_CONTRACT; |
4208 | m_pBaseMD = pMD; |
4209 | m_rel32 = rel32UsingJumpStub(&m_rel32, target, pMD, pLoaderAllocator); |
4210 | } |
4211 | |
4212 | inline BOOL CheckTarget(TADDR target) { |
4213 | WRAPPER_NO_CONTRACT; |
4214 | TADDR addr = rel32Decode(PTR_HOST_MEMBER_TADDR(CentralJumpCode, this, m_rel32)); |
4215 | if (*PTR_BYTE(addr) == 0x48 && |
4216 | *PTR_BYTE(addr+1) == 0xB8 && |
4217 | *PTR_BYTE(addr+10) == 0xFF && |
4218 | *PTR_BYTE(addr+11) == 0xE0) |
4219 | { |
4220 | addr = *PTR_TADDR(addr+2); |
4221 | } |
4222 | return (addr == target); |
4223 | } |
4224 | } |
4225 | c_CentralJumpCode = { |
4226 | { 0x48, 0x0F, 0xB6, 0xC0 }, // movzx rax,al |
4227 | { 0x48, 0xC1, 0xE0, MethodDesc::ALIGNMENT_SHIFT }, // shl rax, MethodDesc::ALIGNMENT_SHIFT |
4228 | { 0x49, 0xBA }, NULL, // mov r10, pBaseMD |
4229 | { 0x4C, 0x03, 0xD0 }, // add r10,rax |
4230 | { 0xE9 }, 0 // jmp PreStub |
4231 | }; |
4232 | #include <poppack.h> |
4233 | |
4234 | #elif defined(_TARGET_ARM_) |
4235 | |
4236 | #include <pshpack1.h> |
4237 | struct CentralJumpCode { |
4238 | BYTE m_ldrPC[4]; |
4239 | BYTE m_short[2]; |
4240 | MethodDescChunk *m_pChunk; |
4241 | PCODE m_target; |
4242 | |
4243 | inline void Setup(PCODE target, MethodDescChunk *pChunk) { |
4244 | WRAPPER_NO_CONTRACT; |
4245 | |
4246 | m_target = target; |
4247 | m_pChunk = pChunk; |
4248 | } |
4249 | |
4250 | inline BOOL CheckTarget(TADDR target) { |
4251 | WRAPPER_NO_CONTRACT; |
4252 | return ((TADDR)m_target == target); |
4253 | } |
4254 | } |
4255 | c_CentralJumpCode = { |
4256 | { 0xDF, 0xF8, 0x08, 0xF0 }, // ldr pc, =pTarget |
4257 | { 0x00, 0x00 }, // short offset for alignment |
4258 | 0, // pChunk |
4259 | 0 // pTarget |
4260 | }; |
4261 | #include <poppack.h> |
4262 | |
4263 | #else |
4264 | #error Unsupported platform |
4265 | #endif |
4266 | |
4267 | typedef DPTR(struct CentralJumpCode) PTR_CentralJumpCode; |
4268 | #define TEP_CENTRAL_JUMP_SIZE sizeof(c_CentralJumpCode) |
4269 | static_assert_no_msg((TEP_CENTRAL_JUMP_SIZE & 1) == 0); |
4270 | |
4271 | #define TEP_ENTRY_SIZE 4 |
4272 | |
4273 | #ifdef _TARGET_ARM_ |
4274 | |
4275 | #define TEP_HALF_ENTRY_SIZE (TEP_ENTRY_SIZE / 2) |
4276 | |
4277 | // Compact entry point on arm consists of two thumb instructions: |
4278 | // mov r12, pc |
4279 | // b CentralJumpCode |
4280 | |
4281 | // First instruction 0x46fc |
4282 | #define TEP_ENTRY_INSTR1_BYTE1 0xFC |
4283 | #define TEP_ENTRY_INSTR1_BYTE2 0x46 |
4284 | |
4285 | // Mask for unconditional branch opcode |
4286 | #define TEP_ENTRY_INSTR2_MASK1 0xE0 |
4287 | |
4288 | // Mask for opcode |
4289 | #define TEP_ENTRY_INSTR2_MASK2 0xF8 |
4290 | |
4291 | // Bit used for ARM to identify compact entry points |
4292 | #define COMPACT_ENTRY_ARM_CODE 0x2 |
4293 | |
4294 | /* static */ int MethodDescChunk::GetCompactEntryPointMaxCount () |
4295 | { |
4296 | LIMITED_METHOD_DAC_CONTRACT; |
4297 | |
4298 | return MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB / TEP_ENTRY_SIZE; |
4299 | } |
4300 | |
4301 | // Get offset from the start of current compact entry point to the CentralJumpCode |
4302 | static uint16_t DecodeOffsetFromBranchToCentralJump (uint16_t instr) |
4303 | { |
4304 | int16_t offset = decodeUnconditionalBranchThumb ((LPBYTE) &instr); |
4305 | |
4306 | offset += PC_REG_RELATIVE_OFFSET + TEP_HALF_ENTRY_SIZE; |
4307 | |
4308 | _ASSERTE (offset >= TEP_ENTRY_SIZE && (offset % TEP_ENTRY_SIZE == 0)); |
4309 | |
4310 | return (uint16_t) offset; |
4311 | } |
4312 | |
4313 | #ifndef DACCESS_COMPILE |
4314 | |
4315 | // Encode branch instruction to central jump for current compact entry point |
4316 | static uint16_t EncodeBranchToCentralJump (int16_t offset) |
4317 | { |
4318 | _ASSERTE (offset >= 0 && (offset % TEP_ENTRY_SIZE == 0)); |
4319 | |
4320 | offset += TEP_HALF_ENTRY_SIZE - PC_REG_RELATIVE_OFFSET; |
4321 | |
4322 | uint16_t instr; |
4323 | emitUnconditionalBranchThumb ((LPBYTE) &instr, offset); |
4324 | |
4325 | return instr; |
4326 | } |
4327 | |
4328 | #endif // DACCESS_COMPILE |
4329 | |
4330 | #else // _TARGET_ARM_ |
4331 | |
4332 | #define TEP_MAX_BEFORE_INDEX (1 + (127 / TEP_ENTRY_SIZE)) |
4333 | #define TEP_MAX_BLOCK_INDEX (TEP_MAX_BEFORE_INDEX + (128 - TEP_CENTRAL_JUMP_SIZE) / TEP_ENTRY_SIZE) |
4334 | #define TEP_FULL_BLOCK_SIZE (TEP_MAX_BLOCK_INDEX * TEP_ENTRY_SIZE + TEP_CENTRAL_JUMP_SIZE) |
4335 | |
4336 | #endif // _TARGET_ARM_ |
4337 | |
4338 | BOOL MethodDescChunk::IsCompactEntryPointAtAddress(PCODE addr) |
4339 | { |
4340 | LIMITED_METHOD_DAC_CONTRACT; |
4341 | |
4342 | #if defined(_TARGET_X86_) || defined(_TARGET_AMD64_) |
4343 | |
4344 | // Compact entrypoints start at odd addresses |
4345 | return (addr & 1) != 0; |
4346 | |
4347 | #elif defined(_TARGET_ARM_) |
4348 | |
4349 | // Compact entrypoints start at odd addresses (thumb) with second bit set to 1 |
4350 | uint8_t compactEntryPointMask = THUMB_CODE | COMPACT_ENTRY_ARM_CODE; |
4351 | return (addr & compactEntryPointMask) == compactEntryPointMask; |
4352 | |
4353 | #else |
4354 | #error Unsupported platform |
4355 | #endif |
4356 | } |
4357 | |
4358 | //******************************************************************************* |
4359 | /* static */ MethodDesc* MethodDescChunk::GetMethodDescFromCompactEntryPoint(PCODE addr, BOOL fSpeculative /*=FALSE*/) |
4360 | { |
4361 | LIMITED_METHOD_CONTRACT; |
4362 | |
4363 | #ifdef DACCESS_COMPILE |
4364 | // Always use speculative checks with DAC |
4365 | fSpeculative = TRUE; |
4366 | #endif |
4367 | |
4368 | // Always do consistency check in debug |
4369 | if (fSpeculative INDEBUG(|| TRUE)) |
4370 | { |
4371 | #ifdef _TARGET_ARM_ |
4372 | TADDR instrCodeAddr = PCODEToPINSTR(addr); |
4373 | if (!IsCompactEntryPointAtAddress(addr) || |
4374 | *PTR_BYTE(instrCodeAddr) != TEP_ENTRY_INSTR1_BYTE1 || |
4375 | *PTR_BYTE(instrCodeAddr+1) != TEP_ENTRY_INSTR1_BYTE2) |
4376 | #else // _TARGET_ARM_ |
4377 | if ((addr & 3) != 1 || |
4378 | *PTR_BYTE(addr) != X86_INSTR_MOV_AL || |
4379 | *PTR_BYTE(addr+2) != X86_INSTR_JMP_REL8) |
4380 | #endif // _TARGET_ARM_ |
4381 | { |
4382 | if (fSpeculative) return NULL; |
4383 | _ASSERTE(!"Unexpected code in temporary entrypoint" ); |
4384 | } |
4385 | } |
4386 | |
4387 | #ifdef _TARGET_ARM_ |
4388 | |
4389 | // On ARM compact entry points are thumb |
4390 | _ASSERTE ((addr & THUMB_CODE) != 0); |
4391 | addr = addr - THUMB_CODE; |
4392 | |
4393 | // Get offset for CentralJumpCode from current compact entry point |
4394 | PTR_UINT16 pBranchInstr = (PTR_UINT16(addr)) + 1; |
4395 | uint16_t offset = DecodeOffsetFromBranchToCentralJump (*pBranchInstr); |
4396 | |
4397 | TADDR centralJump = addr + offset; |
4398 | int index = (centralJump - addr - TEP_ENTRY_SIZE) / TEP_ENTRY_SIZE; |
4399 | |
4400 | #else // _TARGET_ARM_ |
4401 | |
4402 | int index = *PTR_BYTE(addr+1); |
4403 | TADDR centralJump = addr + 4 + *PTR_SBYTE(addr+3); |
4404 | |
4405 | #endif // _TARGET_ARM_ |
4406 | |
4407 | CentralJumpCode* pCentralJumpCode = PTR_CentralJumpCode(centralJump); |
4408 | |
4409 | // Always do consistency check in debug |
4410 | if (fSpeculative INDEBUG(|| TRUE)) |
4411 | { |
4412 | SIZE_T i; |
4413 | for (i = 0; i < TEP_CENTRAL_JUMP_SIZE; i++) |
4414 | { |
4415 | BYTE b = ((BYTE*)&c_CentralJumpCode)[i]; |
4416 | if (b != 0 && b != *PTR_BYTE(centralJump+i)) |
4417 | { |
4418 | if (fSpeculative) return NULL; |
4419 | _ASSERTE(!"Unexpected code in temporary entrypoint" ); |
4420 | } |
4421 | } |
4422 | |
4423 | #ifdef _TARGET_ARM_ |
4424 | |
4425 | _ASSERTE_IMPL(pCentralJumpCode->CheckTarget(GetPreStubCompactARMEntryPoint())); |
4426 | |
4427 | #else // _TARGET_ARM_ |
4428 | |
4429 | _ASSERTE_IMPL(pCentralJumpCode->CheckTarget(GetPreStubEntryPoint())); |
4430 | |
4431 | #endif // _TARGET_ARM_ |
4432 | } |
4433 | |
4434 | #ifdef _TARGET_ARM_ |
4435 | // Go through all MethodDesc in MethodDescChunk and find the one with the required index |
4436 | PTR_MethodDescChunk pChunk = *((DPTR(PTR_MethodDescChunk))(centralJump + offsetof(CentralJumpCode, m_pChunk))); |
4437 | TADDR pMD = PTR_HOST_TO_TADDR (pChunk->GetFirstMethodDesc ()); |
4438 | |
4439 | _ASSERTE (index >= 0 && index < ((int) pChunk->GetCount ())); |
4440 | |
4441 | index = ((int) pChunk->GetCount ()) - 1 - index; |
4442 | |
4443 | SIZE_T totalSize = 0; |
4444 | int curIndex = 0; |
4445 | |
4446 | while (index != curIndex) |
4447 | { |
4448 | SIZE_T sizeCur = (PTR_MethodDesc (pMD))->SizeOf (); |
4449 | totalSize += sizeCur; |
4450 | |
4451 | pMD += sizeCur; |
4452 | ++curIndex; |
4453 | } |
4454 | |
4455 | return PTR_MethodDesc (pMD); |
4456 | #else // _TARGET_ARM_ |
4457 | return PTR_MethodDesc((TADDR)pCentralJumpCode->m_pBaseMD + index * MethodDesc::ALIGNMENT); |
4458 | #endif // _TARGET_ARM_ |
4459 | } |
4460 | |
4461 | //******************************************************************************* |
4462 | SIZE_T MethodDescChunk::SizeOfCompactEntryPoints(int count) |
4463 | { |
4464 | LIMITED_METHOD_DAC_CONTRACT; |
4465 | |
4466 | #ifdef _TARGET_ARM_ |
4467 | |
4468 | return COMPACT_ENTRY_ARM_CODE + count * TEP_ENTRY_SIZE + TEP_CENTRAL_JUMP_SIZE; |
4469 | |
4470 | #else // _TARGET_ARM_ |
4471 | |
4472 | int fullBlocks = count / TEP_MAX_BLOCK_INDEX; |
4473 | int remainder = count % TEP_MAX_BLOCK_INDEX; |
4474 | |
4475 | return 1 + (fullBlocks * TEP_FULL_BLOCK_SIZE) + |
4476 | (remainder * TEP_ENTRY_SIZE) + ((remainder != 0) ? TEP_CENTRAL_JUMP_SIZE : 0); |
4477 | |
4478 | #endif // _TARGET_ARM_ |
4479 | } |
4480 | |
4481 | #ifndef DACCESS_COMPILE |
4482 | TADDR MethodDescChunk::AllocateCompactEntryPoints(LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker) |
4483 | { |
4484 | CONTRACTL { |
4485 | THROWS; |
4486 | GC_NOTRIGGER; |
4487 | } CONTRACTL_END; |
4488 | |
4489 | int count = GetCount(); |
4490 | |
4491 | SIZE_T size = SizeOfCompactEntryPoints(count); |
4492 | |
4493 | TADDR temporaryEntryPoints = (TADDR)pamTracker->Track(pLoaderAllocator->GetPrecodeHeap()->AllocAlignedMem(size, sizeof(TADDR))); |
4494 | |
4495 | #ifdef _TARGET_ARM_ |
4496 | BYTE* p = (BYTE*)temporaryEntryPoints + COMPACT_ENTRY_ARM_CODE; |
4497 | int relOffset = count * TEP_ENTRY_SIZE - TEP_ENTRY_SIZE; // relative offset for the short jump |
4498 | |
4499 | _ASSERTE (relOffset < MAX_OFFSET_UNCONDITIONAL_BRANCH_THUMB); |
4500 | #else // _TARGET_ARM_ |
4501 | // make the temporary entrypoints unaligned, so they are easy to identify |
4502 | BYTE* p = (BYTE*)temporaryEntryPoints + 1; |
4503 | int indexInBlock = TEP_MAX_BLOCK_INDEX; // recompute relOffset in first iteration |
4504 | int relOffset = 0; // relative offset for the short jump |
4505 | #endif // _TARGET_ARM_ |
4506 | |
4507 | MethodDesc * pBaseMD = 0; // index of the start of the block |
4508 | |
4509 | MethodDesc * pMD = GetFirstMethodDesc(); |
4510 | for (int index = 0; index < count; index++) |
4511 | { |
4512 | #ifdef _TARGET_ARM_ |
4513 | |
4514 | uint8_t *pMovInstrByte1 = (uint8_t *)p; |
4515 | uint8_t *pMovInstrByte2 = (uint8_t *)p+1; |
4516 | uint16_t *pBranchInstr = ((uint16_t *)p)+1; |
4517 | |
4518 | *pMovInstrByte1 = TEP_ENTRY_INSTR1_BYTE1; |
4519 | *pMovInstrByte2 = TEP_ENTRY_INSTR1_BYTE2; |
4520 | *pBranchInstr = EncodeBranchToCentralJump ((int16_t) relOffset); |
4521 | |
4522 | p += TEP_ENTRY_SIZE; |
4523 | |
4524 | #else // _TARGET_ARM_ |
4525 | |
4526 | if (indexInBlock == TEP_MAX_BLOCK_INDEX) |
4527 | { |
4528 | relOffset = (min(count - index, TEP_MAX_BEFORE_INDEX) - 1) * TEP_ENTRY_SIZE; |
4529 | indexInBlock = 0; |
4530 | pBaseMD = pMD; |
4531 | } |
4532 | |
4533 | *(p+0) = X86_INSTR_MOV_AL; |
4534 | int methodDescIndex = pMD->GetMethodDescIndex() - pBaseMD->GetMethodDescIndex(); |
4535 | _ASSERTE(FitsInU1(methodDescIndex)); |
4536 | *(p+1) = (BYTE)methodDescIndex; |
4537 | |
4538 | *(p+2) = X86_INSTR_JMP_REL8; |
4539 | _ASSERTE(FitsInI1(relOffset)); |
4540 | *(p+3) = (BYTE)relOffset; |
4541 | |
4542 | p += TEP_ENTRY_SIZE; static_assert_no_msg(TEP_ENTRY_SIZE == 4); |
4543 | |
4544 | if (relOffset == 0) |
4545 | { |
4546 | CentralJumpCode* pCode = (CentralJumpCode*)p; |
4547 | |
4548 | memcpy(pCode, &c_CentralJumpCode, TEP_CENTRAL_JUMP_SIZE); |
4549 | |
4550 | pCode->Setup(pBaseMD, GetPreStubEntryPoint(), pLoaderAllocator); |
4551 | |
4552 | p += TEP_CENTRAL_JUMP_SIZE; |
4553 | |
4554 | relOffset -= TEP_CENTRAL_JUMP_SIZE; |
4555 | } |
4556 | |
4557 | indexInBlock++; |
4558 | |
4559 | #endif // _TARGET_ARM_ |
4560 | |
4561 | relOffset -= TEP_ENTRY_SIZE; |
4562 | pMD = (MethodDesc *)((BYTE *)pMD + pMD->SizeOf()); |
4563 | } |
4564 | |
4565 | #ifdef _TARGET_ARM_ |
4566 | |
4567 | CentralJumpCode* pCode = (CentralJumpCode*)p; |
4568 | memcpy(pCode, &c_CentralJumpCode, TEP_CENTRAL_JUMP_SIZE); |
4569 | pCode->Setup (GetPreStubCompactARMEntryPoint(), this); |
4570 | |
4571 | _ASSERTE(p + TEP_CENTRAL_JUMP_SIZE == (BYTE*)temporaryEntryPoints + size); |
4572 | |
4573 | #else // _TARGET_ARM_ |
4574 | |
4575 | _ASSERTE(p == (BYTE*)temporaryEntryPoints + size); |
4576 | |
4577 | #endif // _TARGET_ARM_ |
4578 | |
4579 | ClrFlushInstructionCache((LPVOID)temporaryEntryPoints, size); |
4580 | |
4581 | SetHasCompactEntryPoints(); |
4582 | return temporaryEntryPoints; |
4583 | } |
4584 | #endif // !DACCESS_COMPILE |
4585 | |
4586 | #endif // HAS_COMPACT_ENTRYPOINTS |
4587 | |
4588 | //******************************************************************************* |
4589 | PCODE MethodDescChunk::GetTemporaryEntryPoint(int index) |
4590 | { |
4591 | LIMITED_METHOD_CONTRACT; |
4592 | |
4593 | _ASSERTE(HasTemporaryEntryPoints()); |
4594 | |
4595 | #ifdef HAS_COMPACT_ENTRYPOINTS |
4596 | if (HasCompactEntryPoints()) |
4597 | { |
4598 | #ifdef _TARGET_ARM_ |
4599 | |
4600 | return GetTemporaryEntryPoints() + COMPACT_ENTRY_ARM_CODE + THUMB_CODE + index * TEP_ENTRY_SIZE; |
4601 | |
4602 | #else // _TARGET_ARM_ |
4603 | |
4604 | int fullBlocks = index / TEP_MAX_BLOCK_INDEX; |
4605 | int remainder = index % TEP_MAX_BLOCK_INDEX; |
4606 | |
4607 | return GetTemporaryEntryPoints() + 1 + (fullBlocks * TEP_FULL_BLOCK_SIZE) + |
4608 | (remainder * TEP_ENTRY_SIZE) + ((remainder >= TEP_MAX_BEFORE_INDEX) ? TEP_CENTRAL_JUMP_SIZE : 0); |
4609 | |
4610 | #endif // _TARGET_ARM_ |
4611 | } |
4612 | #endif // HAS_COMPACT_ENTRYPOINTS |
4613 | |
4614 | return Precode::GetPrecodeForTemporaryEntryPoint(GetTemporaryEntryPoints(), index)->GetEntryPoint(); |
4615 | } |
4616 | |
4617 | PCODE MethodDesc::GetTemporaryEntryPoint() |
4618 | { |
4619 | CONTRACTL |
4620 | { |
4621 | NOTHROW; |
4622 | GC_NOTRIGGER; |
4623 | SO_TOLERANT; |
4624 | MODE_ANY; |
4625 | } |
4626 | CONTRACTL_END; |
4627 | |
4628 | MethodDescChunk* pChunk = GetMethodDescChunk(); |
4629 | _ASSERTE(pChunk->HasTemporaryEntryPoints()); |
4630 | |
4631 | int lo = 0, hi = pChunk->GetCount() - 1; |
4632 | |
4633 | // Find the temporary entrypoint in the chunk by binary search |
4634 | while (lo < hi) |
4635 | { |
4636 | int mid = (lo + hi) / 2; |
4637 | |
4638 | TADDR pEntryPoint = pChunk->GetTemporaryEntryPoint(mid); |
4639 | |
4640 | MethodDesc * pMD = MethodDesc::GetMethodDescFromStubAddr(pEntryPoint); |
4641 | if (PTR_HOST_TO_TADDR(this) == PTR_HOST_TO_TADDR(pMD)) |
4642 | return pEntryPoint; |
4643 | |
4644 | if (PTR_HOST_TO_TADDR(this) > PTR_HOST_TO_TADDR(pMD)) |
4645 | lo = mid + 1; |
4646 | else |
4647 | hi = mid - 1; |
4648 | } |
4649 | |
4650 | _ASSERTE(lo == hi); |
4651 | |
4652 | TADDR pEntryPoint = pChunk->GetTemporaryEntryPoint(lo); |
4653 | |
4654 | #ifdef _DEBUG |
4655 | MethodDesc * pMD = MethodDesc::GetMethodDescFromStubAddr(pEntryPoint); |
4656 | _ASSERTE(PTR_HOST_TO_TADDR(this) == PTR_HOST_TO_TADDR(pMD)); |
4657 | #endif |
4658 | |
4659 | return pEntryPoint; |
4660 | } |
4661 | |
4662 | #ifndef DACCESS_COMPILE |
4663 | //******************************************************************************* |
4664 | void MethodDesc::SetTemporaryEntryPoint(LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker) |
4665 | { |
4666 | WRAPPER_NO_CONTRACT; |
4667 | |
4668 | GetMethodDescChunk()->EnsureTemporaryEntryPointsCreated(pLoaderAllocator, pamTracker); |
4669 | |
4670 | TADDR slot = GetAddrOfSlot(); |
4671 | if (IsVtableSlot()) |
4672 | { |
4673 | MethodTable::VTableIndir2_t *slotPtr = ((MethodTable::VTableIndir2_t *) slot); |
4674 | _ASSERTE(slotPtr->IsNull()); |
4675 | slotPtr->SetValue(GetTemporaryEntryPoint()); |
4676 | } |
4677 | else |
4678 | { |
4679 | PCODE *slotPtr = (PCODE *) slot; |
4680 | _ASSERTE(*slotPtr == NULL); |
4681 | *slotPtr = GetTemporaryEntryPoint(); |
4682 | } |
4683 | |
4684 | if (RequiresStableEntryPoint()) |
4685 | { |
4686 | // The rest of the system assumes that certain methods always have stable entrypoints. |
4687 | // Create them now. |
4688 | GetOrCreatePrecode(); |
4689 | } |
4690 | } |
4691 | |
4692 | //******************************************************************************* |
4693 | void MethodDescChunk::CreateTemporaryEntryPoints(LoaderAllocator *pLoaderAllocator, AllocMemTracker *pamTracker) |
4694 | { |
4695 | WRAPPER_NO_CONTRACT; |
4696 | |
4697 | _ASSERTE(GetTemporaryEntryPoints() == NULL); |
4698 | |
4699 | TADDR temporaryEntryPoints = Precode::AllocateTemporaryEntryPoints(this, pLoaderAllocator, pamTracker); |
4700 | |
4701 | #ifdef HAS_COMPACT_ENTRYPOINTS |
4702 | // Precodes allocated only if they provide more compact representation or if it is required |
4703 | if (temporaryEntryPoints == NULL) |
4704 | { |
4705 | temporaryEntryPoints = AllocateCompactEntryPoints(pLoaderAllocator, pamTracker); |
4706 | } |
4707 | #endif // HAS_COMPACT_ENTRYPOINTS |
4708 | |
4709 | *(((TADDR *)this)-1) = temporaryEntryPoints; |
4710 | |
4711 | _ASSERTE(GetTemporaryEntryPoints() != NULL); |
4712 | } |
4713 | |
4714 | //******************************************************************************* |
4715 | void MethodDesc::InterlockedUpdateFlags2(BYTE bMask, BOOL fSet) |
4716 | { |
4717 | WRAPPER_NO_CONTRACT; |
4718 | |
4719 | ULONG* pLong = (ULONG*)(&m_bFlags2 - 3); |
4720 | static_assert_no_msg(offsetof(MethodDesc, m_bFlags2) % sizeof(LONG) == 3); |
4721 | |
4722 | #if BIGENDIAN |
4723 | if (fSet) |
4724 | FastInterlockOr(pLong, (ULONG)bMask); |
4725 | else |
4726 | FastInterlockAnd(pLong, ~(ULONG)bMask); |
4727 | #else // !BIGENDIAN |
4728 | if (fSet) |
4729 | FastInterlockOr(pLong, (ULONG)bMask << (3 * 8)); |
4730 | else |
4731 | FastInterlockAnd(pLong, ~((ULONG)bMask << (3 * 8))); |
4732 | #endif // !BIGENDIAN |
4733 | } |
4734 | |
4735 | //******************************************************************************* |
4736 | Precode* MethodDesc::GetOrCreatePrecode() |
4737 | { |
4738 | WRAPPER_NO_CONTRACT; |
4739 | |
4740 | if (HasPrecode()) |
4741 | { |
4742 | return GetPrecode(); |
4743 | } |
4744 | |
4745 | TADDR pSlot = GetAddrOfSlot(); |
4746 | PCODE tempEntry = GetTemporaryEntryPoint(); |
4747 | |
4748 | PrecodeType requiredType = GetPrecodeType(); |
4749 | PrecodeType availableType = PRECODE_INVALID; |
4750 | |
4751 | if (!GetMethodDescChunk()->HasCompactEntryPoints()) |
4752 | { |
4753 | availableType = Precode::GetPrecodeFromEntryPoint(tempEntry)->GetType(); |
4754 | } |
4755 | |
4756 | // Allocate the precode if necessary |
4757 | if (requiredType != availableType) |
4758 | { |
4759 | // code:Precode::AllocateTemporaryEntryPoints should always create precode of the right type for dynamic methods. |
4760 | // If we took this path for dynamic methods, the precode may leak since we may allocate it in domain-neutral loader heap. |
4761 | _ASSERTE(!IsLCGMethod()); |
4762 | |
4763 | AllocMemTracker amt; |
4764 | Precode* pPrecode = Precode::Allocate(requiredType, this, GetLoaderAllocator(), &amt); |
4765 | PCODE newVal; |
4766 | PCODE oldVal; |
4767 | TADDR *slotAddr; |
4768 | |
4769 | if (IsVtableSlot()) |
4770 | { |
4771 | newVal = MethodTable::VTableIndir2_t::GetRelative(pSlot, pPrecode->GetEntryPoint()); |
4772 | oldVal = MethodTable::VTableIndir2_t::GetRelative(pSlot, tempEntry); |
4773 | slotAddr = (TADDR *) EnsureWritablePages((MethodTable::VTableIndir2_t *) pSlot); |
4774 | } |
4775 | else |
4776 | { |
4777 | newVal = pPrecode->GetEntryPoint(); |
4778 | oldVal = tempEntry; |
4779 | slotAddr = (TADDR *) EnsureWritablePages((PCODE *) pSlot); |
4780 | } |
4781 | |
4782 | if (FastInterlockCompareExchangePointer(slotAddr, (TADDR) newVal, (TADDR) oldVal) == oldVal) |
4783 | amt.SuppressRelease(); |
4784 | } |
4785 | |
4786 | // Set the flags atomically |
4787 | InterlockedUpdateFlags2(enum_flag2_HasStableEntryPoint | enum_flag2_HasPrecode, TRUE); |
4788 | |
4789 | PCODE addr; |
4790 | if (IsVtableSlot()) |
4791 | { |
4792 | addr = ((MethodTable::VTableIndir2_t *)pSlot)->GetValue(); |
4793 | } |
4794 | else |
4795 | { |
4796 | addr = *((PCODE *)pSlot); |
4797 | } |
4798 | return Precode::GetPrecodeFromEntryPoint(addr); |
4799 | } |
4800 | |
4801 | //******************************************************************************* |
4802 | BOOL MethodDesc::SetNativeCodeInterlocked(PCODE addr, PCODE pExpected /*=NULL*/) |
4803 | { |
4804 | CONTRACTL { |
4805 | THROWS; |
4806 | GC_NOTRIGGER; |
4807 | } CONTRACTL_END; |
4808 | |
4809 | if (HasNativeCodeSlot()) |
4810 | { |
4811 | #ifdef _TARGET_ARM_ |
4812 | _ASSERTE(IsThumbCode(addr) || (addr==NULL)); |
4813 | addr &= ~THUMB_CODE; |
4814 | |
4815 | if (pExpected != NULL) |
4816 | { |
4817 | _ASSERTE(IsThumbCode(pExpected)); |
4818 | pExpected &= ~THUMB_CODE; |
4819 | } |
4820 | #endif |
4821 | |
4822 | TADDR pSlot = GetAddrOfNativeCodeSlot(); |
4823 | NativeCodeSlot value, expected; |
4824 | |
4825 | value.SetValueMaybeNull(pSlot, addr | (*dac_cast<PTR_TADDR>(pSlot) & FIXUP_LIST_MASK)); |
4826 | expected.SetValueMaybeNull(pSlot, pExpected | (*dac_cast<PTR_TADDR>(pSlot) & FIXUP_LIST_MASK)); |
4827 | |
4828 | return FastInterlockCompareExchangePointer(EnsureWritablePages(reinterpret_cast<TADDR*>(pSlot)), |
4829 | (TADDR&)value, (TADDR&)expected) == (TADDR&)expected; |
4830 | } |
4831 | |
4832 | if (IsDefaultInterfaceMethod() && HasPrecode()) |
4833 | { |
4834 | return GetPrecode()->SetTargetInterlocked(addr); |
4835 | } |
4836 | |
4837 | _ASSERTE(pExpected == NULL); |
4838 | return SetStableEntryPointInterlocked(addr); |
4839 | } |
4840 | |
4841 | //******************************************************************************* |
4842 | BOOL MethodDesc::SetStableEntryPointInterlocked(PCODE addr) |
4843 | { |
4844 | CONTRACTL { |
4845 | THROWS; |
4846 | GC_NOTRIGGER; |
4847 | } CONTRACTL_END; |
4848 | |
4849 | _ASSERTE(!HasPrecode()); |
4850 | |
4851 | PCODE pExpected = GetTemporaryEntryPoint(); |
4852 | TADDR pSlot = GetAddrOfSlot(); |
4853 | |
4854 | BOOL fResult; |
4855 | |
4856 | TADDR *slotAddr; |
4857 | PCODE newVal; |
4858 | PCODE oldVal; |
4859 | |
4860 | if (IsVtableSlot()) |
4861 | { |
4862 | newVal = MethodTable::VTableIndir2_t::GetRelative(pSlot, addr); |
4863 | oldVal = MethodTable::VTableIndir2_t::GetRelative(pSlot, pExpected); |
4864 | slotAddr = (TADDR *) EnsureWritablePages((MethodTable::VTableIndir2_t *) pSlot); |
4865 | } |
4866 | else |
4867 | { |
4868 | newVal = addr; |
4869 | oldVal = pExpected; |
4870 | slotAddr = (TADDR *) EnsureWritablePages((PCODE *) pSlot); |
4871 | } |
4872 | |
4873 | fResult = FastInterlockCompareExchangePointer(slotAddr, (TADDR) newVal, (TADDR) oldVal) == oldVal; |
4874 | |
4875 | InterlockedUpdateFlags2(enum_flag2_HasStableEntryPoint, TRUE); |
4876 | |
4877 | return fResult; |
4878 | } |
4879 | |
4880 | //******************************************************************************* |
4881 | void NDirectMethodDesc::InterlockedSetNDirectFlags(WORD wFlags) |
4882 | { |
4883 | CONTRACTL |
4884 | { |
4885 | THROWS; |
4886 | GC_NOTRIGGER; |
4887 | } |
4888 | CONTRACTL_END |
4889 | |
4890 | // Since InterlockedCompareExchange only works on ULONGs, |
4891 | // we'll have to operate on the entire ULONG. Ugh. |
4892 | |
4893 | WORD *pFlags = &ndirect.m_wFlags; |
4894 | |
4895 | EnsureWritablePages(pFlags); |
4896 | |
4897 | // Make sure that m_flags is aligned on a 4 byte boundry |
4898 | _ASSERTE( ( ((size_t) pFlags) & (sizeof(ULONG)-1) ) == 0); |
4899 | |
4900 | // Ensure we won't be reading or writing outside the bounds of the NDirectMethodDesc. |
4901 | _ASSERTE((BYTE*)pFlags >= (BYTE*)this); |
4902 | _ASSERTE((BYTE*)pFlags+sizeof(ULONG) <= (BYTE*)(this+1)); |
4903 | |
4904 | DWORD dwMask = 0; |
4905 | |
4906 | // Set the flags in the mask |
4907 | ((WORD*)&dwMask)[0] |= wFlags; |
4908 | |
4909 | // Now, slam all 32 bits atomically. |
4910 | FastInterlockOr((DWORD*)pFlags, dwMask); |
4911 | } |
4912 | |
4913 | #ifndef CROSSGEN_COMPILE |
4914 | FARPROC NDirectMethodDesc::FindEntryPointWithMangling(HINSTANCE hMod, PTR_CUTF8 entryPointName) const |
4915 | { |
4916 | CONTRACTL |
4917 | { |
4918 | THROWS; |
4919 | GC_TRIGGERS; |
4920 | MODE_ANY; |
4921 | } |
4922 | CONTRACTL_END; |
4923 | |
4924 | FARPROC pFunc = GetProcAddress(hMod, entryPointName); |
4925 | |
4926 | #if defined(_TARGET_X86_) |
4927 | |
4928 | if (pFunc) |
4929 | { |
4930 | return pFunc; |
4931 | } |
4932 | |
4933 | if (IsStdCall()) |
4934 | { |
4935 | DWORD probedEntrypointNameLength = (DWORD)(strlen(entryPointName) + 1); // 1 for null terminator |
4936 | int dstbufsize = (int)(sizeof(char) * (probedEntrypointNameLength + 10)); // 10 for stdcall mangling |
4937 | LPSTR szProbedEntrypointName = ((LPSTR)_alloca(dstbufsize + 1)); |
4938 | szProbedEntrypointName[0] = '_'; |
4939 | strcpy_s(szProbedEntrypointName + 1, dstbufsize, entryPointName); |
4940 | szProbedEntrypointName[probedEntrypointNameLength] = '\0'; // Add an extra '\0'. |
4941 | |
4942 | UINT16 numParamBytesMangle = GetStackArgumentSize(); |
4943 | |
4944 | if (IsStdCallWithRetBuf()) |
4945 | { |
4946 | _ASSERTE(numParamBytesMangle >= sizeof(LPVOID)); |
4947 | numParamBytesMangle -= (UINT16)sizeof(LPVOID); |
4948 | } |
4949 | |
4950 | sprintf_s(szProbedEntrypointName + probedEntrypointNameLength, dstbufsize - probedEntrypointNameLength + 1, "@%lu" , (ULONG)numParamBytesMangle); |
4951 | pFunc = GetProcAddress(hMod, szProbedEntrypointName); |
4952 | } |
4953 | |
4954 | #endif |
4955 | |
4956 | return pFunc; |
4957 | } |
4958 | |
4959 | //******************************************************************************* |
4960 | LPVOID NDirectMethodDesc::FindEntryPoint(HINSTANCE hMod) const |
4961 | { |
4962 | CONTRACTL |
4963 | { |
4964 | THROWS; |
4965 | GC_TRIGGERS; |
4966 | MODE_ANY; |
4967 | } |
4968 | CONTRACTL_END; |
4969 | |
4970 | char const * funcName = GetEntrypointName(); |
4971 | |
4972 | FARPROC pFunc = NULL; |
4973 | |
4974 | #ifndef FEATURE_PAL |
4975 | // Handle ordinals. |
4976 | if (funcName[0] == '#') |
4977 | { |
4978 | long ordinal = atol(funcName + 1); |
4979 | return reinterpret_cast<LPVOID>(GetProcAddress(hMod, (LPCSTR)(size_t)((UINT16)ordinal))); |
4980 | } |
4981 | #endif |
4982 | |
4983 | // Just look for the user-provided name without charset suffixes. If it is unicode fcn, we are going |
4984 | // to need to check for the 'W' API because it takes precedence over the |
4985 | // unmangled one (on NT some APIs have unmangled ANSI exports). |
4986 | pFunc = FindEntryPointWithMangling(hMod, funcName); |
4987 | if ((pFunc != NULL && IsNativeAnsi()) || IsNativeNoMangled()) |
4988 | { |
4989 | return reinterpret_cast<LPVOID>(pFunc); |
4990 | } |
4991 | |
4992 | DWORD probedEntrypointNameLength = (DWORD)(strlen(funcName) + 1); // +1 for charset decorations |
4993 | |
4994 | // Allocate space for a copy of the entry point name. |
4995 | int dstbufsize = (int)(sizeof(char) * (probedEntrypointNameLength + 1)); // +1 for the null terminator |
4996 | |
4997 | LPSTR szProbedEntrypointName = ((LPSTR)_alloca(dstbufsize)); |
4998 | |
4999 | // Copy the name so we can mangle it. |
5000 | strcpy_s(szProbedEntrypointName, dstbufsize, funcName); |
5001 | szProbedEntrypointName[probedEntrypointNameLength] = '\0'; // Add an extra '\0'. |
5002 | |
5003 | if(!IsNativeNoMangled()) |
5004 | { |
5005 | szProbedEntrypointName[probedEntrypointNameLength - 1] = IsNativeAnsi() ? 'A' : 'W'; |
5006 | |
5007 | FARPROC pProbedFunc = FindEntryPointWithMangling(hMod, szProbedEntrypointName); |
5008 | |
5009 | if(pProbedFunc != NULL) |
5010 | { |
5011 | pFunc = pProbedFunc; |
5012 | } |
5013 | |
5014 | probedEntrypointNameLength++; |
5015 | } |
5016 | |
5017 | return reinterpret_cast<LPVOID>(pFunc); |
5018 | } |
5019 | #endif // CROSSGEN_COMPILE |
5020 | |
5021 | #if !defined(CROSSGEN_COMPILE) |
5022 | //******************************************************************************* |
5023 | void NDirectMethodDesc::InitEarlyBoundNDirectTarget() |
5024 | { |
5025 | CONTRACTL |
5026 | { |
5027 | THROWS; |
5028 | GC_TRIGGERS; |
5029 | INJECT_FAULT(COMPlusThrowOM();); |
5030 | } |
5031 | CONTRACTL_END |
5032 | |
5033 | _ASSERTE(IsEarlyBound()); |
5034 | |
5035 | if (IsClassConstructorTriggeredAtLinkTime()) |
5036 | { |
5037 | GetMethodTable()->CheckRunClassInitThrowing(); |
5038 | } |
5039 | |
5040 | const void *target = GetModule()->GetInternalPInvokeTarget(GetRVA()); |
5041 | _ASSERTE(target != 0); |
5042 | |
5043 | if (HeuristicDoesThisLookLikeAGetLastErrorCall((LPBYTE)target)) |
5044 | target = (BYTE*)FalseGetLastError; |
5045 | |
5046 | // As long as we've set the NDirect target field we don't need to backpatch the import thunk glue. |
5047 | // All NDirect calls all through the NDirect target, so if it's updated, then we won't go into |
5048 | // NDirectImportThunk(). In fact, backpatching the import thunk glue leads to race conditions. |
5049 | SetNDirectTarget((LPVOID)target); |
5050 | } |
5051 | #endif // !CROSSGEN_COMPILE |
5052 | |
5053 | //******************************************************************************* |
5054 | void MethodDesc::ComputeSuppressUnmanagedCodeAccessAttr(IMDInternalImport *pImport) |
5055 | { |
5056 | CONTRACTL |
5057 | { |
5058 | THROWS; |
5059 | GC_NOTRIGGER; |
5060 | FORBID_FAULT; |
5061 | } |
5062 | CONTRACTL_END; |
5063 | |
5064 | } |
5065 | |
5066 | //******************************************************************************* |
5067 | BOOL MethodDesc::HasNativeCallableAttribute() |
5068 | { |
5069 | |
5070 | CONTRACTL |
5071 | { |
5072 | THROWS; |
5073 | GC_NOTRIGGER; |
5074 | FORBID_FAULT; |
5075 | } |
5076 | CONTRACTL_END; |
5077 | |
5078 | HRESULT hr = GetMDImport()->GetCustomAttributeByName(GetMemberDef(), |
5079 | g_NativeCallableAttribute, |
5080 | NULL, |
5081 | NULL); |
5082 | if (hr == S_OK) |
5083 | { |
5084 | return TRUE; |
5085 | } |
5086 | |
5087 | return FALSE; |
5088 | } |
5089 | |
5090 | #ifdef FEATURE_COMINTEROP |
5091 | //******************************************************************************* |
5092 | void ComPlusCallMethodDesc::InitComEventCallInfo() |
5093 | { |
5094 | CONTRACTL |
5095 | { |
5096 | THROWS; |
5097 | GC_TRIGGERS; |
5098 | INJECT_FAULT(COMPlusThrowOM()); |
5099 | } |
5100 | CONTRACTL_END |
5101 | |
5102 | MethodTable *pItfMT = GetInterfaceMethodTable(); |
5103 | MethodDesc *pItfMD = this; |
5104 | MethodTable *pSrcItfClass = NULL; |
5105 | MethodTable *pEvProvClass = NULL; |
5106 | |
5107 | // Retrieve the event provider class. |
5108 | WORD cbExtraSlots = ComMethodTable::GetNumExtraSlots(pItfMT->GetComInterfaceType()); |
5109 | WORD itfSlotNum = (WORD) m_pComPlusCallInfo->m_cachedComSlot - cbExtraSlots; |
5110 | pItfMT->GetEventInterfaceInfo(&pSrcItfClass, &pEvProvClass); |
5111 | m_pComPlusCallInfo->m_pEventProviderMD = MemberLoader::FindMethodForInterfaceSlot(pEvProvClass, pItfMT, itfSlotNum); |
5112 | |
5113 | // If we could not find the method, then the event provider does not support |
5114 | // this event. This is a fatal error. |
5115 | if (!m_pComPlusCallInfo->m_pEventProviderMD) |
5116 | { |
5117 | // Init the interface MD for error reporting. |
5118 | pItfMD = (ComPlusCallMethodDesc*)pItfMT->GetMethodDescForSlot(itfSlotNum); |
5119 | |
5120 | // Retrieve the event provider class name. |
5121 | StackSString ssEvProvClassName; |
5122 | pEvProvClass->_GetFullyQualifiedNameForClass(ssEvProvClassName); |
5123 | |
5124 | // Retrieve the COM event interface class name. |
5125 | StackSString ssEvItfName; |
5126 | pItfMT->_GetFullyQualifiedNameForClass(ssEvItfName); |
5127 | |
5128 | // Convert the method name to unicode. |
5129 | StackSString ssMethodName(SString::Utf8, pItfMD->GetName()); |
5130 | |
5131 | // Throw the exception. |
5132 | COMPlusThrow(kTypeLoadException, IDS_EE_METHOD_NOT_FOUND_ON_EV_PROV, |
5133 | ssMethodName.GetUnicode(), ssEvItfName.GetUnicode(), ssEvProvClassName.GetUnicode()); |
5134 | } |
5135 | } |
5136 | #endif // FEATURE_COMINTEROP |
5137 | |
5138 | #endif // !DACCESS_COMPILE |
5139 | |
5140 | |
5141 | #ifdef DACCESS_COMPILE |
5142 | |
5143 | //******************************************************************************* |
5144 | void |
5145 | MethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
5146 | { |
5147 | SUPPORTS_DAC; |
5148 | |
5149 | if (DacHasMethodDescBeenEnumerated(this)) |
5150 | { |
5151 | return; |
5152 | } |
5153 | |
5154 | // Save away the whole MethodDescChunk as in many |
5155 | // places RecoverChunk is called on a method desc so |
5156 | // the whole chunk must be available. This also |
5157 | // automatically picks up any prestubs and such. |
5158 | GetMethodDescChunk()->EnumMemoryRegions(flags); |
5159 | |
5160 | if (HasPrecode()) |
5161 | { |
5162 | GetPrecode()->EnumMemoryRegions(flags); |
5163 | } |
5164 | |
5165 | // Need to save the Debug-Info for this method so that we can see it in a debugger later. |
5166 | DebugInfoManager::EnumMemoryRegionsForMethodDebugInfo(flags, this); |
5167 | |
5168 | if (!IsNoMetadata() ||IsILStub()) |
5169 | { |
5170 | // The assembling of the string below implicitly dumps the memory we need. |
5171 | |
5172 | StackSString str; |
5173 | TypeString::AppendMethodInternal(str, this, TypeString::FormatSignature|TypeString::FormatNamespace|TypeString::FormatFullInst); |
5174 | |
5175 | #ifdef FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
5176 | if (flags == CLRDATA_ENUM_MEM_MINI || flags == CLRDATA_ENUM_MEM_TRIAGE) |
5177 | { |
5178 | // we want to save just the method name, so truncate at the open paranthesis |
5179 | SString::Iterator it = str.Begin(); |
5180 | if (str.Find(it, W('('))) |
5181 | { |
5182 | // ensure the symbol ends in "()" to minimize regressions |
5183 | // in !analyze assuming the existence of the argument list |
5184 | str.Truncate(++it); |
5185 | str.Append(W(')')); |
5186 | } |
5187 | |
5188 | DacMdCacheAddEEName(dac_cast<TADDR>(this), str); |
5189 | } |
5190 | #endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS |
5191 | |
5192 | // The module path is used in the output of !clrstack and !pe if the |
5193 | // module is not available when the minidump is inspected. By retrieving |
5194 | // the path here, the required memory is implicitly dumped. |
5195 | Module* pModule = GetModule(); |
5196 | if (pModule) |
5197 | { |
5198 | pModule->GetPath(); |
5199 | } |
5200 | } |
5201 | |
5202 | // Also, call DacValidateMD to dump the memory it needs. !clrstack calls |
5203 | // DacValidateMD before it retrieves the method name. We don't expect |
5204 | // DacValidateMD to fail, but if it does, ignore the failure and try to assemble the |
5205 | // string anyway so that clients that don't validate the MD still work. |
5206 | |
5207 | DacValidateMD(this); |
5208 | |
5209 | DacSetMethodDescEnumerated(this); |
5210 | |
5211 | } |
5212 | |
5213 | //******************************************************************************* |
5214 | void |
5215 | StoredSigMethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
5216 | { |
5217 | SUPPORTS_DAC; |
5218 | // 'this' already done, see below. |
5219 | DacEnumMemoryRegion(GetSigRVA(), m_cSig); |
5220 | } |
5221 | |
5222 | //******************************************************************************* |
5223 | void |
5224 | MethodDescChunk::EnumMemoryRegions(CLRDataEnumMemoryFlags flags) |
5225 | { |
5226 | SUPPORTS_DAC; |
5227 | |
5228 | DAC_CHECK_ENUM_THIS(); |
5229 | EMEM_OUT(("MEM: %p MethodDescChunk\n" , dac_cast<TADDR>(this))); |
5230 | |
5231 | DacEnumMemoryRegion(dac_cast<TADDR>(this), SizeOf()); |
5232 | |
5233 | PTR_MethodTable pMT = GetMethodTable(); |
5234 | |
5235 | if (pMT.IsValid()) |
5236 | { |
5237 | pMT->EnumMemoryRegions(flags); |
5238 | } |
5239 | |
5240 | if (HasTemporaryEntryPoints()) |
5241 | { |
5242 | SIZE_T size; |
5243 | |
5244 | #ifdef HAS_COMPACT_ENTRYPOINTS |
5245 | if (HasCompactEntryPoints()) |
5246 | { |
5247 | size = SizeOfCompactEntryPoints(GetCount()); |
5248 | } |
5249 | else |
5250 | #endif // HAS_COMPACT_ENTRYPOINTS |
5251 | { |
5252 | size = Precode::SizeOfTemporaryEntryPoints(GetTemporaryEntryPoints(), GetCount()); |
5253 | } |
5254 | |
5255 | DacEnumMemoryRegion(GetTemporaryEntryPoints(), size); |
5256 | } |
5257 | |
5258 | MethodDesc * pMD = GetFirstMethodDesc(); |
5259 | MethodDesc * pOldMD = NULL; |
5260 | while (pMD != NULL && pMD != pOldMD) |
5261 | { |
5262 | pOldMD = pMD; |
5263 | EX_TRY |
5264 | { |
5265 | if (pMD->IsMethodImpl()) |
5266 | { |
5267 | pMD->GetMethodImpl()->EnumMemoryRegions(flags); |
5268 | } |
5269 | } |
5270 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
5271 | |
5272 | EX_TRY |
5273 | { |
5274 | if (pMD->HasStoredSig()) |
5275 | { |
5276 | dac_cast<PTR_StoredSigMethodDesc>(pMD)->EnumMemoryRegions(flags); |
5277 | } |
5278 | |
5279 | // Check whether the next MethodDesc is within the bounds of the current chunks |
5280 | TADDR pNext = dac_cast<TADDR>(pMD) + pMD->SizeOf(); |
5281 | TADDR pEnd = dac_cast<TADDR>(this) + this->SizeOf(); |
5282 | |
5283 | pMD = (pNext < pEnd) ? PTR_MethodDesc(pNext) : NULL; |
5284 | } |
5285 | EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED |
5286 | } |
5287 | } |
5288 | |
5289 | #endif // DACCESS_COMPILE |
5290 | |
5291 | #ifndef DACCESS_COMPILE |
5292 | //******************************************************************************* |
5293 | MethodDesc *MethodDesc::GetInterfaceMD() |
5294 | { |
5295 | CONTRACT (MethodDesc*) { |
5296 | THROWS; |
5297 | GC_TRIGGERS; |
5298 | INSTANCE_CHECK; |
5299 | PRECONDITION(!IsInterface()); |
5300 | POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); |
5301 | } CONTRACT_END; |
5302 | MethodTable *pMT = GetMethodTable(); |
5303 | RETURN(pMT->ReverseInterfaceMDLookup(GetSlot())); |
5304 | } |
5305 | #endif // !DACCESS_COMPILE |
5306 | |
5307 | PTR_LoaderAllocator MethodDesc::GetLoaderAllocator() |
5308 | { |
5309 | WRAPPER_NO_CONTRACT; |
5310 | return GetLoaderModule()->GetLoaderAllocator(); |
5311 | } |
5312 | |
5313 | #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) |
5314 | REFLECTMETHODREF MethodDesc::GetStubMethodInfo() |
5315 | { |
5316 | CONTRACTL |
5317 | { |
5318 | THROWS; |
5319 | GC_TRIGGERS; |
5320 | INJECT_FAULT(COMPlusThrowOM()); |
5321 | MODE_COOPERATIVE; |
5322 | } |
5323 | CONTRACTL_END; |
5324 | |
5325 | REFLECTMETHODREF retVal; |
5326 | REFLECTMETHODREF methodRef = (REFLECTMETHODREF)AllocateObject(MscorlibBinder::GetClass(CLASS__STUBMETHODINFO)); |
5327 | GCPROTECT_BEGIN(methodRef); |
5328 | |
5329 | methodRef->SetMethod(this); |
5330 | LoaderAllocator *pLoaderAllocatorOfMethod = this->GetLoaderAllocator(); |
5331 | if (pLoaderAllocatorOfMethod->IsCollectible()) |
5332 | methodRef->SetKeepAlive(pLoaderAllocatorOfMethod->GetExposedObject()); |
5333 | |
5334 | retVal = methodRef; |
5335 | GCPROTECT_END(); |
5336 | |
5337 | return retVal; |
5338 | } |
5339 | #endif // !DACCESS_COMPILE && CROSSGEN_COMPILE |
5340 | |
5341 | #ifndef DACCESS_COMPILE |
5342 | typedef void (*WalkValueTypeParameterFnPtr)(Module *pModule, mdToken token, Module *pDefModule, mdToken tkDefToken, SigPointer *ptr, SigTypeContext *pTypeContext, void *pData); |
5343 | |
5344 | void MethodDesc::WalkValueTypeParameters(MethodTable *pMT, WalkValueTypeParameterFnPtr function, void *pData) |
5345 | { |
5346 | CONTRACTL |
5347 | { |
5348 | THROWS; |
5349 | GC_TRIGGERS; |
5350 | SO_INTOLERANT; |
5351 | } |
5352 | CONTRACTL_END; |
5353 | |
5354 | ULONG numArgs = 0; |
5355 | Module *pModule = this->GetModule(); |
5356 | SigPointer ptr = this->GetSigPointer(); |
5357 | |
5358 | // skip over calling convention. |
5359 | ULONG callConv = 0; |
5360 | IfFailThrowBF(ptr.GetCallingConvInfo(&callConv), BFA_BAD_SIGNATURE, pModule); |
5361 | |
5362 | // If calling convention is generic, skip GenParamCount |
5363 | if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC) |
5364 | { |
5365 | IfFailThrowBF(ptr.GetData(NULL), BFA_BAD_SIGNATURE, pModule); |
5366 | } |
5367 | |
5368 | IfFailThrowBF(ptr.GetData(&numArgs), BFA_BAD_SIGNATURE, pModule); |
5369 | |
5370 | SigTypeContext typeContext(this, TypeHandle(pMT)); |
5371 | |
5372 | // iterate over the return type and parameters |
5373 | for (DWORD j = 0; j <= numArgs; j++) |
5374 | { |
5375 | CorElementType type = ptr.PeekElemTypeClosed(pModule, &typeContext); |
5376 | if (type != ELEMENT_TYPE_VALUETYPE) |
5377 | goto moveToNextToken; |
5378 | |
5379 | mdToken token; |
5380 | Module *pTokenModule; |
5381 | token = ptr.PeekValueTypeTokenClosed(pModule, &typeContext, &pTokenModule); |
5382 | |
5383 | if (token == mdTokenNil) |
5384 | goto moveToNextToken; |
5385 | |
5386 | DWORD dwAttrType; |
5387 | Module *pDefModule; |
5388 | mdToken defToken; |
5389 | |
5390 | dwAttrType = 0; |
5391 | if (ClassLoader::ResolveTokenToTypeDefThrowing(pTokenModule, token, &pDefModule, &defToken)) |
5392 | { |
5393 | if (function != NULL) |
5394 | function(pModule, token, pDefModule, defToken, &ptr, &typeContext, pData); |
5395 | } |
5396 | |
5397 | moveToNextToken: |
5398 | // move to next argument token |
5399 | IfFailThrowBF(ptr.SkipExactlyOne(), BFA_BAD_SIGNATURE, pModule); |
5400 | } |
5401 | |
5402 | if (!IsZapped() && !IsCompilationProcess() && !HaveValueTypeParametersBeenWalked()) |
5403 | { |
5404 | SetValueTypeParametersWalked(); |
5405 | } |
5406 | } |
5407 | |
5408 | |
5409 | #ifdef FEATURE_TYPEEQUIVALENCE |
5410 | static void CheckForEquivalenceAndLoadType(Module *pModule, mdToken token, Module *pDefModule, mdToken defToken, const SigParser *ptr, SigTypeContext *pTypeContext, void *pData) |
5411 | { |
5412 | CONTRACTL |
5413 | { |
5414 | THROWS; |
5415 | GC_TRIGGERS; |
5416 | SO_INTOLERANT; |
5417 | } |
5418 | CONTRACTL_END; |
5419 | |
5420 | BOOL *pHasEquivalentParam = (BOOL *)pData; |
5421 | |
5422 | if (IsTypeDefEquivalent(defToken, pDefModule)) |
5423 | { |
5424 | *pHasEquivalentParam = TRUE; |
5425 | SigPointer sigPtr(*ptr); |
5426 | TypeHandle th = sigPtr.GetTypeHandleThrowing(pModule, pTypeContext); |
5427 | } |
5428 | } |
5429 | #endif // FEATURE_TYPEEQUIVALENCE |
5430 | |
5431 | BOOL MethodDesc::HasTypeEquivalentStructParameters() |
5432 | { |
5433 | #ifdef FEATURE_TYPEEQUIVALENCE |
5434 | CONTRACTL |
5435 | { |
5436 | THROWS; |
5437 | GC_TRIGGERS; |
5438 | MODE_ANY; |
5439 | } |
5440 | CONTRACTL_END; |
5441 | |
5442 | BOOL fHasTypeEquivalentStructParameters = FALSE; |
5443 | if (DoesNotHaveEquivalentValuetypeParameters()) |
5444 | return FALSE; |
5445 | |
5446 | WalkValueTypeParameters(this->GetMethodTable(), CheckForEquivalenceAndLoadType, &fHasTypeEquivalentStructParameters); |
5447 | |
5448 | if (!fHasTypeEquivalentStructParameters && !IsZapped()) |
5449 | SetDoesNotHaveEquivalentValuetypeParameters(); |
5450 | |
5451 | return fHasTypeEquivalentStructParameters; |
5452 | |
5453 | #else |
5454 | LIMITED_METHOD_CONTRACT; |
5455 | return FALSE; |
5456 | |
5457 | #endif // FEATURE_TYPEEQUIVALENCE |
5458 | } |
5459 | |
5460 | |
5461 | PrecodeType MethodDesc::GetPrecodeType() |
5462 | { |
5463 | LIMITED_METHOD_CONTRACT; |
5464 | |
5465 | PrecodeType precodeType = PRECODE_INVALID; |
5466 | |
5467 | #ifdef HAS_FIXUP_PRECODE |
5468 | if (!RequiresMethodDescCallingConvention()) |
5469 | { |
5470 | // Use the more efficient fixup precode if possible |
5471 | precodeType = PRECODE_FIXUP; |
5472 | } |
5473 | else |
5474 | #endif // HAS_FIXUP_PRECODE |
5475 | { |
5476 | precodeType = PRECODE_STUB; |
5477 | } |
5478 | |
5479 | return precodeType; |
5480 | } |
5481 | |
5482 | #endif // !DACCESS_COMPILE |
5483 | |
5484 | #ifdef FEATURE_COMINTEROP |
5485 | #ifndef DACCESS_COMPILE |
5486 | void ComPlusCallMethodDesc::InitRetThunk() |
5487 | { |
5488 | WRAPPER_NO_CONTRACT; |
5489 | |
5490 | #ifdef _TARGET_X86_ |
5491 | if (m_pComPlusCallInfo->m_pRetThunk != NULL) |
5492 | return; |
5493 | |
5494 | // Record the fact that we are writting into the ComPlusCallMethodDesc |
5495 | g_IBCLogger.LogMethodDescAccess(this); |
5496 | |
5497 | UINT numStackBytes = CbStackPop(); |
5498 | |
5499 | LPVOID pRetThunk = ComPlusCall::GetRetThunk(numStackBytes); |
5500 | |
5501 | FastInterlockCompareExchangePointer<void *>(&m_pComPlusCallInfo->m_pRetThunk, pRetThunk, NULL); |
5502 | #endif // _TARGET_X86_ |
5503 | } |
5504 | #endif //!DACCESS_COMPILE |
5505 | #endif // FEATURE_COMINTEROP |
5506 | |
5507 | #ifndef DACCESS_COMPILE |
5508 | void MethodDesc::PrepareForUseAsADependencyOfANativeImageWorker() |
5509 | { |
5510 | STANDARD_VM_CONTRACT; |
5511 | |
5512 | // This function ensures that a method is ready for use as a dependency of a native image |
5513 | // The current requirement is only that valuetypes can be resolved to their type defs as much |
5514 | // as is possible. (If the method is actually called, then this will not throw, but there |
5515 | // are cases where we call this method and we are unaware if this method will actually be called |
5516 | // or accessed as a native image dependency. This explains the contract (STANDARD_VM_CONTRACT) |
5517 | // - This method should be callable only when general purpose VM code can be called |
5518 | // , as well as the TRY/CATCH. |
5519 | // - This function should not introduce failures |
5520 | |
5521 | EX_TRY |
5522 | { |
5523 | WalkValueTypeParameters(this->GetMethodTable(), NULL, NULL); |
5524 | } |
5525 | EX_CATCH |
5526 | { |
5527 | } |
5528 | EX_END_CATCH(RethrowTerminalExceptions); |
5529 | _ASSERTE(IsZapped() || HaveValueTypeParametersBeenWalked()); |
5530 | } |
5531 | #endif //!DACCESS_COMPILE |
5532 | |
5533 | #ifdef _MSC_VER |
5534 | #pragma warning(pop) |
5535 | #endif // _MSC_VER: warning C4244 |
5536 | |