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