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
48GVAL_IMPL(DWORD, g_MiniMetaDataBuffMaxSize);
49GVAL_IMPL(TADDR, g_MiniMetaDataBuffAddress);
50#endif // FEATURE_MINIMETADATA_IN_TRIAGEDUMPS
51
52// forward decl
53bool 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//
67static_assert_no_msg((sizeof(MethodDescChunk) & MethodDesc::ALIGNMENT_MASK) == 0);
68static_assert_no_msg((sizeof(MethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0);
69static_assert_no_msg((sizeof(FCallMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0);
70static_assert_no_msg((sizeof(NDirectMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0);
71static_assert_no_msg((sizeof(EEImplMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0);
72static_assert_no_msg((sizeof(ArrayMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0);
73static_assert_no_msg((sizeof(ComPlusCallMethodDesc) & MethodDesc::ALIGNMENT_MASK) == 0);
74static_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
86const 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
101class ArgIteratorBaseForPInvoke : public ArgIteratorBase
102{
103protected:
104 FORCEINLINE BOOL IsRegPassedStruct(MethodTable* pMT)
105 {
106 return pMT->GetLayoutInfo()->IsNativeStructPassedInRegisters();
107 }
108};
109
110class PInvokeArgIterator : public ArgIteratorTemplate<ArgIteratorBaseForPInvoke>
111{
112public:
113 PInvokeArgIterator(MetaSig* pSig)
114 {
115 m_pSig = pSig;
116 }
117};
118
119
120//*******************************************************************************
121SIZE_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
143BOOL 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
177VOID 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//*******************************************************************************
201CHECK MethodDesc::CheckActivated()
202{
203 WRAPPER_NO_CONTRACT;
204 CHECK(GetModule()->CheckActivated());
205 CHECK_OK;
206}
207
208//*******************************************************************************
209BaseDomain *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//*******************************************************************************
227LoaderAllocator * MethodDesc::GetLoaderAllocatorForCode()
228{
229 if (IsLCGMethod())
230 {
231 return ::GetAppDomain()->GetLoaderAllocator();
232 }
233 else
234 {
235 return GetLoaderAllocator();
236 }
237}
238
239
240//*******************************************************************************
241LoaderAllocator * 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//*******************************************************************************
257LPCUTF8 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//*******************************************************************************
267LPCUTF8 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 */
313VOID 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 */
330VOID 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 */
347VOID 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 */
365VOID 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//*******************************************************************************
391void 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//*******************************************************************************
420BOOL 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//*******************************************************************************
445void 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
487void 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//*******************************************************************************
511PCCOR_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
525Signature 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
540PCODE 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
569TADDR 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//*******************************************************************************
598PTR_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.
654COUNT_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
731DWORD 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//*******************************************************************************
755MethodTable * 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//*******************************************************************************
780Instantiation 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//*******************************************************************************
798BOOL 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//*******************************************************************************
809BOOL 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//*******************************************************************************
829Instantiation 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//*******************************************************************************
848Module *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//*******************************************************************************
882BOOL 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//*******************************************************************************
917BOOL 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.
946WORD 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
993WORD 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.
1045PCODE 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//*******************************************************************************
1073TADDR 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//*******************************************************************************
1085PCODE 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//*******************************************************************************
1115BOOL MethodDesc::IsVoid()
1116{
1117 WRAPPER_NO_CONTRACT;
1118
1119 MetaSig sig(this);
1120 return sig.IsReturnTypeVoid();
1121}
1122
1123//*******************************************************************************
1124BOOL 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.
1136ULONG 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//*******************************************************************************
1180BOOL 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//*******************************************************************************
1197COR_ILMETHOD* MethodDesc::GetILHeader(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//*******************************************************************************
1241MetaSig::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//*******************************************************************************
1339LONG 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//*******************************************************************************
1361WORD 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//*******************************************************************************
1396DWORD 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//*******************************************************************************
1423DWORD 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//*******************************************************************************
1444Module* 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//*******************************************************************************
1476Module* 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//*******************************************************************************
1505Module *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//*******************************************************************************
1520Module *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.
1535BOOL 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//*******************************************************************************
1547BOOL MethodDesc::IsWrapperStub()
1548{
1549 WRAPPER_NO_CONTRACT;
1550 SUPPORTS_DAC;
1551
1552 return (IsUnboxingStub() || IsInstantiatingStub());
1553}
1554
1555#ifndef DACCESS_COMPILE
1556//*******************************************************************************
1557
1558MethodDesc *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
1587MethodDesc *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//*******************************************************************************
1617BOOL 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//*******************************************************************************
1629BOOL 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>
1647BOOL 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
1662BOOL 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?
1672BOOL 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//*******************************************************************************
1684BOOL 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
1698MethodDesc* 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//*******************************************************************************
1731BOOL 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//*******************************************************************************
1745BOOL 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//*******************************************************************************
1758UINT MethodDesc::SizeOfArgStack()
1759{
1760 WRAPPER_NO_CONTRACT;
1761 MetaSig msig(this);
1762 ArgIterator argit(&msig);
1763 return argit.SizeOfArgStack();
1764}
1765
1766
1767UINT 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//*******************************************************************************
1781UINT 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
1799MethodDesc* 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//*******************************************************************************
1821MethodDescChunk *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.
1921MethodDesc* 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//*******************************************************************************
1984PCODE 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.
2020PCODE 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//*******************************************************************************
2070PCODE 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
2121PCODE 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//*******************************************************************************
2208PCODE 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
2237MethodDesc* 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//*******************************************************************************
2281BOOL 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//*******************************************************************************
2297BOOL 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//*******************************************************************************
2321void 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//*******************************************************************************
2367Dictionary* MethodDesc::GetMethodDictionary()
2368{
2369 WRAPPER_NO_CONTRACT;
2370
2371 return
2372 (GetClassification() == mcInstantiated)
2373 ? (Dictionary*) (AsInstantiatedMethodDesc()->IMD_GetMethodDictionary())
2374 : NULL;
2375}
2376
2377//*******************************************************************************
2378DictionaryLayout* 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//*******************************************************************************
2391MethodImpl *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//*******************************************************************************
2411BOOL 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//*******************************************************************************
2424BOOL 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//*******************************************************************************
2463BOOL 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//*******************************************************************************
2514void 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//*******************************************************************************
2736bool 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//*******************************************************************************
2866BOOL 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//
2919bool
2920FixupSignatureContainingInternalTypesParseType(
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//
3002bool
3003FixupSignatureContainingInternalTypes(
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//
3059void
3060RestoreSignatureContainingInternalTypesParseType(
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//
3122static
3123void
3124RestoreSignatureContainingInternalTypes(
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
3173void 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
3195void 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//
3210void
3211MethodDesc::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//*******************************************************************************
3513Precode* 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
3524Precode* 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//*******************************************************************************
3540void 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//*******************************************************************************
3561SIZE_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//*******************************************************************************
3593void 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//*******************************************************************************
3712void 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//*******************************************************************************
3779int __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//*******************************************************************************
3800ZapStoredStructure * 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
3864BOOL 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
3874void 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//*******************************************************************************
3937void 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//*******************************************************************************
4017void MethodDesc::CheckRestore(ClassLoadLevel level)
4018{
4019 LIMITED_METHOD_CONTRACT;
4020}
4021#endif // !FEATURE_PREJIT
4022
4023// static
4024MethodDesc* 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//*******************************************************************************
4059TADDR 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//*******************************************************************************
4074BOOL 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
4105BOOL 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//*******************************************************************************
4147BOOL MethodDesc::IsRestored_NoLogging()
4148{
4149 LIMITED_METHOD_CONTRACT;
4150 return TRUE;
4151}
4152//*******************************************************************************
4153BOOL 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>
4166static 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}
4186c_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>
4197static 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}
4225c_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>
4237struct 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}
4255c_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
4267typedef DPTR(struct CentralJumpCode) PTR_CentralJumpCode;
4268#define TEP_CENTRAL_JUMP_SIZE sizeof(c_CentralJumpCode)
4269static_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
4302static 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
4316static 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
4338BOOL 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//*******************************************************************************
4462SIZE_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
4482TADDR 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//*******************************************************************************
4589PCODE 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
4617PCODE 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//*******************************************************************************
4664void 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//*******************************************************************************
4693void 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//*******************************************************************************
4715void 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//*******************************************************************************
4736Precode* 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//*******************************************************************************
4802BOOL 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//*******************************************************************************
4842BOOL 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//*******************************************************************************
4881void 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
4914FARPROC 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//*******************************************************************************
4960LPVOID 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//*******************************************************************************
5023void 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//*******************************************************************************
5054void MethodDesc::ComputeSuppressUnmanagedCodeAccessAttr(IMDInternalImport *pImport)
5055{
5056 CONTRACTL
5057 {
5058 THROWS;
5059 GC_NOTRIGGER;
5060 FORBID_FAULT;
5061 }
5062 CONTRACTL_END;
5063
5064}
5065
5066//*******************************************************************************
5067BOOL 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//*******************************************************************************
5092void 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//*******************************************************************************
5144void
5145MethodDesc::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//*******************************************************************************
5214void
5215StoredSigMethodDesc::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
5216{
5217 SUPPORTS_DAC;
5218 // 'this' already done, see below.
5219 DacEnumMemoryRegion(GetSigRVA(), m_cSig);
5220}
5221
5222//*******************************************************************************
5223void
5224MethodDescChunk::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//*******************************************************************************
5293MethodDesc *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
5307PTR_LoaderAllocator MethodDesc::GetLoaderAllocator()
5308{
5309 WRAPPER_NO_CONTRACT;
5310 return GetLoaderModule()->GetLoaderAllocator();
5311}
5312
5313#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
5314REFLECTMETHODREF 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
5342typedef void (*WalkValueTypeParameterFnPtr)(Module *pModule, mdToken token, Module *pDefModule, mdToken tkDefToken, SigPointer *ptr, SigTypeContext *pTypeContext, void *pData);
5343
5344void 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
5397moveToNextToken:
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
5410static 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
5431BOOL 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
5461PrecodeType 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
5486void 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
5508void 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