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 **
6 ** File:    callhelpers.h
7 ** Purpose: Provides helpers for making managed calls
8 **
9 
10 ===========================================================*/
11 #ifndef __CALLHELPERS_H__
12 #define __CALLHELPERS_H__
13 
14 struct CallDescrData
15 {
16     //
17     // Input arguments
18     //
19     LPVOID                      pSrc;
20     UINT32                      numStackSlots;
21 #ifdef CALLDESCR_ARGREGS
22     const ArgumentRegisters *   pArgumentRegisters;
23 #endif
24 #ifdef CALLDESCR_FPARGREGS
25     const FloatArgumentRegisters * pFloatArgumentRegisters;
26 #endif
27 #ifdef CALLDESCR_REGTYPEMAP
28     UINT64                      dwRegTypeMap;
29 #endif
30     UINT32                      fpReturnSize;
31     PCODE                       pTarget;
32 
33     //
34     // Return value
35     //
36 #ifdef ENREGISTERED_RETURNTYPE_MAXSIZE
37     // Use UINT64 to ensure proper alignment
38     UINT64 returnValue[ENREGISTERED_RETURNTYPE_MAXSIZE / sizeof(UINT64)];
39 #else
40     UINT64 returnValue;
41 #endif
42 };
43 
44 #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
45 
46 extern "C" void STDCALL CallDescrWorkerInternal(CallDescrData * pCallDescrData);
47 
48 #if !defined(_WIN64) && defined(_DEBUG)
49 void CallDescrWorker(CallDescrData * pCallDescrData);
50 #else
51 #define CallDescrWorker(pCallDescrData) CallDescrWorkerInternal(pCallDescrData)
52 #endif
53 
54 void CallDescrWorkerWithHandler(
55                 CallDescrData *   pCallDescrData,
56                 BOOL              fCriticalCall = FALSE);
57 
58 void DispatchCall(
59                 CallDescrData *   pCallDescrData,
60                 OBJECTREF *             pRefException,
61                 ContextTransitionFrame* pFrame = NULL
62 #ifdef FEATURE_CORRUPTING_EXCEPTIONS
63                 , CorruptionSeverity *  pSeverity = NULL
64 #endif // FEATURE_CORRUPTING_EXCEPTIONS
65                 );
66 
67 // Helper for VM->managed calls with simple signatures.
68 void * DispatchCallSimple(
69                     SIZE_T *pSrc,
70                     DWORD numStackSlotsToCopy,
71                     PCODE pTargetAddress,
72                     DWORD dwDispatchCallSimpleFlags);
73 
74 bool IsCerRootMethod(MethodDesc *pMD);
75 
76 class MethodDescCallSite
77 {
78 private:
79     MethodDesc* m_pMD;
80     PCODE       m_pCallTarget;
81     MetaSig     m_methodSig;
82     ArgIterator m_argIt;
83 
84 #ifdef _DEBUG
LogWeakAssert()85     __declspec(noinline) void LogWeakAssert()
86     {
87         LIMITED_METHOD_CONTRACT;
88         LOG((LF_ASSERT, LL_WARNING, "%s::%s\n", m_pMD->m_pszDebugClassName, m_pMD->m_pszDebugMethodName));
89     }
90 #endif // _DEBUG
91 
DefaultInit(OBJECTREF * porProtectedThis)92     void DefaultInit(OBJECTREF* porProtectedThis)
93     {
94         CONTRACTL
95         {
96             MODE_ANY;
97             GC_TRIGGERS;
98             THROWS;
99         }
100         CONTRACTL_END;
101 
102 #ifdef _DEBUG
103         //
104         // Make sure we are passing in a 'this' if and only if it is required
105         //
106         if (m_pMD->IsVtableMethod())
107         {
108             CONSISTENCY_CHECK_MSG(NULL != porProtectedThis, "You did not pass in the 'this' object for a vtable method");
109         }
110         else
111         {
112             if (NULL != porProtectedThis)
113             {
114                 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnUnneededThis))
115                 {
116                     CONSISTENCY_CHECK_MSG(NULL == porProtectedThis, "You passed in a 'this' object to a non-vtable method.");
117                 }
118                 else
119                 {
120                     LogWeakAssert();
121                 }
122 
123             }
124         }
125 #endif // _DEBUG
126 
127         m_pCallTarget = m_pMD->GetCallTarget(porProtectedThis);
128 
129         m_argIt.ForceSigWalk();
130     }
131 
132 #ifdef FEATURE_INTERPRETER
133 public:
134     ARG_SLOT CallTargetWorker(const ARG_SLOT *pArguments, bool transitionToPreemptive = false);
135 #else
136     ARG_SLOT CallTargetWorker(const ARG_SLOT *pArguments);
137 #endif
138 
139 public:
140     // Used to avoid touching metadata for mscorlib methods.
141     // instance methods must pass in the 'this' object
142     // static methods must pass null
143     MethodDescCallSite(BinderMethodID id, OBJECTREF* porProtectedThis = NULL) :
m_pMD(MscorlibBinder::GetMethod (id))144         m_pMD(
145             MscorlibBinder::GetMethod(id)
146             ),
147         m_methodSig(id),
148         m_argIt(&m_methodSig)
149     {
150         CONTRACTL
151         {
152             THROWS;
153             GC_TRIGGERS;
154             MODE_COOPERATIVE;
155         }
156         CONTRACTL_END;
157         DefaultInit(porProtectedThis);
158     }
159 
160     // Used to avoid touching metadata for mscorlib methods.
161     // instance methods must pass in the 'this' object
162     // static methods must pass null
MethodDescCallSite(BinderMethodID id,OBJECTHANDLE hThis)163     MethodDescCallSite(BinderMethodID id, OBJECTHANDLE hThis) :
164         m_pMD(
165             MscorlibBinder::GetMethod(id)
166             ),
167         m_methodSig(id),
168         m_argIt(&m_methodSig)
169     {
170         WRAPPER_NO_CONTRACT;
171 
172         DefaultInit((OBJECTREF*)hThis);
173     }
174 
175     // instance methods must pass in the 'this' object
176     // static methods must pass null
177     MethodDescCallSite(MethodDesc* pMD, OBJECTREF* porProtectedThis = NULL) :
m_pMD(pMD)178         m_pMD(pMD),
179         m_methodSig(pMD),
180         m_argIt(&m_methodSig)
181     {
182         CONTRACTL
183         {
184             THROWS;
185             GC_TRIGGERS;
186             MODE_COOPERATIVE;
187         }
188         CONTRACTL_END;
189 
190         if (porProtectedThis == NULL)
191         {
192             // We don't have a "this" pointer - ensure that we have activated the containing module
193             m_pMD->EnsureActive();
194         }
195 
196         DefaultInit(porProtectedThis);
197     }
198 
199     // instance methods must pass in the 'this' object
200     // static methods must pass null
MethodDescCallSite(MethodDesc * pMD,OBJECTHANDLE hThis)201     MethodDescCallSite(MethodDesc* pMD, OBJECTHANDLE hThis) :
202         m_pMD(pMD),
203         m_methodSig(pMD),
204         m_argIt(&m_methodSig)
205     {
206         WRAPPER_NO_CONTRACT;
207 
208         if (hThis == NULL)
209         {
210             // We don't have a "this" pointer - ensure that we have activated the containing module
211             m_pMD->EnsureActive();
212         }
213 
214         DefaultInit((OBJECTREF*)hThis);
215     }
216 
217     // instance methods must pass in the 'this' object
218     // static methods must pass null
219     MethodDescCallSite(MethodDesc* pMD, LPHARDCODEDMETASIG pwzSignature, OBJECTREF* porProtectedThis = NULL) :
m_pMD(pMD)220         m_pMD(pMD),
221         m_methodSig(pwzSignature),
222         m_argIt(&m_methodSig)
223     {
224         WRAPPER_NO_CONTRACT;
225 
226         if (porProtectedThis == NULL)
227         {
228             // We don't have a "this" pointer - ensure that we have activated the containing module
229             m_pMD->EnsureActive();
230         }
231 
232         DefaultInit(porProtectedThis);
233     }
234 
235     //
236     // Only use this constructor if you're certain you know where
237     // you're going and it cannot be affected by generics/virtual
238     // dispatch/etc..
239     //
MethodDescCallSite(MethodDesc * pMD,PCODE pCallTarget)240     MethodDescCallSite(MethodDesc* pMD, PCODE pCallTarget) :
241         m_pMD(pMD),
242         m_pCallTarget(pCallTarget),
243         m_methodSig(pMD),
244         m_argIt(&m_methodSig)
245     {
246         CONTRACTL
247         {
248             THROWS;
249             GC_TRIGGERS;
250             MODE_ANY;
251         }
252         CONTRACTL_END;
253 
254         m_pMD->EnsureActive();
255 
256         m_argIt.ForceSigWalk();
257     }
258 
259 #ifdef FEATURE_INTERPRETER
MethodDescCallSite(MethodDesc * pMD,MetaSig * pSig,PCODE pCallTarget)260     MethodDescCallSite(MethodDesc* pMD, MetaSig* pSig, PCODE pCallTarget) :
261         m_pMD(pMD),
262         m_pCallTarget(pCallTarget),
263         m_methodSig(*pSig),
264         m_argIt(pSig)
265     {
266         CONTRACTL
267         {
268             THROWS;
269             GC_TRIGGERS;
270             MODE_ANY;
271         }
272         CONTRACTL_END;
273 
274         m_pMD->EnsureActive();
275 
276         m_argIt.ForceSigWalk();
277     }
278 #endif // FEATURE_INTERPRETER
279 
GetMetaSig()280     MetaSig* GetMetaSig()
281     {
282         return &m_methodSig;
283     }
284 
285     //
286     // Call_RetXXX definition macros:
287     //
288     // These macros provide type protection for the return value from calls to managed
289     // code. This should help to prevent errors like what we're seeing on 64bit where
290     // the JIT64 is returning the BOOL as 1byte with the rest of the ARG_SLOT still
291     // polluted by the remnants of its last value. Previously we would cast to a (BOOL)
292     // and end up having if((BOOL)pMD->Call(...)) statements always being true.
293     //
294 
295     // Use OTHER_ELEMENT_TYPE when defining CallXXX_RetXXX variations where the return type
296     // is not in CorElementType (like LPVOID) or the return type can be one of a number of
297     // CorElementTypes, like XXX_RetObjPtr which is used for all kinds of Object* return
298     // types, or XXX_RetArgSlot which is unspecified.
299 #define OTHER_ELEMENT_TYPE -1
300 
301 // Note "permitvaluetypes" is not really used for anything
302 #define MDCALLDEF(wrappedmethod, permitvaluetypes, ext, rettype, eltype)            \
303         FORCEINLINE rettype wrappedmethod##ext (const ARG_SLOT* pArguments)         \
304         {                                                                           \
305             WRAPPER_NO_CONTRACT;                                                    \
306             {                                                                       \
307                 GCX_FORBID();  /* arg array is not protected */                     \
308                 CONSISTENCY_CHECK(eltype == OTHER_ELEMENT_TYPE ||                   \
309                                   eltype == m_methodSig.GetReturnType());           \
310             }                                                                       \
311             ARG_SLOT retval;                                                        \
312             retval = CallTargetWorker(pArguments);                                  \
313             return *(rettype *)ArgSlotEndianessFixup(&retval, sizeof(rettype));     \
314         }
315 
316 #define MDCALLDEF_REFTYPE(wrappedmethod,  permitvaluetypes, ext, ptrtype, reftype)              \
317         FORCEINLINE reftype wrappedmethod##ext (const ARG_SLOT* pArguments)                     \
318         {                                                                                       \
319             WRAPPER_NO_CONTRACT;                                                                \
320             {                                                                                   \
321                 GCX_FORBID();  /* arg array is not protected */                                 \
322                 CONSISTENCY_CHECK(MetaSig::RETOBJ == m_pMD->ReturnsObject(true));               \
323             }                                                                                   \
324             ARG_SLOT retval;                                                                    \
325             retval = CallTargetWorker(pArguments);                                              \
326             return ObjectTo##reftype(*(ptrtype *)                                               \
327                         ArgSlotEndianessFixup(&retval, sizeof(ptrtype)));                       \
328         }
329 
330 
331     // The MDCALLDEF_XXX_VOID macros take a customized assertion and calls the worker without
332     // returning a value, this is the macro that _should_ be used to define the CallXXX variations
333     // (without _RetXXX extension) so that misuse will be caught at compile time.
334 
335 #define MDCALLDEF_VOID(wrappedmethod, permitvaluetypes)                 \
336         FORCEINLINE void wrappedmethod (const ARG_SLOT* pArguments)     \
337         {                                                               \
338             WRAPPER_NO_CONTRACT;                                        \
339             CallTargetWorker(pArguments);                               \
340         }
341 
342 #define MDCALLDEFF_STD_RETTYPES(wrappedmethod,permitvaluetypes)                                         \
343         MDCALLDEF_VOID(wrappedmethod,permitvaluetypes)                                                  \
344         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetBool,   CLR_BOOL,      ELEMENT_TYPE_BOOLEAN)  \
345         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetChar,   CLR_CHAR,      ELEMENT_TYPE_CHAR)     \
346         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetI1,     CLR_I1,        ELEMENT_TYPE_I1)       \
347         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetU1,     CLR_U1,        ELEMENT_TYPE_U1)       \
348         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetI2,     CLR_I2,        ELEMENT_TYPE_I2)       \
349         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetU2,     CLR_U2,        ELEMENT_TYPE_U2)       \
350         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetI4,     CLR_I4,        ELEMENT_TYPE_I4)       \
351         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetU4,     CLR_U4,        ELEMENT_TYPE_U4)       \
352         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetI8,     CLR_I8,        ELEMENT_TYPE_I8)       \
353         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetU8,     CLR_U8,        ELEMENT_TYPE_U8)       \
354         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetR4,     CLR_R4,        ELEMENT_TYPE_R4)       \
355         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetR8,     CLR_R8,        ELEMENT_TYPE_R8)       \
356         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetI,      CLR_I,         ELEMENT_TYPE_I)        \
357         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetU,      CLR_U,         ELEMENT_TYPE_U)        \
358         MDCALLDEF(wrappedmethod,permitvaluetypes,     _RetArgSlot,ARG_SLOT,      OTHER_ELEMENT_TYPE)
359 
360 
361     public:
362         //--------------------------------------------------------------------
363         // Invoke a method. Arguments are packaged up in right->left order
364         // which each array element corresponding to one argument.
365         //
366         // Can throw a COM+ exception.
367         //
368         // All the appropriate "virtual" semantics (include thunking like context
369         // proxies) occurs inside Call.
370         //
371         // Call should never be called on interface MethodDesc's. The exception
372         // to this rule is when calling on a COM object. In that case the call
373         // needs to go through an interface MD and CallOnInterface is there
374         // for that.
375         //--------------------------------------------------------------------
376 
377         //
378         // NOTE on Call methods
379         //  MethodDesc::Call uses a virtual portable calling convention
380         //  Arguments are put left-to-right in the ARG_SLOT array, in the following order:
381         //    - this pointer (if any)
382         //    - return buffer address (if signature.HasRetBuffArg())
383         //    - all other fixed arguments (left-to-right)
384         //  Vararg is not supported yet.
385         //
386         //  The args that fit in an ARG_SLOT are inline. The ones that don't fit in an ARG_SLOT are allocated somewhere else
387         //      (usually on the stack) and a pointer to that area is put in the corresponding ARG_SLOT
388         // ARG_SLOT is guaranteed to be big enough to fit all basic types and pointer types. Basically, one has
389         //      to check only for aggregate value-types and 80-bit floating point values or greater.
390         //
391         // Calls with value type parameters must use the CallXXXWithValueTypes
392         // variants.  Using the WithValueTypes variant indicates that the caller
393         // has gc-protected the contents of value types of size greater than
394         // ENREGISTERED_PARAMTYPE_MAXSIZE (when it is defined, which is currently
395         // only on AMD64).  ProtectValueClassFrame can be used to accomplish this,
396         // see CallDescrWithObjectArray in stackbuildersink.cpp.
397         //
398         // Not all usages of MethodDesc::CallXXX have been ported to the new convention. The end goal is to port them all and get
399         //      rid of the non-portable BYTE* version.
400         //
401         // We have converted all usage of CallXXX in the runtime to some more specific CallXXX_RetXXX type (CallXXX usages
402         // where the return value is unused remain CallXXX). In most cases we were able to use something more specific than
403         // CallXXX_RetArgSlot (which is the equivalent of the old behavior). It is recommended that as you add usages of
404         // CallXXX in the future you try to avoid CallXXX_RetArgSlot whenever possible.
405         //
406         // If the return value is unused you can use the CallXXX syntax which has a void return and is not protected
407         // by any assertions around the return value type. This should protect against people trying to use the old
408         // semantics of ->Call as if they try to assign the return value to something they'll get a compile time error.
409         //
410         // If you are unable to be sure of the return type at runtime and are just blindly casting then continue to use
411         // CallXXX_RetArgSlot, Do not for instance use CallXXX_RetI4 as a mechanism to cast the result to an I4 as it will
412         // also try to assert the fact that the callee managed method actually does return an I4.
413         //
414 
415         // All forms of CallXXX should have at least the CallXXX_RetArgSlot definition which maps to the old behavior
416         //  -  MDCALL_ARG_____STD_RETTYPES includes CallXXX_RetArgSlot
417         //  -  MDCALL_ARG_SIG_STD_RETTYPES includes CallXXX_RetArgSlot
418 
419         // XXX Call_RetXXX(const ARG_SLOT* pArguments);
420         MDCALLDEFF_STD_RETTYPES(Call, FALSE)
421         MDCALLDEF(              Call, FALSE, _RetHR,        HRESULT,       OTHER_ELEMENT_TYPE)
422         MDCALLDEF(              Call, FALSE, _RetObjPtr,    Object*,       OTHER_ELEMENT_TYPE)
423         MDCALLDEF_REFTYPE(      Call, FALSE, _RetOBJECTREF, Object*,       OBJECTREF)
424         MDCALLDEF_REFTYPE(      Call, FALSE, _RetSTRINGREF, StringObject*, STRINGREF)
425         MDCALLDEF(              Call, FALSE, _RetLPVOID,    LPVOID,        OTHER_ELEMENT_TYPE)
426 
427         // XXX CallWithValueTypes_RetXXX(const ARG_SLOT* pArguments);
428         MDCALLDEF_VOID(     CallWithValueTypes, TRUE)
429         MDCALLDEF(          CallWithValueTypes, TRUE,   _RetArgSlot,    ARG_SLOT,   OTHER_ELEMENT_TYPE)
430         MDCALLDEF_REFTYPE(  CallWithValueTypes, TRUE,   _RetOBJECTREF,  Object*,    OBJECTREF)
431         MDCALLDEF(          CallWithValueTypes, TRUE,   _RetOleColor,   OLE_COLOR,  OTHER_ELEMENT_TYPE)
432 #undef OTHER_ELEMENT_TYPE
433 #undef MDCALL_ARG_SIG_STD_RETTYPES
434 #undef MDCALLDEF
435 #undef MDCALLDEF_REFTYPE
436 #undef MDCALLDEF_VOID
437 }; // MethodDescCallSite
438 
439 
440 #ifdef CALLDESCR_REGTYPEMAP
441 void FillInRegTypeMap(int argOffset, CorElementType typ, BYTE * pMap);
442 #endif // CALLDESCR_REGTYPEMAP
443 
444 
445 /***********************************************************************/
446 /* Macros used to indicate a call to managed code is starting/ending   */
447 /***********************************************************************/
448 
449 enum EEToManagedCallFlags
450 {
451     EEToManagedDefault                  = 0x0000,
452     EEToManagedCriticalCall             = 0x0001,
453 };
454 
455 #define BEGIN_CALL_TO_MANAGED()                                                 \
456     BEGIN_CALL_TO_MANAGEDEX(EEToManagedDefault)
457 
458 #define BEGIN_CALL_TO_MANAGEDEX(flags)                                          \
459 {                                                                               \
460     MAKE_CURRENT_THREAD_AVAILABLE();                                            \
461     DECLARE_CPFH_EH_RECORD(CURRENT_THREAD);                                     \
462     _ASSERTE(CURRENT_THREAD);                                                   \
463     _ASSERTE(!CURRENT_THREAD->IsAbortPrevented() ||                             \
464              CURRENT_THREAD->IsAbortCheckDisabled());                           \
465     _ASSERTE((CURRENT_THREAD->m_StateNC & Thread::TSNC_OwnsSpinLock) == 0);     \
466     /* This bit should never be set when we call into managed code.  The */     \
467     /* stack walking code explicitly clears this around any potential calls */  \
468     /* into managed code. */                                                    \
469     _ASSERTE(!IsStackWalkerThread());                                           \
470     /* If this isn't a critical transition, we need to check to see if a */     \
471     /* thread abort has been requested */                                       \
472     if (!(flags & EEToManagedCriticalCall))                                     \
473     {                                                                           \
474         TESTHOOKCALL(AppDomainCanBeUnloaded(CURRENT_THREAD->GetDomain()->GetId().m_dwId,FALSE)); \
475         if (CURRENT_THREAD->IsAbortRequested()) {                               \
476             CURRENT_THREAD->HandleThreadAbort();                                \
477         }                                                                       \
478     }                                                                           \
479     BEGIN_SO_TOLERANT_CODE(CURRENT_THREAD);                                     \
480     INSTALL_COMPLUS_EXCEPTION_HANDLER_NO_DECLARE();
481 
482 #define END_CALL_TO_MANAGED()                                                   \
483     UNINSTALL_COMPLUS_EXCEPTION_HANDLER();                                      \
484     END_SO_TOLERANT_CODE;                                                       \
485 }
486 
487 /***********************************************************************/
488 /* Macros that provide abstraction to the usage of DispatchCallSimple    */
489 /***********************************************************************/
490 
491 enum DispatchCallSimpleFlags
492 {
493     DispatchCallSimple_CriticalCall                  = 0x0001,
494     DispatchCallSimple_CatchHandlerFoundNotification = 0x0002,
495 };
496 
497 #define ARGHOLDER_TYPE LPVOID
498 #define OBJECTREF_TO_ARGHOLDER(x) (LPVOID)OBJECTREFToObject(x)
499 #define STRINGREF_TO_ARGHOLDER(x) (LPVOID)STRINGREFToObject(x)
500 #define PTR_TO_ARGHOLDER(x) (LPVOID)x
501 #define DWORD_TO_ARGHOLDER(x)   (LPVOID)(SIZE_T)x
502 
503 #define INIT_VARIABLES(count)                               \
504         DWORD   __numArgs = count;                          \
505         DWORD   __dwDispatchCallSimpleFlags = 0;            \
506 
507 #define PREPARE_NONVIRTUAL_CALLSITE(id) \
508         static PCODE s_pAddr##id = NULL;                    \
509         PCODE __pSlot = VolatileLoad(&s_pAddr##id);         \
510         if ( __pSlot == NULL )                              \
511         {                                                   \
512             MethodDesc *pMeth = MscorlibBinder::GetMethod(id);   \
513             _ASSERTE(pMeth);                                \
514             __pSlot = pMeth->GetMultiCallableAddrOfCode();  \
515             VolatileStore(&s_pAddr##id, __pSlot);           \
516         }
517 
518 #define PREPARE_VIRTUAL_CALLSITE(id, objref)                \
519         MethodDesc *__pMeth = MscorlibBinder::GetMethod(id);     \
520         PCODE __pSlot = __pMeth->GetCallTarget(&objref);
521 
522 #define PREPARE_VIRTUAL_CALLSITE_USING_METHODDESC(pMD, objref)                \
523         PCODE __pSlot = pMD->GetCallTarget(&objref);
524 
525 #ifdef _DEBUG
526 #define SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, methodTable)                     \
527         {                                                                        \
528             MethodDesc* __pMeth = methodTable->GetMethodDescForSlot(slotNumber); \
529             _ASSERTE(__pMeth);                                                   \
530             _ASSERTE(!__pMeth->HasMethodInstantiation() &&                       \
531                      !__pMeth->GetMethodTable()->IsInterface());                 \
532         }
533 #else
534 #define SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, objref)
535 #endif
536 
537 // a simple virtual method is a non-interface/non-generic method
538 // Note: objref has to be protected!
539 #define PREPARE_SIMPLE_VIRTUAL_CALLSITE(id, objref)                              \
540         static WORD s_slot##id = MethodTable::NO_SLOT;                           \
541         WORD __slot = VolatileLoad(&s_slot##id);                                 \
542         if (__slot == MethodTable::NO_SLOT)                                      \
543         {                                                                        \
544             MethodDesc *pMeth = MscorlibBinder::GetMethod(id);                        \
545             _ASSERTE(pMeth);                                                     \
546             __slot = pMeth->GetSlot();                                           \
547             VolatileStore(&s_slot##id, __slot);                                  \
548         }                                                                        \
549         PREPARE_SIMPLE_VIRTUAL_CALLSITE_USING_SLOT(__slot, objref)               \
550 
551 // a simple virtual method is a non-interface/non-generic method
552 #define PREPARE_SIMPLE_VIRTUAL_CALLSITE_USING_SLOT(slotNumber, objref)           \
553         MethodTable* __pObjMT = (objref)->GetMethodTable();                      \
554         SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, __pObjMT);                       \
555         PCODE __pSlot = (PCODE) __pObjMT->GetRestoredSlot(slotNumber);
556 
557 #define PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pMD)   \
558         PCODE __pSlot = (pMD)->GetSingleCallableAddrOfCode();
559 
560 #define PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pCode)       \
561         PCODE __pSlot = pCode;
562 
563 #define CRITICAL_CALLSITE                                   \
564         __dwDispatchCallSimpleFlags |= DispatchCallSimple_CriticalCall;
565 
566 // This flag should be used for callsites that catch exception up the stack inside the VM. The most common causes are
567 // such as END_DOMAIN_TRANSITION or EX_CATCH. Catching exceptions in the managed code is properly instrumented and
568 // does not need this notification.
569 //
570 // The notification is what enables both the managed �unhandled exception� dialog and the �user unhandled� dialog when
571 // JMC is turned on. Many things that VS puts up the unhandled exception dialog for are actually cases where the native
572 // exception was caught, for example catching exceptions at the thread base. JMC requires further accuracy - in that case
573 // VS is checking to see if an exception escaped particular ranges of managed code frames.
574 #define CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE            \
575         __dwDispatchCallSimpleFlags |= DispatchCallSimple_CatchHandlerFoundNotification;
576 
577 #define PERFORM_CALL    \
578         void * __retval = NULL;                         \
579         __retval = DispatchCallSimple(__pArgs,          \
580                            __numStackSlotsToCopy,       \
581                            __pSlot,                     \
582                            __dwDispatchCallSimpleFlags);\
583 
584 #ifdef CALLDESCR_ARGREGS
585 
586 #if defined(_TARGET_X86_)
587 
588 // Arguments on x86 are passed backward
589 #define ARGNUM_0    1
590 #define ARGNUM_1    0
591 #define ARGNUM_N(n)    __numArgs - n + 1
592 
593 #else
594 
595 #define ARGNUM_0    0
596 #define ARGNUM_1    1
597 #define ARGNUM_N(n)    n
598 
599 #endif
600 
601 #define PRECALL_PREP(args)  \
602         DWORD __numStackSlotsToCopy = (__numArgs > NUM_ARGUMENT_REGISTERS) ? (__numArgs - NUM_ARGUMENT_REGISTERS) : 0; \
603         SIZE_T * __pArgs = (SIZE_T *)args;
604 
605 #define DECLARE_ARGHOLDER_ARRAY(arg, count)             \
606         INIT_VARIABLES(count)                           \
607         ARGHOLDER_TYPE arg[(count <= NUM_ARGUMENT_REGISTERS ? NUM_ARGUMENT_REGISTERS : count)];
608 
609 #else   // CALLDESCR_ARGREGS
610 
611 #define ARGNUM_0    0
612 #define ARGNUM_1    1
613 #define ARGNUM_N(n)    n
614 
615 #define PRECALL_PREP(args)                              \
616         DWORD __numStackSlotsToCopy = (__numArgs > NUM_ARGUMENT_REGISTERS) ? __numArgs : NUM_ARGUMENT_REGISTERS; \
617         SIZE_T * __pArgs = (SIZE_T *)args;
618 
619 #define DECLARE_ARGHOLDER_ARRAY(arg, count)             \
620         INIT_VARIABLES(count)                           \
621         ARGHOLDER_TYPE arg[(count <= NUM_ARGUMENT_REGISTERS ? NUM_ARGUMENT_REGISTERS : count)];
622 
623 #endif  // CALLDESCR_ARGREGS
624 
625 
626 #define CALL_MANAGED_METHOD(ret, rettype, args)         \
627         PRECALL_PREP(args)                              \
628         PERFORM_CALL                                    \
629         ret = *(rettype *)(&__retval);
630 
631 #define CALL_MANAGED_METHOD_NORET(args)                 \
632         PRECALL_PREP(args)                              \
633         PERFORM_CALL
634 
635 #define CALL_MANAGED_METHOD_RETREF(ret, reftype, args)  \
636         PRECALL_PREP(args)                              \
637         PERFORM_CALL                                    \
638         ret = (reftype)ObjectToOBJECTREF((Object *)__retval);
639 
640 #define ARGNUM_2 ARGNUM_N(2)
641 #define ARGNUM_3 ARGNUM_N(3)
642 #define ARGNUM_4 ARGNUM_N(4)
643 #define ARGNUM_5 ARGNUM_N(5)
644 #define ARGNUM_6 ARGNUM_N(6)
645 #define ARGNUM_7 ARGNUM_N(7)
646 #define ARGNUM_8 ARGNUM_N(8)
647 
648 
649 void CallDefaultConstructor(OBJECTREF ref);
650 
651 #endif //!DACCESS_COMPILE && !CROSSGEN_COMPILE
652 
653 #endif // __CALLHELPERS_H__
654