// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. /*============================================================ ** ** File: callhelpers.h ** Purpose: Provides helpers for making managed calls ** ===========================================================*/ #ifndef __CALLHELPERS_H__ #define __CALLHELPERS_H__ struct CallDescrData { // // Input arguments // LPVOID pSrc; UINT32 numStackSlots; #ifdef CALLDESCR_ARGREGS const ArgumentRegisters * pArgumentRegisters; #endif #ifdef CALLDESCR_FPARGREGS const FloatArgumentRegisters * pFloatArgumentRegisters; #endif #ifdef CALLDESCR_REGTYPEMAP UINT64 dwRegTypeMap; #endif UINT32 fpReturnSize; PCODE pTarget; #ifdef CALLDESCR_RETBUFFARGREG // Pointer to return buffer arg location UINT64* pRetBuffArg; #endif // // Return value // #ifdef ENREGISTERED_RETURNTYPE_MAXSIZE // Use UINT64 to ensure proper alignment UINT64 returnValue[ENREGISTERED_RETURNTYPE_MAXSIZE / sizeof(UINT64)]; #else UINT64 returnValue; #endif }; #define NUMBER_RETURNVALUE_SLOTS (ENREGISTERED_RETURNTYPE_MAXSIZE / sizeof(ARG_SLOT)) #if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) extern "C" void STDCALL CallDescrWorkerInternal(CallDescrData * pCallDescrData); #if !defined(_WIN64) && defined(_DEBUG) void CallDescrWorker(CallDescrData * pCallDescrData); #else #define CallDescrWorker(pCallDescrData) CallDescrWorkerInternal(pCallDescrData) #endif void CallDescrWorkerWithHandler( CallDescrData * pCallDescrData, BOOL fCriticalCall = FALSE); void DispatchCall( CallDescrData * pCallDescrData, OBJECTREF * pRefException, ContextTransitionFrame* pFrame = NULL #ifdef FEATURE_CORRUPTING_EXCEPTIONS , CorruptionSeverity * pSeverity = NULL #endif // FEATURE_CORRUPTING_EXCEPTIONS ); // Helper for VM->managed calls with simple signatures. void * DispatchCallSimple( SIZE_T *pSrc, DWORD numStackSlotsToCopy, PCODE pTargetAddress, DWORD dwDispatchCallSimpleFlags); bool IsCerRootMethod(MethodDesc *pMD); class MethodDescCallSite { private: MethodDesc* m_pMD; PCODE m_pCallTarget; MetaSig m_methodSig; ArgIterator m_argIt; #ifdef _DEBUG __declspec(noinline) void LogWeakAssert() { LIMITED_METHOD_CONTRACT; LOG((LF_ASSERT, LL_WARNING, "%s::%s\n", m_pMD->m_pszDebugClassName, m_pMD->m_pszDebugMethodName)); } #endif // _DEBUG void DefaultInit(OBJECTREF* porProtectedThis) { CONTRACTL { MODE_ANY; GC_TRIGGERS; THROWS; } CONTRACTL_END; #ifdef _DEBUG // // Make sure we are passing in a 'this' if and only if it is required // if (m_pMD->IsVtableMethod()) { CONSISTENCY_CHECK_MSG(NULL != porProtectedThis, "You did not pass in the 'this' object for a vtable method"); } else { if (NULL != porProtectedThis) { if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertOnUnneededThis)) { CONSISTENCY_CHECK_MSG(NULL == porProtectedThis, "You passed in a 'this' object to a non-vtable method."); } else { LogWeakAssert(); } } } #endif // _DEBUG m_pCallTarget = m_pMD->GetCallTarget(porProtectedThis); m_argIt.ForceSigWalk(); } void DefaultInit(TypeHandle th) { CONTRACTL { MODE_ANY; GC_TRIGGERS; THROWS; } CONTRACTL_END; m_pCallTarget = m_pMD->GetCallTarget(NULL, th); m_argIt.ForceSigWalk(); } #ifdef FEATURE_INTERPRETER public: void CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *pReturnValue, int cbReturnValue, bool transitionToPreemptive = false); #else void CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT *pReturnValue, int cbReturnValue); #endif public: // Used to avoid touching metadata for mscorlib methods. // instance methods must pass in the 'this' object // static methods must pass null MethodDescCallSite(BinderMethodID id, OBJECTREF* porProtectedThis = NULL) : m_pMD( MscorlibBinder::GetMethod(id) ), m_methodSig(id), m_argIt(&m_methodSig) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; } CONTRACTL_END; DefaultInit(porProtectedThis); } // Used to avoid touching metadata for mscorlib methods. // instance methods must pass in the 'this' object // static methods must pass null MethodDescCallSite(BinderMethodID id, OBJECTHANDLE hThis) : m_pMD( MscorlibBinder::GetMethod(id) ), m_methodSig(id), m_argIt(&m_methodSig) { WRAPPER_NO_CONTRACT; DefaultInit((OBJECTREF*)hThis); } // instance methods must pass in the 'this' object // static methods must pass null MethodDescCallSite(MethodDesc* pMD, OBJECTREF* porProtectedThis = NULL) : m_pMD(pMD), m_methodSig(pMD), m_argIt(&m_methodSig) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; } CONTRACTL_END; if (porProtectedThis == NULL) { // We don't have a "this" pointer - ensure that we have activated the containing module m_pMD->EnsureActive(); } DefaultInit(porProtectedThis); } // instance methods must pass in the 'this' object // static methods must pass null MethodDescCallSite(MethodDesc* pMD, OBJECTHANDLE hThis) : m_pMD(pMD), m_methodSig(pMD), m_argIt(&m_methodSig) { WRAPPER_NO_CONTRACT; if (hThis == NULL) { // We don't have a "this" pointer - ensure that we have activated the containing module m_pMD->EnsureActive(); } DefaultInit((OBJECTREF*)hThis); } // instance methods must pass in the 'this' object // static methods must pass null MethodDescCallSite(MethodDesc* pMD, LPHARDCODEDMETASIG pwzSignature, OBJECTREF* porProtectedThis = NULL) : m_pMD(pMD), m_methodSig(pwzSignature), m_argIt(&m_methodSig) { WRAPPER_NO_CONTRACT; if (porProtectedThis == NULL) { // We don't have a "this" pointer - ensure that we have activated the containing module m_pMD->EnsureActive(); } DefaultInit(porProtectedThis); } MethodDescCallSite(MethodDesc* pMD, TypeHandle th) : m_pMD(pMD), m_methodSig(pMD, th), m_argIt(&m_methodSig) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_COOPERATIVE; } CONTRACTL_END; // We don't have a "this" pointer - ensure that we have activated the containing module m_pMD->EnsureActive(); DefaultInit(th); } // // Only use this constructor if you're certain you know where // you're going and it cannot be affected by generics/virtual // dispatch/etc.. // MethodDescCallSite(MethodDesc* pMD, PCODE pCallTarget) : m_pMD(pMD), m_pCallTarget(pCallTarget), m_methodSig(pMD), m_argIt(&m_methodSig) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; m_pMD->EnsureActive(); m_argIt.ForceSigWalk(); } #ifdef FEATURE_INTERPRETER MethodDescCallSite(MethodDesc* pMD, MetaSig* pSig, PCODE pCallTarget) : m_pMD(pMD), m_pCallTarget(pCallTarget), m_methodSig(*pSig), m_argIt(pSig) { CONTRACTL { THROWS; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; m_pMD->EnsureActive(); m_argIt.ForceSigWalk(); } #endif // FEATURE_INTERPRETER MetaSig* GetMetaSig() { return &m_methodSig; } // // Call_RetXXX definition macros: // // These macros provide type protection for the return value from calls to managed // code. This should help to prevent errors like what we're seeing on 64bit where // the JIT64 is returning the BOOL as 1byte with the rest of the ARG_SLOT still // polluted by the remnants of its last value. Previously we would cast to a (BOOL) // and end up having if((BOOL)pMD->Call(...)) statements always being true. // // Use OTHER_ELEMENT_TYPE when defining CallXXX_RetXXX variations where the return type // is not in CorElementType (like LPVOID) or the return type can be one of a number of // CorElementTypes, like XXX_RetObjPtr which is used for all kinds of Object* return // types, or XXX_RetArgSlot which is unspecified. #define OTHER_ELEMENT_TYPE -1 // Note "permitvaluetypes" is not really used for anything #define MDCALLDEF(wrappedmethod, permitvaluetypes, ext, rettype, eltype) \ FORCEINLINE rettype wrappedmethod##ext (const ARG_SLOT* pArguments) \ { \ WRAPPER_NO_CONTRACT; \ { \ GCX_FORBID(); /* arg array is not protected */ \ CONSISTENCY_CHECK(eltype == OTHER_ELEMENT_TYPE || \ eltype == m_methodSig.GetReturnType()); \ } \ ARG_SLOT retval; \ CallTargetWorker(pArguments, &retval, sizeof(retval)); \ return *(rettype *)ArgSlotEndianessFixup(&retval, sizeof(rettype)); \ } #define MDCALLDEF_ARGSLOT(wrappedmethod, ext) \ FORCEINLINE void wrappedmethod##ext (const ARG_SLOT* pArguments, ARG_SLOT *pReturnValue, int cbReturnValue) \ { \ WRAPPER_NO_CONTRACT; \ { \ GCX_FORBID(); /* arg array is not protected */ \ } \ CallTargetWorker(pArguments, pReturnValue, cbReturnValue); \ /* Bigendian layout not support */ \ } #define MDCALLDEF_REFTYPE(wrappedmethod, permitvaluetypes, ext, ptrtype, reftype) \ FORCEINLINE reftype wrappedmethod##ext (const ARG_SLOT* pArguments) \ { \ WRAPPER_NO_CONTRACT; \ { \ GCX_FORBID(); /* arg array is not protected */ \ CONSISTENCY_CHECK(MetaSig::RETOBJ == m_pMD->ReturnsObject(true)); \ } \ ARG_SLOT retval; \ CallTargetWorker(pArguments, &retval, sizeof(retval)); \ return ObjectTo##reftype(*(ptrtype *) \ ArgSlotEndianessFixup(&retval, sizeof(ptrtype))); \ } // The MDCALLDEF_XXX_VOID macros take a customized assertion and calls the worker without // returning a value, this is the macro that _should_ be used to define the CallXXX variations // (without _RetXXX extension) so that misuse will be caught at compile time. #define MDCALLDEF_VOID(wrappedmethod, permitvaluetypes) \ FORCEINLINE void wrappedmethod (const ARG_SLOT* pArguments) \ { \ WRAPPER_NO_CONTRACT; \ CallTargetWorker(pArguments, NULL, 0); \ } #define MDCALLDEFF_STD_RETTYPES(wrappedmethod,permitvaluetypes) \ MDCALLDEF_VOID(wrappedmethod,permitvaluetypes) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetBool, CLR_BOOL, ELEMENT_TYPE_BOOLEAN) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetChar, CLR_CHAR, ELEMENT_TYPE_CHAR) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI1, CLR_I1, ELEMENT_TYPE_I1) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU1, CLR_U1, ELEMENT_TYPE_U1) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI2, CLR_I2, ELEMENT_TYPE_I2) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU2, CLR_U2, ELEMENT_TYPE_U2) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI4, CLR_I4, ELEMENT_TYPE_I4) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU4, CLR_U4, ELEMENT_TYPE_U4) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI8, CLR_I8, ELEMENT_TYPE_I8) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU8, CLR_U8, ELEMENT_TYPE_U8) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetR4, CLR_R4, ELEMENT_TYPE_R4) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetR8, CLR_R8, ELEMENT_TYPE_R8) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetI, CLR_I, ELEMENT_TYPE_I) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetU, CLR_U, ELEMENT_TYPE_U) \ MDCALLDEF(wrappedmethod,permitvaluetypes, _RetArgSlot,ARG_SLOT, OTHER_ELEMENT_TYPE) public: //-------------------------------------------------------------------- // Invoke a method. Arguments are packaged up in right->left order // which each array element corresponding to one argument. // // Can throw a COM+ exception. // // All the appropriate "virtual" semantics (include thunking like context // proxies) occurs inside Call. // // Call should never be called on interface MethodDesc's. The exception // to this rule is when calling on a COM object. In that case the call // needs to go through an interface MD and CallOnInterface is there // for that. //-------------------------------------------------------------------- // // NOTE on Call methods // MethodDesc::Call uses a virtual portable calling convention // Arguments are put left-to-right in the ARG_SLOT array, in the following order: // - this pointer (if any) // - return buffer address (if signature.HasRetBuffArg()) // - all other fixed arguments (left-to-right) // Vararg is not supported yet. // // The args that fit in an ARG_SLOT are inline. The ones that don't fit in an ARG_SLOT are allocated somewhere else // (usually on the stack) and a pointer to that area is put in the corresponding ARG_SLOT // ARG_SLOT is guaranteed to be big enough to fit all basic types and pointer types. Basically, one has // to check only for aggregate value-types and 80-bit floating point values or greater. // // Calls with value type parameters must use the CallXXXWithValueTypes // variants. Using the WithValueTypes variant indicates that the caller // has gc-protected the contents of value types of size greater than // ENREGISTERED_PARAMTYPE_MAXSIZE (when it is defined, which is currently // only on AMD64). ProtectValueClassFrame can be used to accomplish this, // see CallDescrWithObjectArray in stackbuildersink.cpp. // // Not all usages of MethodDesc::CallXXX have been ported to the new convention. The end goal is to port them all and get // rid of the non-portable BYTE* version. // // We have converted all usage of CallXXX in the runtime to some more specific CallXXX_RetXXX type (CallXXX usages // where the return value is unused remain CallXXX). In most cases we were able to use something more specific than // CallXXX_RetArgSlot (which is the equivalent of the old behavior). It is recommended that as you add usages of // CallXXX in the future you try to avoid CallXXX_RetArgSlot whenever possible. // // If the return value is unused you can use the CallXXX syntax which has a void return and is not protected // by any assertions around the return value type. This should protect against people trying to use the old // semantics of ->Call as if they try to assign the return value to something they'll get a compile time error. // // If you are unable to be sure of the return type at runtime and are just blindly casting then continue to use // CallXXX_RetArgSlot, Do not for instance use CallXXX_RetI4 as a mechanism to cast the result to an I4 as it will // also try to assert the fact that the callee managed method actually does return an I4. // // All forms of CallXXX should have at least the CallXXX_RetArgSlot definition which maps to the old behavior // - MDCALL_ARG_____STD_RETTYPES includes CallXXX_RetArgSlot // - MDCALL_ARG_SIG_STD_RETTYPES includes CallXXX_RetArgSlot // XXX Call_RetXXX(const ARG_SLOT* pArguments); MDCALLDEFF_STD_RETTYPES(Call, FALSE) MDCALLDEF( Call, FALSE, _RetHR, HRESULT, OTHER_ELEMENT_TYPE) MDCALLDEF( Call, FALSE, _RetObjPtr, Object*, OTHER_ELEMENT_TYPE) MDCALLDEF_REFTYPE( Call, FALSE, _RetOBJECTREF, Object*, OBJECTREF) MDCALLDEF_REFTYPE( Call, FALSE, _RetSTRINGREF, StringObject*, STRINGREF) MDCALLDEF( Call, FALSE, _RetLPVOID, LPVOID, OTHER_ELEMENT_TYPE) // XXX CallWithValueTypes_RetXXX(const ARG_SLOT* pArguments); MDCALLDEF_VOID( CallWithValueTypes, TRUE) MDCALLDEF_ARGSLOT( CallWithValueTypes, _RetArgSlot) MDCALLDEF_REFTYPE( CallWithValueTypes, TRUE, _RetOBJECTREF, Object*, OBJECTREF) MDCALLDEF( CallWithValueTypes, TRUE, _RetOleColor, OLE_COLOR, OTHER_ELEMENT_TYPE) #undef OTHER_ELEMENT_TYPE #undef MDCALL_ARG_SIG_STD_RETTYPES #undef MDCALLDEF #undef MDCALLDEF_REFTYPE #undef MDCALLDEF_VOID }; // MethodDescCallSite #ifdef CALLDESCR_REGTYPEMAP void FillInRegTypeMap(int argOffset, CorElementType typ, BYTE * pMap); #endif // CALLDESCR_REGTYPEMAP /***********************************************************************/ /* Macros used to indicate a call to managed code is starting/ending */ /***********************************************************************/ #ifdef FEATURE_PAL // Install a native exception holder that doesn't catch any exceptions but its presence // in a stack range of native frames indicates that there was a call from native to // managed code. It is used by the DispatchManagedException to detect the case when // the INSTALL_MANAGED_EXCEPTION_DISPATCHER was not at the managed to native boundary. // For example in the PreStubWorker, which can be called from both native and managed // code. #define INSTALL_CALL_TO_MANAGED_EXCEPTION_HOLDER() \ NativeExceptionHolderNoCatch __exceptionHolder; \ __exceptionHolder.Push(); #else // FEATURE_PAL #define INSTALL_CALL_TO_MANAGED_EXCEPTION_HOLDER() #endif // FEATURE_PAL enum EEToManagedCallFlags { EEToManagedDefault = 0x0000, EEToManagedCriticalCall = 0x0001, }; #define BEGIN_CALL_TO_MANAGED() \ BEGIN_CALL_TO_MANAGEDEX(EEToManagedDefault) #define BEGIN_CALL_TO_MANAGEDEX(flags) \ { \ MAKE_CURRENT_THREAD_AVAILABLE(); \ DECLARE_CPFH_EH_RECORD(CURRENT_THREAD); \ _ASSERTE(CURRENT_THREAD); \ _ASSERTE(!CURRENT_THREAD->IsAbortPrevented() || \ CURRENT_THREAD->IsAbortCheckDisabled()); \ _ASSERTE((CURRENT_THREAD->m_StateNC & Thread::TSNC_OwnsSpinLock) == 0); \ /* This bit should never be set when we call into managed code. The */ \ /* stack walking code explicitly clears this around any potential calls */ \ /* into managed code. */ \ _ASSERTE(!IsStackWalkerThread()); \ /* If this isn't a critical transition, we need to check to see if a */ \ /* thread abort has been requested */ \ if (!(flags & EEToManagedCriticalCall)) \ { \ TESTHOOKCALL(AppDomainCanBeUnloaded(CURRENT_THREAD->GetDomain()->GetId().m_dwId,FALSE)); \ if (CURRENT_THREAD->IsAbortRequested()) { \ CURRENT_THREAD->HandleThreadAbort(); \ } \ } \ BEGIN_SO_TOLERANT_CODE(CURRENT_THREAD); \ INSTALL_CALL_TO_MANAGED_EXCEPTION_HOLDER(); \ INSTALL_COMPLUS_EXCEPTION_HANDLER_NO_DECLARE(); #define END_CALL_TO_MANAGED() \ UNINSTALL_COMPLUS_EXCEPTION_HANDLER(); \ END_SO_TOLERANT_CODE; \ } /***********************************************************************/ /* Macros that provide abstraction to the usage of DispatchCallSimple */ /***********************************************************************/ enum DispatchCallSimpleFlags { DispatchCallSimple_CriticalCall = 0x0001, DispatchCallSimple_CatchHandlerFoundNotification = 0x0002, }; #define ARGHOLDER_TYPE LPVOID #define OBJECTREF_TO_ARGHOLDER(x) (LPVOID)OBJECTREFToObject(x) #define STRINGREF_TO_ARGHOLDER(x) (LPVOID)STRINGREFToObject(x) #define PTR_TO_ARGHOLDER(x) (LPVOID)x #define DWORD_TO_ARGHOLDER(x) (LPVOID)(SIZE_T)x #define BOOL_TO_ARGHOLDER(x) DWORD_TO_ARGHOLDER(!!(x)) #define INIT_VARIABLES(count) \ DWORD __numArgs = count; \ DWORD __dwDispatchCallSimpleFlags = 0; \ #define PREPARE_NONVIRTUAL_CALLSITE(id) \ static PCODE s_pAddr##id = NULL; \ PCODE __pSlot = VolatileLoad(&s_pAddr##id); \ if ( __pSlot == NULL ) \ { \ MethodDesc *pMeth = MscorlibBinder::GetMethod(id); \ _ASSERTE(pMeth); \ __pSlot = pMeth->GetMultiCallableAddrOfCode(); \ VolatileStore(&s_pAddr##id, __pSlot); \ } #define PREPARE_VIRTUAL_CALLSITE(id, objref) \ MethodDesc *__pMeth = MscorlibBinder::GetMethod(id); \ PCODE __pSlot = __pMeth->GetCallTarget(&objref); #define PREPARE_VIRTUAL_CALLSITE_USING_METHODDESC(pMD, objref) \ PCODE __pSlot = pMD->GetCallTarget(&objref); #ifdef _DEBUG #define SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, methodTable) \ { \ MethodDesc* __pMeth = methodTable->GetMethodDescForSlot(slotNumber); \ _ASSERTE(__pMeth); \ _ASSERTE(!__pMeth->HasMethodInstantiation() && \ !__pMeth->GetMethodTable()->IsInterface()); \ } #else #define SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, objref) #endif // a simple virtual method is a non-interface/non-generic method // Note: objref has to be protected! #define PREPARE_SIMPLE_VIRTUAL_CALLSITE(id, objref) \ static WORD s_slot##id = MethodTable::NO_SLOT; \ WORD __slot = VolatileLoad(&s_slot##id); \ if (__slot == MethodTable::NO_SLOT) \ { \ MethodDesc *pMeth = MscorlibBinder::GetMethod(id); \ _ASSERTE(pMeth); \ __slot = pMeth->GetSlot(); \ VolatileStore(&s_slot##id, __slot); \ } \ PREPARE_SIMPLE_VIRTUAL_CALLSITE_USING_SLOT(__slot, objref) \ // a simple virtual method is a non-interface/non-generic method #define PREPARE_SIMPLE_VIRTUAL_CALLSITE_USING_SLOT(slotNumber, objref) \ MethodTable* __pObjMT = (objref)->GetMethodTable(); \ SIMPLE_VIRTUAL_METHOD_CHECK(slotNumber, __pObjMT); \ PCODE __pSlot = (PCODE) __pObjMT->GetRestoredSlot(slotNumber); #define PREPARE_NONVIRTUAL_CALLSITE_USING_METHODDESC(pMD) \ PCODE __pSlot = (pMD)->GetSingleCallableAddrOfCode(); #define PREPARE_NONVIRTUAL_CALLSITE_USING_CODE(pCode) \ PCODE __pSlot = pCode; #define CRITICAL_CALLSITE \ __dwDispatchCallSimpleFlags |= DispatchCallSimple_CriticalCall; // This flag should be used for callsites that catch exception up the stack inside the VM. The most common causes are // such as END_DOMAIN_TRANSITION or EX_CATCH. Catching exceptions in the managed code is properly instrumented and // does not need this notification. // // The notification is what enables both the managed 'unhandled exception' dialog and the 'user unhandled' dialog when // JMC is turned on. Many things that VS puts up the unhandled exception dialog for are actually cases where the native // exception was caught, for example catching exceptions at the thread base. JMC requires further accuracy - in that case // VS is checking to see if an exception escaped particular ranges of managed code frames. #define CATCH_HANDLER_FOUND_NOTIFICATION_CALLSITE \ __dwDispatchCallSimpleFlags |= DispatchCallSimple_CatchHandlerFoundNotification; #define PERFORM_CALL \ void * __retval = NULL; \ __retval = DispatchCallSimple(__pArgs, \ __numStackSlotsToCopy, \ __pSlot, \ __dwDispatchCallSimpleFlags);\ #ifdef CALLDESCR_ARGREGS #if defined(_TARGET_X86_) // Arguments on x86 are passed backward #define ARGNUM_0 1 #define ARGNUM_1 0 #define ARGNUM_N(n) __numArgs - n + 1 #else #define ARGNUM_0 0 #define ARGNUM_1 1 #define ARGNUM_N(n) n #endif #define PRECALL_PREP(args) \ DWORD __numStackSlotsToCopy = (__numArgs > NUM_ARGUMENT_REGISTERS) ? (__numArgs - NUM_ARGUMENT_REGISTERS) : 0; \ SIZE_T * __pArgs = (SIZE_T *)args; #define DECLARE_ARGHOLDER_ARRAY(arg, count) \ INIT_VARIABLES(count) \ ARGHOLDER_TYPE arg[(count <= NUM_ARGUMENT_REGISTERS ? NUM_ARGUMENT_REGISTERS : count)]; #else // CALLDESCR_ARGREGS #define ARGNUM_0 0 #define ARGNUM_1 1 #define ARGNUM_N(n) n #define PRECALL_PREP(args) \ DWORD __numStackSlotsToCopy = (__numArgs > NUM_ARGUMENT_REGISTERS) ? __numArgs : NUM_ARGUMENT_REGISTERS; \ SIZE_T * __pArgs = (SIZE_T *)args; #define DECLARE_ARGHOLDER_ARRAY(arg, count) \ INIT_VARIABLES(count) \ ARGHOLDER_TYPE arg[(count <= NUM_ARGUMENT_REGISTERS ? NUM_ARGUMENT_REGISTERS : count)]; #endif // CALLDESCR_ARGREGS #define CALL_MANAGED_METHOD(ret, rettype, args) \ PRECALL_PREP(args) \ PERFORM_CALL \ ret = *(rettype *)(&__retval); #define CALL_MANAGED_METHOD_NORET(args) \ PRECALL_PREP(args) \ PERFORM_CALL #define CALL_MANAGED_METHOD_RETREF(ret, reftype, args) \ PRECALL_PREP(args) \ PERFORM_CALL \ ret = (reftype)ObjectToOBJECTREF((Object *)__retval); #define ARGNUM_2 ARGNUM_N(2) #define ARGNUM_3 ARGNUM_N(3) #define ARGNUM_4 ARGNUM_N(4) #define ARGNUM_5 ARGNUM_N(5) #define ARGNUM_6 ARGNUM_N(6) #define ARGNUM_7 ARGNUM_N(7) #define ARGNUM_8 ARGNUM_N(8) void CallDefaultConstructor(OBJECTREF ref); #endif //!DACCESS_COMPILE && !CROSSGEN_COMPILE #endif // __CALLHELPERS_H__