diff options
Diffstat (limited to 'src/vm/stubhelpers.cpp')
-rw-r--r-- | src/vm/stubhelpers.cpp | 2176 |
1 files changed, 2176 insertions, 0 deletions
diff --git a/src/vm/stubhelpers.cpp b/src/vm/stubhelpers.cpp new file mode 100644 index 0000000000..6e7fb49b96 --- /dev/null +++ b/src/vm/stubhelpers.cpp @@ -0,0 +1,2176 @@ +// 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: stubhelpers.cpp +// + +// + + +#include "common.h" + +#include "mlinfo.h" +#include "stubhelpers.h" +#include "jitinterface.h" +#include "dllimport.h" +#include "fieldmarshaler.h" +#include "comdelegate.h" +#include "security.h" +#include "eventtrace.h" +#include "comdatetime.h" +#include "gc.h" +#include "interoputil.h" +#include "gcscan.h" +#ifdef FEATURE_REMOTING +#include "remoting.h" +#endif + +#ifdef FEATURE_COMINTEROP +#include <oletls.h> +#include "olecontexthelpers.h" +#include "runtimecallablewrapper.h" +#include "comcallablewrapper.h" +#include "clrtocomcall.h" +#include "cominterfacemarshaler.h" +#include "winrttypenameconverter.h" +#endif + +#ifdef VERIFY_HEAP + +CQuickArray<StubHelpers::ByrefValidationEntry> StubHelpers::s_ByrefValidationEntries; +SIZE_T StubHelpers::s_ByrefValidationIndex = 0; +CrstStatic StubHelpers::s_ByrefValidationLock; + +// static +void StubHelpers::Init() +{ + WRAPPER_NO_CONTRACT; + s_ByrefValidationLock.Init(CrstPinnedByrefValidation); +} + +// static +void StubHelpers::ValidateObjectInternal(Object *pObjUNSAFE, BOOL fValidateNextObj) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + SO_TOLERANT; +} + CONTRACTL_END; + + _ASSERTE(GCScan::GetGcRuntimeStructuresValid()); + + // validate the object - there's no need to validate next object's + // header since we validate the next object explicitly below + pObjUNSAFE->Validate(/*bDeep=*/ TRUE, /*bVerifyNextHeader=*/ FALSE, /*bVerifySyncBlock=*/ TRUE); + + // and the next object as required + if (fValidateNextObj) + { + Object *nextObj = GCHeap::GetGCHeap()->NextObj(pObjUNSAFE); + if (nextObj != NULL) + { + // Note that the MethodTable of the object (i.e. the pointer at offset 0) can change from + // g_pFreeObjectMethodTable to NULL, from NULL to <legal-value>, or possibly also from + // g_pFreeObjectMethodTable to <legal-value> concurrently while executing this function. + // Once <legal-value> is seen, we believe that the object should pass the Validate check. + // We have to be careful and read the pointer only once to avoid "phantom reads". + MethodTable *pMT = VolatileLoad(nextObj->GetMethodTablePtr()); + if (pMT != NULL && pMT != g_pFreeObjectMethodTable) + { + // do *not* verify the next object's syncblock - the next object is not guaranteed to + // be "alive" so the finalizer thread may have already released its syncblock + nextObj->Validate(/*bDeep=*/ TRUE, /*bVerifyNextHeader=*/ FALSE, /*bVerifySyncBlock=*/ FALSE); + } + } + } +} + +// static +MethodDesc *StubHelpers::ResolveInteropMethod(Object *pThisUNSAFE, MethodDesc *pMD) +{ + WRAPPER_NO_CONTRACT; + + if (pMD == NULL && pThisUNSAFE != NULL) + { + // if this is a call via delegate, get its Invoke method + MethodTable *pMT = pThisUNSAFE->GetMethodTable(); + + _ASSERTE(pMT->IsDelegate()); + return ((DelegateEEClass *)pMT->GetClass())->m_pInvokeMethod; + } + return pMD; +} + +// static +void StubHelpers::FormatValidationMessage(MethodDesc *pMD, SString &ssErrorString) +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + ssErrorString.Append(W("Detected managed heap corruption, likely culprit is interop call through ")); + + if (pMD == NULL) + { + // the only case where we don't have interop MD is CALLI + ssErrorString.Append(W("CALLI.")); + } + else + { + ssErrorString.Append(W("method '")); + + StackSString ssClassName; + pMD->GetMethodTable()->_GetFullyQualifiedNameForClass(ssClassName); + + ssErrorString.Append(ssClassName); + ssErrorString.Append(NAMESPACE_SEPARATOR_CHAR); + ssErrorString.AppendUTF8(pMD->GetName()); + + ssErrorString.Append(W("'.")); + } +} + +// static +void StubHelpers::ProcessByrefValidationList() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + StackSString errorString; + ByrefValidationEntry entry = { NULL, NULL }; + + EX_TRY + { + AVInRuntimeImplOkayHolder AVOkay; + + // Process all byref validation entries we have saved since the last GC. Note that EE is suspended at + // this point so we don't have to take locks and we can safely call code:GCHeap.GetContainingObject. + for (SIZE_T i = 0; i < s_ByrefValidationIndex; i++) + { + entry = s_ByrefValidationEntries[i]; + + Object *pObjUNSAFE = GCHeap::GetGCHeap()->GetGCHeap()->GetContainingObject(entry.pByref); + ValidateObjectInternal(pObjUNSAFE, TRUE); + } + } + EX_CATCH + { + EX_TRY + { + FormatValidationMessage(entry.pMD, errorString); + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, errorString.GetUnicode()); + } + EX_CATCH + { + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + } + EX_END_CATCH_UNREACHABLE; + } + EX_END_CATCH_UNREACHABLE; + + s_ByrefValidationIndex = 0; +} + +#endif // VERIFY_HEAP + +FCIMPL1(double, StubHelpers::DateMarshaler__ConvertToNative, INT64 managedDate) +{ + FCALL_CONTRACT; + + double retval = 0.0; + HELPER_METHOD_FRAME_BEGIN_RET_0(); + retval = COMDateTime::TicksToDoubleDate(managedDate); + HELPER_METHOD_FRAME_END(); + return retval; +} +FCIMPLEND + +FCIMPL1_V(INT64, StubHelpers::DateMarshaler__ConvertToManaged, double nativeDate) +{ + FCALL_CONTRACT; + + INT64 retval = 0; + HELPER_METHOD_FRAME_BEGIN_RET_0(); + retval = COMDateTime::DoubleDateToTicks(nativeDate); + HELPER_METHOD_FRAME_END(); + return retval; +} +FCIMPLEND + +FCIMPL4(void, StubHelpers::ValueClassMarshaler__ConvertToNative, LPVOID pDest, LPVOID pSrc, MethodTable* pMT, OBJECTREF *ppCleanupWorkListOnStack) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + FmtValueTypeUpdateNative(&pSrc, pMT, (BYTE*)pDest, ppCleanupWorkListOnStack); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL3(void, StubHelpers::ValueClassMarshaler__ConvertToManaged, LPVOID pDest, LPVOID pSrc, MethodTable* pMT) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + FmtValueTypeUpdateCLR(&pDest, pMT, (BYTE*)pSrc); + HELPER_METHOD_FRAME_END_POLL(); +} +FCIMPLEND + +FCIMPL2(void, StubHelpers::ValueClassMarshaler__ClearNative, LPVOID pDest, MethodTable* pMT) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + FmtClassDestroyNative(pDest, pMT); + HELPER_METHOD_FRAME_END_POLL(); +} +FCIMPLEND + +#ifdef FEATURE_COMINTEROP + +FORCEINLINE static void GetCOMIPFromRCW_ClearFP() +{ + LIMITED_METHOD_CONTRACT; + +#ifdef _TARGET_X86_ + // As per ASURT 146699 we need to clear FP state before calling to COM + // the following sequence was previously generated to compiled ML stubs + // and is faster than _clearfp(). + __asm + { + fnstsw ax + and eax, 0x3F + jz NoNeedToClear + fnclex +NoNeedToClear: + } +#endif // _TARGET_X86_ +} + +FORCEINLINE static SOleTlsData *GetOrCreateOleTlsData() +{ + LIMITED_METHOD_CONTRACT; + + SOleTlsData *pOleTlsData; +#ifdef _TARGET_X86_ + // This saves 1 memory instruction over NtCurretTeb()->ReservedForOle because + // NtCurrentTeb() reads _TEB.NtTib.Self which is the same as what FS:0 already + // points to. + pOleTlsData = (SOleTlsData *)(ULONG_PTR)__readfsdword(offsetof(TEB, ReservedForOle)); +#else // _TARGET_X86_ + pOleTlsData = (SOleTlsData *)NtCurrentTeb()->ReservedForOle; +#endif // _TARGET_X86_ + if (pOleTlsData == NULL) + { + pOleTlsData = (SOleTlsData *)SetupOleContext(); + } + return pOleTlsData; +} + +FORCEINLINE static void *GetCOMIPFromRCW_GetTargetNoInterception(IUnknown *pUnk, ComPlusCallInfo *pComInfo) +{ + LIMITED_METHOD_CONTRACT; + +#ifdef _TARGET_X86_ + _ASSERTE(pComInfo->m_pInterceptStub == NULL || pComInfo->m_pInterceptStub == (LPVOID)-1); + _ASSERTE(!pComInfo->HasCopyCtorArgs()); +#endif // _TARGET_X86_ + _ASSERTE(!NDirect::IsHostHookEnabled()); + + LPVOID *lpVtbl = *(LPVOID **)pUnk; + return lpVtbl[pComInfo->m_cachedComSlot]; +} + +FORCEINLINE static IUnknown *GetCOMIPFromRCW_GetIUnknownFromRCWCache(RCW *pRCW, MethodTable * pItfMT) +{ + LIMITED_METHOD_CONTRACT; + + // The code in this helper is the "fast path" that used to be generated directly + // to compiled ML stubs. The idea is to aim for an efficient RCW cache hit. + SOleTlsData * pOleTlsData = GetOrCreateOleTlsData(); + + // test for free-threaded after testing for context match to optimize for apartment-bound objects + if (pOleTlsData->pCurrentCtx == pRCW->GetWrapperCtxCookie() || pRCW->IsFreeThreaded()) + { + for (int i = 0; i < INTERFACE_ENTRY_CACHE_SIZE; i++) + { + if (pRCW->m_aInterfaceEntries[i].m_pMT == pItfMT) + { + return pRCW->m_aInterfaceEntries[i].m_pUnknown; + } + } + } + + // also search the auxiliary cache if it's available + RCWAuxiliaryData *pAuxData = pRCW->m_pAuxiliaryData; + if (pAuxData != NULL) + { + LPVOID pCtxCookie = (pRCW->IsFreeThreaded() ? NULL : pOleTlsData->pCurrentCtx); + return pAuxData->FindInterfacePointer(pItfMT, pCtxCookie); + } + + return NULL; +} + +// Like GetCOMIPFromRCW_GetIUnknownFromRCWCache but also computes the target. This is a couple of instructions +// faster than GetCOMIPFromRCW_GetIUnknownFromRCWCache + GetCOMIPFromRCW_GetTargetNoInterception. +FORCEINLINE static IUnknown *GetCOMIPFromRCW_GetIUnknownFromRCWCache_NoInterception(RCW *pRCW, ComPlusCallInfo *pComInfo, void **ppTarget) +{ + LIMITED_METHOD_CONTRACT; + + // The code in this helper is the "fast path" that used to be generated directly + // to compiled ML stubs. The idea is to aim for an efficient RCW cache hit. + SOleTlsData *pOleTlsData = GetOrCreateOleTlsData(); + MethodTable *pItfMT = pComInfo->m_pInterfaceMT; + + // test for free-threaded after testing for context match to optimize for apartment-bound objects + if (pOleTlsData->pCurrentCtx == pRCW->GetWrapperCtxCookie() || pRCW->IsFreeThreaded()) + { + for (int i = 0; i < INTERFACE_ENTRY_CACHE_SIZE; i++) + { + if (pRCW->m_aInterfaceEntries[i].m_pMT == pItfMT) + { + IUnknown *pUnk = pRCW->m_aInterfaceEntries[i].m_pUnknown; + _ASSERTE(pUnk != NULL); + *ppTarget = GetCOMIPFromRCW_GetTargetNoInterception(pUnk, pComInfo); + return pUnk; + } + } + } + + // also search the auxiliary cache if it's available + RCWAuxiliaryData *pAuxData = pRCW->m_pAuxiliaryData; + if (pAuxData != NULL) + { + LPVOID pCtxCookie = (pRCW->IsFreeThreaded() ? NULL : pOleTlsData->pCurrentCtx); + + IUnknown *pUnk = pAuxData->FindInterfacePointer(pItfMT, pCtxCookie); + if (pUnk != NULL) + { + *ppTarget = GetCOMIPFromRCW_GetTargetNoInterception(pUnk, pComInfo); + return pUnk; + } + } + + return NULL; +} + +FORCEINLINE static void *GetCOMIPFromRCW_GetTarget(IUnknown *pUnk, ComPlusCallInfo *pComInfo) +{ + LIMITED_METHOD_CONTRACT; + +#ifndef FEATURE_CORECLR +#ifdef _TARGET_X86_ + // m_pInterceptStub is either NULL if we never called on this method, -1 if we're not + // hosted, or the host hook stub if we are hosted. The stub will extract the real target + // from the 'this' argument. + PVOID pInterceptStub = VolatileLoadWithoutBarrier(&pComInfo->m_pInterceptStub); + + if (pInterceptStub != (LPVOID)-1) + { + if (pInterceptStub != NULL) + { + return pInterceptStub; + } + + if (NDirect::IsHostHookEnabled() || pComInfo->HasCopyCtorArgs()) + { + return NULL; + } + + if (!EnsureWritablePagesNoThrow(&pComInfo->m_pInterceptStub, sizeof(pComInfo->m_pInterceptStub))) + { + return NULL; + } + + pComInfo->m_pInterceptStub = (LPVOID)-1; + } +#else // _TARGET_X86_ +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + if (NDirect::IsHostHookEnabled()) + { + // There's one static stub on !_TARGET_X86_. + return (LPVOID)GetEEFuncEntryPoint(PInvokeStubForHost); + } +#endif // FEATURE_INCLUDE_ALL_INTERFACES +#endif // _TARGET_X86_ +#endif // FEATURE_CORECLR + + LPVOID *lpVtbl = *(LPVOID **)pUnk; + return lpVtbl[pComInfo->m_cachedComSlot]; +} + +NOINLINE static IUnknown* GetCOMIPFromRCWHelper(LPVOID pFCall, OBJECTREF pSrc, MethodDesc* pMD, void **ppTarget) +{ + FC_INNER_PROLOG(pFCall); + + IUnknown *pIntf = NULL; + + // This is only called in IL stubs which are in CER, so we don't need to worry about ThreadAbort + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_NO_THREAD_ABORT|Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, pSrc); + + SafeComHolder<IUnknown> pRetUnk; + + ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD); + pRetUnk = ComObject::GetComIPFromRCWThrowing(&pSrc, pComInfo->m_pInterfaceMT); + + if (pFCall == StubHelpers::GetCOMIPFromRCW_WinRT || + pFCall == StubHelpers::GetCOMIPFromRCW_WinRTSharedGeneric || + pFCall == StubHelpers::GetCOMIPFromRCW_WinRTDelegate) + { + pRetUnk.Release(); + } + +#ifndef FEATURE_CORECLR +#ifdef _TARGET_X86_ + GCX_PREEMP(); + Stub *pInterceptStub = NULL; + + if (pComInfo->m_pInterceptStub == NULL) + { + if (pComInfo->HasCopyCtorArgs()) + { + // static stub that gets its arguments in a thread-static field + pInterceptStub = NDirect::GetStubForCopyCtor(); + } + + if (NDirect::IsHostHookEnabled()) + { + pInterceptStub = pComInfo->GenerateStubForHost( + pMD->GetDomain()->GetLoaderAllocator()->GetStubHeap(), + pInterceptStub); + } + + EnsureWritablePages(&pComInfo->m_pInterceptStub); + + if (pInterceptStub != NULL) + { + if (InterlockedCompareExchangeT(&pComInfo->m_pInterceptStub, + (LPVOID)pInterceptStub->GetEntryPoint(), + NULL) != NULL) + { + pInterceptStub->DecRef(); + } + } + else + { + pComInfo->m_pInterceptStub = (LPVOID)-1; + } + } +#endif // _TARGET_X86_ +#endif // !FEATURE_CORECLR + + *ppTarget = GetCOMIPFromRCW_GetTarget(pRetUnk, pComInfo); + _ASSERTE(*ppTarget != NULL); + + GetCOMIPFromRCW_ClearFP(); + + pIntf = pRetUnk.Extract(); + + // No exception will be thrown here (including thread abort as it is delayed in IL stubs) + HELPER_METHOD_FRAME_END(); + + FC_INNER_EPILOG(); + return pIntf; +} + +//================================================================================================================== +// The GetCOMIPFromRCW helper exists in four specialized versions to optimize CLR->COM perf. Please be careful when +// changing this code as one of these methods is executed as part of every CLR->COM call so every instruction counts. +//================================================================================================================== + + +#include <optsmallperfcritical.h> + +// This helper can handle any CLR->COM call (classic COM, WinRT, WinRT delegate, WinRT generic), it supports hosting, +// and clears FP state on x86 for compatibility with VB6. +FCIMPL4(IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void **ppTarget, CLR_BOOL* pfNeedsRelease) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(pMD->IsComPlusCall() || pMD->IsGenericComPlusCall() || pMD->IsEEImpl()); + } + CONTRACTL_END; + + OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); + *pfNeedsRelease = false; + + ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD); + RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); + if (pRCW != NULL) + { + + IUnknown * pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache(pRCW, pComInfo->m_pInterfaceMT); + if (pUnk != NULL) + { + *ppTarget = GetCOMIPFromRCW_GetTarget(pUnk, pComInfo); + if (*ppTarget != NULL) + { + GetCOMIPFromRCW_ClearFP(); + return pUnk; + } + } + } + + /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ + *pfNeedsRelease = true; + FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW, pSrc, pMD, ppTarget)); +} +FCIMPLEND + +// This helper can handle only non-generic WinRT calls, does not support hosting/interception, and does not clear FP state. +FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW_WinRT, Object* pSrcUNSAFE, MethodDesc* pMD, void** ppTarget) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(pMD->IsComPlusCall()); + } + CONTRACTL_END; + + OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); + + ComPlusCallInfo *pComInfo = ((ComPlusCallMethodDesc *)pMD)->m_pComPlusCallInfo; + RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); + if (pRCW != NULL) + { + IUnknown *pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache_NoInterception(pRCW, pComInfo, ppTarget); + if (pUnk != NULL) + { + return pUnk; + } + } + + /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ + FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW_WinRT, pSrc, pMD, ppTarget)); +} +FCIMPLEND + +// This helper can handle only generic WinRT calls, does not support hosting, and does not clear FP state. +FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW_WinRTSharedGeneric, Object* pSrcUNSAFE, MethodDesc* pMD, void** ppTarget) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(pMD->IsGenericComPlusCall()); + } + CONTRACTL_END; + + OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); + + ComPlusCallInfo *pComInfo = pMD->AsInstantiatedMethodDesc()->IMD_GetComPlusCallInfo(); + RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); + if (pRCW != NULL) + { + IUnknown *pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache_NoInterception(pRCW, pComInfo, ppTarget); + if (pUnk != NULL) + { + return pUnk; + } + } + + /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ + FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW_WinRTSharedGeneric, pSrc, pMD, ppTarget)); +} +FCIMPLEND + +// This helper can handle only delegate WinRT calls, does not support hosting, and does not clear FP state. +FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW_WinRTDelegate, Object* pSrcUNSAFE, MethodDesc* pMD, void** ppTarget) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(pMD->IsEEImpl()); + } + CONTRACTL_END; + + OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); + + ComPlusCallInfo *pComInfo = ((DelegateEEClass *)pMD->GetClass())->m_pComPlusCallInfo; + RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); + if (pRCW != NULL) + { + IUnknown *pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache_NoInterception(pRCW, pComInfo, ppTarget); + if (pUnk != NULL) + { + return pUnk; + } + } + + /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ + FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW_WinRTDelegate, pSrc, pMD, ppTarget)); +} +FCIMPLEND + +#include <optdefault.h> + + +NOINLINE static FC_BOOL_RET ShouldCallWinRTInterfaceHelper(RCW *pRCW, MethodTable *pItfMT) +{ + FC_INNER_PROLOG(StubHelpers::ShouldCallWinRTInterface); + + bool result = false; + + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); + + // call the GC-triggering version + result = pRCW->SupportsWinRTInteropInterface(pItfMT); + + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); + + FC_RETURN_BOOL(result); +} + +FCIMPL2(FC_BOOL_RET, StubHelpers::ShouldCallWinRTInterface, Object *pSrcUNSAFE, MethodDesc *pMD) +{ + FCALL_CONTRACT; + + OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); + + ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD); + RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); + if (pRCW == NULL) + { + // Pretend that we are not redirected WinRT type + // We'll throw InvalidComObjectException later in GetComIPFromRCW + return false; + } + + TypeHandle::CastResult result = pRCW->SupportsWinRTInteropInterfaceNoGC(pComInfo->m_pInterfaceMT); + switch (result) + { + case TypeHandle::CanCast: FC_RETURN_BOOL(true); + case TypeHandle::CannotCast: FC_RETURN_BOOL(false); + } + + FC_INNER_RETURN(FC_BOOL_RET, ShouldCallWinRTInterfaceHelper(pRCW, pComInfo->m_pInterfaceMT)); +} +FCIMPLEND + +NOINLINE static DelegateObject *GetTargetForAmbiguousVariantCallHelper(RCW *pRCW, MethodTable *pMT, BOOL fIsEnumerable, CLR_BOOL *pfUseString) +{ + FC_INNER_PROLOG(StubHelpers::GetTargetForAmbiguousVariantCall); + + DelegateObject *pRetVal = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); + + // Note that if the call succeeds, it will set the right OBJECTHANDLE/flags on the RCW so we won't have to do this + // next time. If the call fails, we don't care because it is an error and an exception will be thrown later. + SafeComHolder<IUnknown> pUnk = pRCW->GetComIPFromRCW(pMT); + + WinRTInterfaceRedirector::WinRTLegalStructureBaseType baseType = WinRTInterfaceRedirector::GetStructureBaseType(pMT->GetInstantiation()); + + BOOL fUseString = FALSE; + BOOL fUseT = FALSE; + pRetVal = (DelegateObject *)OBJECTREFToObject(pRCW->GetTargetForAmbiguousVariantCall(fIsEnumerable, baseType, &fUseString, &fUseT)); + + *pfUseString = !!fUseString; + + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); + + return pRetVal; +} + +// Performs a run-time check to see how an ambiguous variant call on an RCW should be handled. Returns a delegate which should +// be called, or sets *pfUseString to true which means that the caller should use the <string> instantiation. If NULL is returned +// and *pfUseString is false, the caller should attempt to handle the call as usual. +FCIMPL3(DelegateObject*, StubHelpers::GetTargetForAmbiguousVariantCall, Object *pSrcUNSAFE, MethodTable *pMT, CLR_BOOL *pfUseString) +{ + FCALL_CONTRACT; + + OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); + + RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); + if (pRCW == NULL) + { + // ignore this - the call we'll attempt to make later will throw the right exception + *pfUseString = false; + return NULL; + } + + BOOL fIsEnumerable = pMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC)); + _ASSERTE(fIsEnumerable || pMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__IREADONLYLISTGENERIC))); + + WinRTInterfaceRedirector::WinRTLegalStructureBaseType baseType = WinRTInterfaceRedirector::GetStructureBaseType(pMT->GetInstantiation()); + + BOOL fUseString = FALSE; + BOOL fUseT = FALSE; + DelegateObject *pRetVal = (DelegateObject *)OBJECTREFToObject(pRCW->GetTargetForAmbiguousVariantCall(fIsEnumerable, baseType, &fUseString, &fUseT)); + + if (pRetVal != NULL || fUseT || fUseString) + { + *pfUseString = !!fUseString; + return pRetVal; + } + + // we haven't seen QI for the interface yet, trigger it now + FC_INNER_RETURN(DelegateObject*, GetTargetForAmbiguousVariantCallHelper(pRCW, pMT, fIsEnumerable, pfUseString)); +} +FCIMPLEND + +FCIMPL2(void, StubHelpers::ObjectMarshaler__ConvertToNative, Object* pSrcUNSAFE, VARIANT* pDest) +{ + FCALL_CONTRACT; + + OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); + + HELPER_METHOD_FRAME_BEGIN_1(pSrc); + if (pDest->vt & VT_BYREF) + { + OleVariant::MarshalOleRefVariantForObject(&pSrc, pDest); + } + else + { + OleVariant::MarshalOleVariantForObject(&pSrc, pDest); + } + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL1(Object*, StubHelpers::ObjectMarshaler__ConvertToManaged, VARIANT* pSrc) +{ + FCALL_CONTRACT; + + OBJECTREF retVal = NULL; + HELPER_METHOD_FRAME_BEGIN_RET_1(retVal); + // The IL stub is going to call ObjectMarshaler__ClearNative() afterwards. + // If it doesn't it's a bug in ILObjectMarshaler. + OleVariant::MarshalObjectForOleVariant(pSrc, &retVal); + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(retVal); +} +FCIMPLEND + +FCIMPL1(void, StubHelpers::ObjectMarshaler__ClearNative, VARIANT* pSrc) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + SafeVariantClear(pSrc); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +#include <optsmallperfcritical.h> +FCIMPL4(IUnknown*, StubHelpers::InterfaceMarshaler__ConvertToNative, Object* pObjUNSAFE, MethodTable* pItfMT, MethodTable* pClsMT, DWORD dwFlags) +{ + FCALL_CONTRACT; + + if (NULL == pObjUNSAFE) + { + return NULL; + } + + IUnknown *pIntf = NULL; + OBJECTREF pObj = ObjectToOBJECTREF(pObjUNSAFE); + + // This is only called in IL stubs which are in CER, so we don't need to worry about ThreadAbort + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_NO_THREAD_ABORT, pObj); + + pIntf = MarshalObjectToInterface(&pObj, pItfMT, pClsMT, dwFlags); + + // No exception will be thrown here (including thread abort as it is delayed in IL stubs) + HELPER_METHOD_FRAME_END(); + + return pIntf; +} +FCIMPLEND + +FCIMPL4(Object*, StubHelpers::InterfaceMarshaler__ConvertToManaged, IUnknown **ppUnk, MethodTable *pItfMT, MethodTable *pClsMT, DWORD dwFlags) +{ + FCALL_CONTRACT; + + if (NULL == *ppUnk) + { + return NULL; + } + + OBJECTREF pObj = NULL; + HELPER_METHOD_FRAME_BEGIN_RET_1(pObj); + + UnmarshalObjectFromInterface(&pObj, ppUnk, pItfMT, pClsMT, dwFlags); + + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(pObj); +} +FCIMPLEND + +void QCALLTYPE StubHelpers::InterfaceMarshaler__ClearNative(IUnknown * pUnk) +{ + QCALL_CONTRACT; + + BEGIN_QCALL_SO_TOLERANT; + + ULONG cbRef = SafeReleasePreemp(pUnk); + LogInteropRelease(pUnk, cbRef, "InterfaceMarshalerBase::ClearNative: In/Out release"); + + END_QCALL_SO_TOLERANT; +} +#include <optdefault.h> + + + + +FCIMPL1(StringObject*, StubHelpers::UriMarshaler__GetRawUriFromNative, ABI::Windows::Foundation::IUriRuntimeClass* pIUriRC) +{ + FCALL_CONTRACT; + + if (NULL == pIUriRC) + { + return NULL; + } + + STRINGREF strRef = NULL; + UINT32 cchRawUri = 0; + LPCWSTR pwszRawUri = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_1(strRef); + + WinRtString hsRawUriName; + + { + GCX_PREEMP(); + + // Get the RawUri string from the WinRT URI object + { + LeaveRuntimeHolder lrh(**(size_t**)(IUnknown*)pIUriRC); + IfFailThrow(pIUriRC->get_RawUri(hsRawUriName.Address())); + } + + pwszRawUri = hsRawUriName.GetRawBuffer(&cchRawUri); + } + + strRef = StringObject::NewString(pwszRawUri, cchRawUri); + + HELPER_METHOD_FRAME_END(); + + return STRINGREFToObject(strRef); +} +FCIMPLEND + +FCIMPL2(IUnknown*, StubHelpers::UriMarshaler__CreateNativeUriInstance, WCHAR* pRawUri, UINT strLen) +{ + CONTRACTL { + FCALL_CHECK; + PRECONDITION(CheckPointer(pRawUri)); + } + CONTRACTL_END; + + ABI::Windows::Foundation::IUriRuntimeClass* pIUriRC = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_0(); + + GCX_PREEMP(); + pIUriRC = CreateWinRTUri(pRawUri, strLen); + + HELPER_METHOD_FRAME_END(); + + return pIUriRC; +} +FCIMPLEND + +ABI::Windows::UI::Xaml::Interop::INotifyCollectionChangedEventArgs* QCALLTYPE +StubHelpers::EventArgsMarshaler__CreateNativeNCCEventArgsInstance +(int action, ABI::Windows::UI::Xaml::Interop::IBindableVector *newItem, ABI::Windows::UI::Xaml::Interop::IBindableVector *oldItem, int newIndex, int oldIndex) +{ + QCALL_CONTRACT; + + ABI::Windows::UI::Xaml::Interop::INotifyCollectionChangedEventArgs *pArgsRC = NULL; + + BEGIN_QCALL; + + EventArgsMarshalingInfo *marshalingInfo = GetAppDomain()->GetMarshalingData()->GetEventArgsMarshalingInfo(); + ABI::Windows::UI::Xaml::Interop::INotifyCollectionChangedEventArgsFactory *pFactory = marshalingInfo->GetNCCEventArgsFactory(); + + SafeComHolderPreemp<IInspectable> pInner; + HRESULT hr; + { + LeaveRuntimeHolder lrh(**(size_t **)(IUnknown *)pFactory); + hr = pFactory->CreateInstanceWithAllParameters( + (ABI::Windows::UI::Xaml::Interop::NotifyCollectionChangedAction)action, + (ABI::Windows::UI::Xaml::Interop::IBindableVector *)newItem, + (ABI::Windows::UI::Xaml::Interop::IBindableVector *)oldItem, + newIndex, + oldIndex, + NULL, + &pInner, + &pArgsRC); + } + IfFailThrow(hr); + + END_QCALL; + + return pArgsRC; +} + +ABI::Windows::UI::Xaml::Data::IPropertyChangedEventArgs* QCALLTYPE + StubHelpers::EventArgsMarshaler__CreateNativePCEventArgsInstance(HSTRING name) +{ + QCALL_CONTRACT; + + ABI::Windows::UI::Xaml::Data::IPropertyChangedEventArgs *pArgsRC = NULL; + + BEGIN_QCALL; + + EventArgsMarshalingInfo *marshalingInfo = GetAppDomain()->GetMarshalingData()->GetEventArgsMarshalingInfo(); + ABI::Windows::UI::Xaml::Data::IPropertyChangedEventArgsFactory *pFactory = marshalingInfo->GetPCEventArgsFactory(); + + SafeComHolderPreemp<IInspectable> pInner; + HRESULT hr; + { + LeaveRuntimeHolder lrh(**(size_t **)(IUnknown *)pFactory); + hr = pFactory->CreateInstance( + name, + NULL, + &pInner, + &pArgsRC); + } + IfFailThrow(hr); + + END_QCALL; + + return pArgsRC; +} + +// A helper to convert an IP to object using special flags. +FCIMPL1(Object *, StubHelpers::InterfaceMarshaler__ConvertToManagedWithoutUnboxing, IUnknown *pNative) +{ + FCALL_CONTRACT; + + OBJECTREF oref = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_1(oref); + + // + // Get a wrapper for pNative + // Note that we need to skip WinRT unboxing at this point because + // 1. We never know whether GetObjectRefFromComIP went through unboxing path. + // For example, user could just pass a IUnknown * to T and we'll happily convert that to T + // 2. If for some reason we end up getting something that does not implement IReference<T>, + // we'll get a nice message later when we do the cast in CLRIReferenceImpl.UnboxHelper + // + GetObjectRefFromComIP( + &oref, + pNative, // pUnk + g_pBaseCOMObject, // Use __ComObject + NULL, // pItfMT + ObjFromComIP::CLASS_IS_HINT | // No cast check - we'll do cast later + ObjFromComIP::UNIQUE_OBJECT | // Do not cache the object - To ensure that the unboxing code is called on this object always + // and the object is not retrieved from the cache as an __ComObject. + // Don't call GetRuntimeClassName - I just want a RCW of __ComObject + ObjFromComIP::IGNORE_WINRT_AND_SKIP_UNBOXING // Skip unboxing + ); + + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(oref); +} +FCIMPLEND + +FCIMPL2(StringObject *, StubHelpers::WinRTTypeNameConverter__ConvertToWinRTTypeName, + ReflectClassBaseObject *pTypeUNSAFE, CLR_BOOL *pbIsWinRTPrimitive) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(pTypeUNSAFE)); + PRECONDITION(CheckPointer(pbIsWinRTPrimitive)); + } + CONTRACTL_END; + + REFLECTCLASSBASEREF refClass = (REFLECTCLASSBASEREF) pTypeUNSAFE; + STRINGREF refString= NULL; + HELPER_METHOD_FRAME_BEGIN_RET_2(refClass, refString); + + SString strWinRTTypeName; + bool bIsPrimitive; + if (WinRTTypeNameConverter::AppendWinRTTypeNameForManagedType( + refClass->GetType(), // thManagedType + strWinRTTypeName, // strWinRTTypeName to append + FALSE, // for type conversion, not for GetRuntimeClassName + &bIsPrimitive + )) + { + *pbIsWinRTPrimitive = bIsPrimitive; + refString = AllocateString(strWinRTTypeName); + } + else + { + *pbIsWinRTPrimitive = FALSE; + refString = NULL; + } + + HELPER_METHOD_FRAME_END(); + + return STRINGREFToObject(refString); +} +FCIMPLEND + +FCIMPL2(ReflectClassBaseObject *, StubHelpers::WinRTTypeNameConverter__GetTypeFromWinRTTypeName, StringObject *pWinRTTypeNameUNSAFE, CLR_BOOL *pbIsPrimitive) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(pWinRTTypeNameUNSAFE)); + } + CONTRACTL_END; + + OBJECTREF refClass = NULL; + STRINGREF refString = ObjectToSTRINGREF(pWinRTTypeNameUNSAFE); + HELPER_METHOD_FRAME_BEGIN_RET_2(refClass, refString); + + bool isPrimitive; + TypeHandle th = WinRTTypeNameConverter::GetManagedTypeFromWinRTTypeName(refString->GetBuffer(), &isPrimitive); + *pbIsPrimitive = isPrimitive; + + refClass = th.GetManagedClassObject(); + + HELPER_METHOD_FRAME_END(); + + return (ReflectClassBaseObject *)OBJECTREFToObject(refClass); +} +FCIMPLEND + +FCIMPL1(MethodDesc*, StubHelpers::GetDelegateInvokeMethod, DelegateObject *pThisUNSAFE) +{ + FCALL_CONTRACT; + + MethodDesc *pMD = NULL; + + OBJECTREF pThis = ObjectToOBJECTREF(pThisUNSAFE); + HELPER_METHOD_FRAME_BEGIN_RET_1(pThis); + + MethodTable *pDelMT = pThis->GetMethodTable(); + + pMD = COMDelegate::FindDelegateInvokeMethod(pDelMT); + if (pMD->IsSharedByGenericInstantiations()) + { + // we need the exact MethodDesc + pMD = InstantiatedMethodDesc::FindOrCreateExactClassMethod(pDelMT, pMD); + } + + HELPER_METHOD_FRAME_END(); + + _ASSERTE(pMD); + return pMD; +} +FCIMPLEND + +// Called from COM-to-CLR factory method stubs to get the return value (the delegating interface pointer +// corresponding to the default WinRT interface of the class which we are constructing). +FCIMPL2(IInspectable *, StubHelpers::GetWinRTFactoryReturnValue, Object *pThisUNSAFE, PCODE pCtorEntry) +{ + FCALL_CONTRACT; + + IInspectable *pInsp = NULL; + + OBJECTREF pThis = ObjectToOBJECTREF(pThisUNSAFE); + HELPER_METHOD_FRAME_BEGIN_RET_1(pThis); + + // COM-to-CLR stubs use the target method entry point as their stub context + MethodDesc *pCtorMD = Entry2MethodDesc(pCtorEntry, NULL); + MethodTable *pClassMT = pCtorMD->GetMethodTable(); + + // make sure that we talk to the right CCW + ComCallWrapperTemplate *pTemplate = ComCallWrapperTemplate::GetTemplate(TypeHandle(pClassMT)); + CCWHolder pWrap = ComCallWrapper::InlineGetWrapper(&pThis, pTemplate); + + MethodTable *pDefaultItf = pClassMT->GetDefaultWinRTInterface(); + const IID &riid = (pDefaultItf == NULL ? IID_IInspectable : IID_NULL); + + pInsp = static_cast<IInspectable *>(ComCallWrapper::GetComIPFromCCW(pWrap, riid, pDefaultItf, + GetComIPFromCCW::CheckVisibility)); + + HELPER_METHOD_FRAME_END(); + + return pInsp; +} +FCIMPLEND + +// Called from CLR-to-COM factory method stubs to get the outer IInspectable to pass +// to the underlying factory object. +FCIMPL2(IInspectable *, StubHelpers::GetOuterInspectable, Object *pThisUNSAFE, MethodDesc *pCtorMD) +{ + FCALL_CONTRACT; + + IInspectable *pInsp = NULL; + + OBJECTREF pThis = ObjectToOBJECTREF(pThisUNSAFE); + + if (pThis->GetTrueMethodTable() != pCtorMD->GetMethodTable()) + { + // this is a composition scenario + HELPER_METHOD_FRAME_BEGIN_RET_1(pThis); + + // we don't have the "outer" yet, marshal the object + pInsp = static_cast<IInspectable *> + (MarshalObjectToInterface(&pThis, NULL, NULL, ItfMarshalInfo::ITF_MARSHAL_INSP_ITF | ItfMarshalInfo::ITF_MARSHAL_USE_BASIC_ITF)); + + HELPER_METHOD_FRAME_END(); + } + + return pInsp; +} +FCIMPLEND + +#ifdef MDA_SUPPORTED +FCIMPL2(ExceptionObject*, StubHelpers::TriggerExceptionSwallowedMDA, ExceptionObject* pExceptionUNSAFE, PCODE pManagedTarget) +{ + FCALL_CONTRACT; + OBJECTREF pException = ObjectToOBJECTREF(pExceptionUNSAFE); + HELPER_METHOD_FRAME_BEGIN_RET_1(pException); + + // COM-to-CLR stubs use the target method entry point as their stub context + MethodDesc * pMD = Entry2MethodDesc(pManagedTarget, NULL); + + MDA_TRIGGER_ASSISTANT(ExceptionSwallowedOnCallFromCom, ReportViolation(pMD, &pException)); + + HELPER_METHOD_FRAME_END(); + return (ExceptionObject*)OBJECTREFToObject(pException); +} +FCIMPLEND +#endif // MDA_SUPPORTED + +#endif // FEATURE_COMINTEROP + +FCIMPL0(void, StubHelpers::SetLastError) +{ + // Make sure this is the first thing we do after returning from the target, as almost everything can cause the last error to get trashed + DWORD lastError = ::GetLastError(); + + FCALL_CONTRACT; + + GetThread()->m_dwLastError = lastError; +} +FCIMPLEND + +FCIMPL0(void, StubHelpers::ClearLastError) +{ + FCALL_CONTRACT; + + ::SetLastError(0); +} +FCIMPLEND + +FCIMPL1(FC_BOOL_RET, StubHelpers::IsQCall, NDirectMethodDesc* pNMD) +{ + FCALL_CONTRACT; + FC_RETURN_BOOL(pNMD->IsQCall()); +} +FCIMPLEND + +NOINLINE static void InitDeclaringTypeHelper(MethodTable *pMT) +{ + FC_INNER_PROLOG(StubHelpers::InitDeclaringType); + + HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); + pMT->CheckRunClassInitThrowing(); + HELPER_METHOD_FRAME_END(); + + FC_INNER_EPILOG(); +} + +// Triggers cctor of pNMD's declarer, similar to code:JIT_InitClass. +#include <optsmallperfcritical.h> +FCIMPL1(void, StubHelpers::InitDeclaringType, NDirectMethodDesc* pNMD) +{ + FCALL_CONTRACT; + + MethodTable *pMT = pNMD->GetMethodTable(); + _ASSERTE(!pMT->IsClassPreInited()); + + if (pMT->GetDomainLocalModule()->IsClassInitialized(pMT)) + return; + + FC_INNER_RETURN_VOID(InitDeclaringTypeHelper(pMT)); +} +FCIMPLEND +#include <optdefault.h> + +FCIMPL1(void*, StubHelpers::GetNDirectTarget, NDirectMethodDesc* pNMD) +{ + FCALL_CONTRACT; + + FCUnique(0xa2); + return pNMD->GetNDirectTarget(); +} +FCIMPLEND + +FCIMPL2(void*, StubHelpers::GetDelegateTarget, DelegateObject *pThisUNSAFE, UINT_PTR *ppStubArg) +{ + PCODE pEntryPoint = NULL; + +#ifdef _DEBUG + BEGIN_PRESERVE_LAST_ERROR; +#endif + + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(CheckPointer(pThisUNSAFE)); + } + CONTRACTL_END; + + DELEGATEREF orefThis = (DELEGATEREF)ObjectToOBJECTREF(pThisUNSAFE); + +#if defined(_TARGET_X86_) + // On x86 we wrap the call with a thunk that handles host notifications. + SyncBlock *pSyncBlock = orefThis->PassiveGetSyncBlock(); + if (pSyncBlock != NULL) + { + InteropSyncBlockInfo *pInteropInfo = pSyncBlock->GetInteropInfoNoCreate(); + if (pInteropInfo != NULL) + { + // we return entry point to a stub that wraps the real target + Stub *pInterceptStub = pInteropInfo->GetInterceptStub(); + if (pInterceptStub != NULL) + { + pEntryPoint = pInterceptStub->GetEntryPoint(); + } + } + } +#endif // _TARGET_X86_ + +#if defined(_WIN64) + UINT_PTR target = (UINT_PTR)orefThis->GetMethodPtrAux(); + + // The lowest bit is used to distinguish between MD and target on 64-bit. + target = (target << 1) | 1; + + // On 64-bit we pass the real target to the stub-for-host through this out argument, + // see IL code gen in NDirectStubLinker::DoNDirect for details. + *ppStubArg = target; + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + if (NDirect::IsHostHookEnabled()) + { + // There's one static stub on !_TARGET_X86_. + pEntryPoint = GetEEFuncEntryPoint(PInvokeStubForHost); + } +#endif // FEATURE_INCLUDE_ALL_INTERFACES +#elif defined(_TARGET_ARM_) + // @ARMTODO: Nothing to do for ARM yet since we don't support the hosted path. +#endif // _WIN64, _TARGET_ARM_ + + if (pEntryPoint == NULL) + { + pEntryPoint = orefThis->GetMethodPtrAux(); + } + +#ifdef _DEBUG + END_PRESERVE_LAST_ERROR; +#endif + + return (PVOID)pEntryPoint; +} +FCIMPLEND + +#ifndef FEATURE_CORECLR // CAS +static void DoDeclarativeActionsForPInvoke(MethodDesc* pCurrent) +{ + CONTRACTL + { + MODE_COOPERATIVE; + GC_TRIGGERS; + THROWS; + SO_INTOLERANT; + } + CONTRACTL_END; + + MethodSecurityDescriptor MDSecDesc(pCurrent); + MethodSecurityDescriptor::LookupOrCreateMethodSecurityDescriptor(&MDSecDesc); + + DeclActionInfo* pRuntimeDeclActionInfo = MDSecDesc.GetRuntimeDeclActionInfo(); + if (pRuntimeDeclActionInfo != NULL) + { + // Tell the debugger not to start on any managed code that we call in this method + FrameWithCookie<DebuggerSecurityCodeMarkFrame> __dbgSecFrame; + + Security::DoDeclarativeActions(pCurrent, pRuntimeDeclActionInfo, NULL, &MDSecDesc); + + // Pop the debugger frame + __dbgSecFrame.Pop(); + } +} +#endif // FEATURE_CORECLR + +#ifndef FEATURE_CORECLR +#ifndef _WIN64 +FCIMPL3(void*, StubHelpers::GetFinalStubTarget, LPVOID pStubArg, LPVOID pUnmngThis, DWORD dwFlags) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(SF_IsForwardStub(dwFlags)); + } + CONTRACTL_END; + + if (SF_IsCALLIStub(dwFlags)) + { + // stub argument is the target + return pStubArg; + } + else if (SF_IsDelegateStub(dwFlags)) + { + // stub argument is not used but we pass _methodPtrAux which is the target + return pStubArg; + } + else if (SF_IsCOMStub(dwFlags)) + { + // stub argument is a ComPlusCallMethodDesc + ComPlusCallMethodDesc *pCMD = (ComPlusCallMethodDesc *)pStubArg; + LPVOID *lpVtbl = *(LPVOID **)pUnmngThis; + return lpVtbl[pCMD->m_pComPlusCallInfo->m_cachedComSlot]; + } + else // P/Invoke + { + // secret stub argument is an NDirectMethodDesc + NDirectMethodDesc *pNMD = (NDirectMethodDesc *)pStubArg; + return pNMD->GetNativeNDirectTarget(); + } +} +FCIMPLEND +#endif // !_WIN64 + +FCIMPL1(void, StubHelpers::DemandPermission, NDirectMethodDesc *pNMD) +{ + FCALL_CONTRACT; + + // ETWOnStartup (SecurityCatchCall, SecurityCatchCallEnd); // this is messing up HMF below + + if (pNMD != NULL) + { + g_IBCLogger.LogMethodDescAccess(pNMD); + + if (pNMD->IsInterceptedForDeclSecurity()) + { + if (pNMD->IsInterceptedForDeclSecurityCASDemandsOnly() && + SecurityStackWalk::HasFlagsOrFullyTrusted(1 << SECURITY_UNMANAGED_CODE)) + { + // Track perfmon counters. Runtime security checks. + Security::IncrementSecurityPerfCounter(); + } + else + { + HELPER_METHOD_FRAME_BEGIN_0(); + DoDeclarativeActionsForPInvoke(pNMD); + HELPER_METHOD_FRAME_END(); + } + } + } + else + { + // This is either CLR->COM or delegate P/Invoke (we don't call this helper for CALLI). + HELPER_METHOD_FRAME_BEGIN_0(); + SecurityStackWalk::SpecialDemand(SSWT_DECLARATIVE_DEMAND, SECURITY_UNMANAGED_CODE); + HELPER_METHOD_FRAME_END(); + } +} +FCIMPLEND +#endif // !FEATURE_CORECLR + +FCIMPL2(void, StubHelpers::ThrowInteropParamException, UINT resID, UINT paramIdx) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + ::ThrowInteropParamException(resID, paramIdx); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +#ifdef FEATURE_COMINTEROP +FCIMPL1(void, StubHelpers::StubRegisterRCW, Object *unsafe_pThis) +{ + FCALL_CONTRACT; + + OBJECTREF oref = ObjectToOBJECTREF(unsafe_pThis); + HELPER_METHOD_FRAME_BEGIN_1(oref); + +#if defined(_DEBUG) && defined(FEATURE_MDA) + // Make sure that we only get here because the MDA is turned on. + MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup); + _ASSERTE(mda != NULL); +#endif // _DEBUG + + // RegisterRCW may throw OOM in which case we need to decrement the refcount on the RCW + class RCWDecrementUseCountHolder + { + public: + RCW *m_pRCW; + + RCWDecrementUseCountHolder(RCW *pRCW) + { + LIMITED_METHOD_CONTRACT; + m_pRCW = pRCW; + } + + ~RCWDecrementUseCountHolder() + { + WRAPPER_NO_CONTRACT; + if (m_pRCW != NULL) + { + m_pRCW->DecrementUseCount(); + } + } + }; + + RCWDecrementUseCountHolder holder(oref->GetSyncBlock()->GetInteropInfoNoCreate()->GetRCWAndIncrementUseCount()); + if (holder.m_pRCW == NULL) + { + COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_NO_LONGER_HAS_WRAPPER); + } + + GET_THREAD()->RegisterRCW(holder.m_pRCW); + + // if we made it here, suppress the DecrementUseCount call + holder.m_pRCW = NULL; + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL1(void, StubHelpers::StubUnregisterRCW, Object *unsafe_pThis) +{ + FCALL_CONTRACT; + + OBJECTREF oref = ObjectToOBJECTREF(unsafe_pThis); + HELPER_METHOD_FRAME_BEGIN_1(oref); + +#if defined(_DEBUG) && defined(FEATURE_MDA) + // Make sure that we only get here because the MDA is turned on. + MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup); + _ASSERTE(mda != NULL); +#endif // _DEBUG + + RCW *pRCW = GET_THREAD()->UnregisterRCW(INDEBUG(oref->GetSyncBlock())); + + if (pRCW != NULL) + { + // Thread::RegisterRCW incremented the use count, decrement it now + pRCW->DecrementUseCount(); + } + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +class COMInterfaceMarshalerCallback : public ICOMInterfaceMarshalerCallback +{ +public : + COMInterfaceMarshalerCallback(Thread *pThread, LPVOID pCtxCookie) + { + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + _ASSERTE(pThread != NULL); + _ASSERTE(pCtxCookie != NULL); + + m_bIsFreeThreaded = false; + m_pThread = pThread; + m_pCtxCookie = pCtxCookie; + + m_bIsDCOMProxy = false; + } + + virtual void OnRCWCreated(RCW *pRCW) + { + LIMITED_METHOD_CONTRACT; + + _ASSERTE(pRCW != NULL); + + if (pRCW->IsFreeThreaded()) + m_bIsFreeThreaded = true; + + if (pRCW->IsDCOMProxy()) + m_bIsDCOMProxy = true; + } + + // Return true if ComInterfaceMarshaler should use this RCW + // Return false if ComInterfaceMarshaler should just skip this RCW and proceed + // to create a duplicate one instead + virtual bool ShouldUseThisRCW(RCW *pRCW) + { + LIMITED_METHOD_CONTRACT; + + _ASSERTE(pRCW->SupportsIInspectable()); + + // Is this a free threaded RCW or a context-bound RCW created in the same context + if (pRCW->IsFreeThreaded() || + pRCW->GetWrapperCtxCookie() == m_pCtxCookie) + { + return true; + } + else + { + // + // Now we get back a WinRT factory RCW created in a different context. This means the + // factory is a singleton, and the returned IActivationFactory could be either one of + // the following: + // 1) A raw pointer, and it acts like a free threaded object + // 2) A proxy that is used across different contexts. It might maintain a list of contexts + // that it is marshaled to, and will fail to be called if it is not marshaled to this + // context yet. + // + // In this case, it is unsafe to use this RCW in this context and we should proceed + // to create a duplicated one instead. It might make sense to have a context-sensitive + // RCW cache but I don't think this case will be common enough to justify it + // + return false; + } + } + + virtual void OnRCWCacheHit(RCW *pRCW) + { + LIMITED_METHOD_CONTRACT; + + if (pRCW->IsFreeThreaded()) + m_bIsFreeThreaded = true; + + if (pRCW->IsDCOMProxy()) + m_bIsDCOMProxy = true; + } + + bool IsFreeThreaded() + { + LIMITED_METHOD_CONTRACT; + + return m_bIsFreeThreaded; + } + + bool IsDCOMProxy() + { + LIMITED_METHOD_CONTRACT; + + return m_bIsDCOMProxy; + } + +private : + Thread *m_pThread; // Current thread + LPVOID m_pCtxCookie; // Current context cookie + bool m_bIsFreeThreaded; // Whether we got back the RCW from a different context + bool m_bIsDCOMProxy; // Is this a proxy to an object in a different process +}; + +// +// Retrieve cached WinRT factory RCW or create a new one, according to the MethodDesc of the .ctor +// +FCIMPL1(Object*, StubHelpers::GetWinRTFactoryObject, MethodDesc *pCMD) +{ + FCALL_CONTRACT; + + OBJECTREF refFactory = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_1(refFactory); + + MethodTable *pMTOfTypeToCreate = pCMD->GetMethodTable(); + AppDomain *pDomain = GetAppDomain(); + + // + // Look up cached WinRT factory according to type to create + current context cookie + // For each type in AppDomain, we cache only the last WinRT factory object + // We don't cache factory per context in order to avoid explosion of objects if there are + // multiple STA apartments + // + // Note that if cached WinRT factory is FTM, we'll get it back regardless of the supplied cookie + // + LPVOID lpCtxCookie = GetCurrentCtxCookie(); + refFactory = pDomain->LookupWinRTFactoryObject(pMTOfTypeToCreate, lpCtxCookie); + if (refFactory == NULL) + { + // + // Didn't find a cached factory that matches the context + // Time to create a new factory and wrap it in a RCW + // + + // + // Creates a callback to checks for singleton WinRT factory during RCW creation + // + // If we get back an existing RCW from a different context, this callback + // will make the RCW a context-agile (but not free-threaded) RCW. Being context-agile + // in this case means RCW will not make any context transition. As long as we are only + // calling this RCW from where we got it back (using IInspectable* as identity), we should + // be fine (as we are supposed to call that pointer directly anyway) + // + // See code:COMInterfaceMarshalerCallback for more details + // + COMInterfaceMarshalerCallback callback(GET_THREAD(), lpCtxCookie); + + // + // Get the activation factory instance for this WinRT type and create a RCW for it + // + GetNativeWinRTFactoryObject( + pMTOfTypeToCreate, + GET_THREAD(), + ComPlusCallInfo::FromMethodDesc(pCMD)->m_pInterfaceMT, // Factory interface + FALSE, // Don't need a unique RCW + // it is only needed in WindowsRuntimeMarshal.GetActivationFactory API + &callback, + &refFactory); + + // + // If this is free-threaded factory RCW, set lpCtxCookie = NULL, which means + // this RCW can be used anywhere + // Otherwise, we can only use this RCW from current thread + // + if (callback.IsFreeThreaded()) + lpCtxCookie = NULL; + + // Cache the result in the AD-wide cache, unless this is a proxy to a DCOM object on Apollo. In the + // Apollo app model, out of process WinRT servers can have lifetimes independent of the application, + // and the cache may wind up with stale pointers if we save proxies to OOP factories. In addition, + // their app model is such that OOP WinRT objects cannot rely on having static state as they can be + // forcibly terminated at any point. Therefore, not caching an OOP WinRT factory object in Apollo + // does not lead to correctness issues. + // + // This is not the same on the desktop, where OOP objects may contain static state, and therefore + // we need to keep them alive. +#ifdef FEATURE_WINDOWSPHONE + if (!callback.IsDCOMProxy()) +#endif // FEATURE_WINDOWSPHONE + { + pDomain->CacheWinRTFactoryObject(pMTOfTypeToCreate, &refFactory, lpCtxCookie); + } + } + + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(refFactory); +} +FCIMPLEND + + +#endif + +#ifdef MDA_SUPPORTED +NOINLINE static void CheckCollectedDelegateMDAHelper(UMEntryThunk *pEntryThunk) +{ + FC_INNER_PROLOG(StubHelpers::CheckCollectedDelegateMDA); + HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); + + CallbackOnCollectedDelegateHelper(pEntryThunk); + + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); +} + +FCIMPL1(void, StubHelpers::CheckCollectedDelegateMDA, LPVOID pEntryThunk) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(pEntryThunk != NULL); + } + CONTRACTL_END; + + if (MDA_GET_ASSISTANT(CallbackOnCollectedDelegate) == NULL) + return; + + // keep this FCall as fast as possible for the "MDA is off" case + FC_INNER_RETURN_VOID(CheckCollectedDelegateMDAHelper((UMEntryThunk *)pEntryThunk)); +} +FCIMPLEND +#endif // MDA_SUPPORTED + +#ifdef PROFILING_SUPPORTED +FCIMPL3(SIZE_T, StubHelpers::ProfilerBeginTransitionCallback, SIZE_T pSecretParam, Thread* pThread, Object* unsafe_pThis) +{ + FCALL_CONTRACT; + + // We can get here with an ngen image generated with "/prof", + // even if the profiler doesn't want to track transitions. + if (!CORProfilerTrackTransitions()) + { + return NULL; + } + + MethodDesc* pRealMD = NULL; + + BEGIN_PRESERVE_LAST_ERROR; + + // We must transition to preemptive GC mode before calling out to the profiler, + // and the transition requires us to set up a HMF. + DELEGATEREF dref = (DELEGATEREF)ObjectToOBJECTREF(unsafe_pThis); + HELPER_METHOD_FRAME_BEGIN_RET_1(dref); + + bool fReverseInterop = false; + + if (NULL == pThread) + { + // This is our signal for the reverse interop cases. + fReverseInterop = true; + pThread = GET_THREAD(); + // the secret param in this casee is the UMEntryThunk + pRealMD = ((UMEntryThunk*)pSecretParam)->GetMethod(); + } + else + if (pSecretParam == 0) + { + // Secret param is null. This is the calli pinvoke case or the unmanaged delegate case. + // We have an unmanaged target address but no MD. For the unmanaged delegate case, we can + // still retrieve the MD by looking at the "this" object. + + if (dref == NULL) + { + // calli pinvoke case + pRealMD = NULL; + } + else + { + // unmanaged delegate case + MethodTable* pMT = dref->GetMethodTable(); + _ASSERTE(pMT->IsDelegate()); + + EEClass * pClass = pMT->GetClass(); + pRealMD = ((DelegateEEClass*)pClass)->m_pInvokeMethod; + _ASSERTE(pRealMD); + } + } + else + { + // This is either the COM interop or the pinvoke case. + pRealMD = (MethodDesc*)pSecretParam; + } + + { + GCX_PREEMP_THREAD_EXISTS(pThread); + + if (fReverseInterop) + { + ProfilerUnmanagedToManagedTransitionMD(pRealMD, COR_PRF_TRANSITION_CALL); + } + else + { + ProfilerManagedToUnmanagedTransitionMD(pRealMD, COR_PRF_TRANSITION_CALL); + } + } + + HELPER_METHOD_FRAME_END(); + + END_PRESERVE_LAST_ERROR; + + return (SIZE_T)pRealMD; +} +FCIMPLEND + +FCIMPL2(void, StubHelpers::ProfilerEndTransitionCallback, MethodDesc* pRealMD, Thread* pThread) +{ + FCALL_CONTRACT; + + // We can get here with an ngen image generated with "/prof", + // even if the profiler doesn't want to track transitions. + if (!CORProfilerTrackTransitions()) + { + return; + } + + BEGIN_PRESERVE_LAST_ERROR; + + // We must transition to preemptive GC mode before calling out to the profiler, + // and the transition requires us to set up a HMF. + HELPER_METHOD_FRAME_BEGIN_0(); + { + bool fReverseInterop = false; + + if (NULL == pThread) + { + // if pThread is null, we are doing reverse interop + pThread = GET_THREAD(); + fReverseInterop = true; + } + + GCX_PREEMP_THREAD_EXISTS(pThread); + + if (fReverseInterop) + { + ProfilerManagedToUnmanagedTransitionMD(pRealMD, COR_PRF_TRANSITION_RETURN); + } + else + { + ProfilerUnmanagedToManagedTransitionMD(pRealMD, COR_PRF_TRANSITION_RETURN); + } + } + HELPER_METHOD_FRAME_END(); + + END_PRESERVE_LAST_ERROR; +} +FCIMPLEND +#endif // PROFILING_SUPPORTED + +FCIMPL1(Object*, StubHelpers::GetHRExceptionObject, HRESULT hr) +{ + FCALL_CONTRACT; + + OBJECTREF oThrowable = NULL; + + HELPER_METHOD_FRAME_BEGIN_RET_1(oThrowable); + { + // GetExceptionForHR uses equivalant logic as COMPlusThrowHR + GetExceptionForHR(hr, &oThrowable); + } + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(oThrowable); +} +FCIMPLEND + +#ifdef FEATURE_COMINTEROP +FCIMPL4(Object*, StubHelpers::GetCOMHRExceptionObject, HRESULT hr, MethodDesc *pMD, Object *unsafe_pThis, CLR_BOOL fForWinRT) +{ + FCALL_CONTRACT; + + OBJECTREF oThrowable = NULL; + + // get 'this' + OBJECTREF oref = ObjectToOBJECTREF(unsafe_pThis); + + HELPER_METHOD_FRAME_BEGIN_RET_2(oref, oThrowable); + { + IErrorInfo *pErrInfo = NULL; + + IRestrictedErrorInfo *pResErrorInfo = NULL; + BOOL bHasNonCLRLanguageErrorObject = FALSE; + + if (fForWinRT) + { + SafeGetRestrictedErrorInfo(&pResErrorInfo); + if (pResErrorInfo != NULL) + { + // If we have a restricted error Info lets try and find the corresponding errorInfo, + // bHasNonCLRLanguageErrorObject can be TRUE|FALSE depending on whether we have an associtated LanguageExceptionObject + // and whether it is CLR exceptionObject => bHasNonCLRLanguageErrorObject = FALSE; + // or whether it is a non-CLRExceptionObject => bHasNonCLRLanguageErrorObject = TRUE; + pErrInfo = GetCorrepondingErrorInfo_WinRT(hr, pResErrorInfo, &bHasNonCLRLanguageErrorObject); + } + } + if (pErrInfo == NULL && pMD != NULL) + { + // Retrieve the interface method table. + MethodTable *pItfMT = ComPlusCallInfo::FromMethodDesc(pMD)->m_pInterfaceMT; + + // Get IUnknown pointer for this interface on this object + IUnknown* pUnk = ComObject::GetComIPFromRCW(&oref, pItfMT); + if (pUnk != NULL) + { + // Check to see if the component supports error information for this interface. + IID ItfIID; + pItfMT->GetGuid(&ItfIID, TRUE); + pErrInfo = GetSupportedErrorInfo(pUnk, ItfIID, !fForWinRT); + + DWORD cbRef = SafeRelease(pUnk); + LogInteropRelease(pUnk, cbRef, "IUnk to QI for ISupportsErrorInfo"); + } + } + + GetExceptionForHR(hr, pErrInfo, fForWinRT, &oThrowable, pResErrorInfo, bHasNonCLRLanguageErrorObject); + } + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(oThrowable); +} +FCIMPLEND +#endif // FEATURE_COMINTEROP + +FCIMPL3(void, StubHelpers::FmtClassUpdateNativeInternal, Object* pObjUNSAFE, BYTE* pbNative, OBJECTREF *ppCleanupWorkListOnStack) +{ + FCALL_CONTRACT; + + OBJECTREF pObj = ObjectToOBJECTREF(pObjUNSAFE); + HELPER_METHOD_FRAME_BEGIN_1(pObj); + + FmtClassUpdateNative(&pObj, pbNative, ppCleanupWorkListOnStack); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL2(void, StubHelpers::FmtClassUpdateCLRInternal, Object* pObjUNSAFE, BYTE* pbNative) +{ + FCALL_CONTRACT; + + OBJECTREF pObj = ObjectToOBJECTREF(pObjUNSAFE); + HELPER_METHOD_FRAME_BEGIN_1(pObj); + + FmtClassUpdateCLR(&pObj, pbNative); + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL2(void, StubHelpers::LayoutDestroyNativeInternal, BYTE* pbNative, MethodTable* pMT) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + LayoutDestroyNative(pbNative, pMT); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL1(Object*, StubHelpers::AllocateInternal, EnregisteredTypeHandle pRegisteredTypeHnd) +{ + FCALL_CONTRACT; + + TypeHandle typeHnd = TypeHandle::FromPtr(pRegisteredTypeHnd); + OBJECTREF objRet = NULL; + HELPER_METHOD_FRAME_BEGIN_RET_1(objRet); + + MethodTable* pMT = typeHnd.GetMethodTable(); + objRet = pMT->Allocate(); + + HELPER_METHOD_FRAME_END(); + + return OBJECTREFToObject(objRet); +} +FCIMPLEND + +FCIMPL1(void, StubHelpers::DecimalCanonicalizeInternal, DECIMAL *pDec) +{ + FCALL_CONTRACT; + + if (FAILED(DecimalCanonicalize(pDec))) + { + FCThrowResVoid(kOverflowException, W("Overflow_Currency")); + } +} +FCIMPLEND + +FCIMPL1(int, StubHelpers::AnsiStrlen, __in_z char* pszStr) +{ + FCALL_CONTRACT; + + size_t len = strlen(pszStr); + + // the length should have been checked earlier (see StubHelpers.CheckStringLength) + _ASSERTE(FitsInI4(len)); + + return (int)len; +} +FCIMPLEND + +FCIMPL3(void, StubHelpers::MarshalToUnmanagedVaListInternal, va_list va, DWORD cbVaListSize, const VARARGS* pArgIterator) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + VARARGS::MarshalToUnmanagedVaList(va, cbVaListSize, pArgIterator); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL2(void, StubHelpers::MarshalToManagedVaListInternal, va_list va, VARARGS* pArgIterator) +{ + FCALL_CONTRACT; + + VARARGS::MarshalToManagedVaList(va, pArgIterator); +} +FCIMPLEND + +FCIMPL3(void, StubHelpers::ValidateObject, Object *pObjUNSAFE, MethodDesc *pMD, Object *pThisUNSAFE) +{ + FCALL_CONTRACT; + +#ifdef VERIFY_HEAP + HELPER_METHOD_FRAME_BEGIN_0(); + + StackSString errorString; + EX_TRY + { + AVInRuntimeImplOkayHolder AVOkay; + // don't validate the next object if a BGC is in progress. we can race with background + // sweep which could make the next object a Free object underneath us if it's dead. + ValidateObjectInternal(pObjUNSAFE, !(GCHeap::GetGCHeap()->IsConcurrentGCInProgress())); + } + EX_CATCH + { + FormatValidationMessage(ResolveInteropMethod(pThisUNSAFE, pMD), errorString); + EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, errorString.GetUnicode()); + } + EX_END_CATCH_UNREACHABLE; + + HELPER_METHOD_FRAME_END(); +#else // VERIFY_HEAP + FCUnique(0xa3); + UNREACHABLE_MSG("No validation support without VERIFY_HEAP"); +#endif // VERIFY_HEAP +} +FCIMPLEND + +FCIMPL3(void, StubHelpers::ValidateByref, void *pByref, MethodDesc *pMD, Object *pThisUNSAFE) +{ + FCALL_CONTRACT; + +#ifdef VERIFY_HEAP + // We cannot validate byrefs at this point as code:GCHeap.GetContainingObject could potentially race + // with allocations on other threads. We'll just remember this byref along with the interop MD and + // perform the validation on next GC (see code:StubHelpers.ProcessByrefValidationList). + + // Skip byref if is not pointing inside managed heap + if (!GCHeap::GetGCHeap()->IsHeapPointer(pByref)) + { + return; + } + ByrefValidationEntry entry; + entry.pByref = pByref; + entry.pMD = ResolveInteropMethod(pThisUNSAFE, pMD); + + HELPER_METHOD_FRAME_BEGIN_0(); + + SIZE_T NumOfEntries = 0; + { + CrstHolder ch(&s_ByrefValidationLock); + + if (s_ByrefValidationIndex >= s_ByrefValidationEntries.Size()) + { + // The validation list grows as necessary, for simplicity we never shrink it. + SIZE_T newSize; + if (!ClrSafeInt<SIZE_T>::multiply(s_ByrefValidationIndex, 2, newSize) || + !ClrSafeInt<SIZE_T>::addition(newSize, 1, newSize)) + { + ThrowHR(COR_E_OVERFLOW); + } + + s_ByrefValidationEntries.ReSizeThrows(newSize); + _ASSERTE(s_ByrefValidationIndex < s_ByrefValidationEntries.Size()); + } + + s_ByrefValidationEntries[s_ByrefValidationIndex] = entry; + NumOfEntries = ++s_ByrefValidationIndex; + } + + if (NumOfEntries > BYREF_VALIDATION_LIST_MAX_SIZE) + { + // if the list is too big, trigger GC now + GCHeap::GetGCHeap()->GarbageCollect(0); + } + + HELPER_METHOD_FRAME_END(); +#else // VERIFY_HEAP + FCUnique(0xa4); + UNREACHABLE_MSG("No validation support without VERIFY_HEAP"); +#endif // VERIFY_HEAP +} +FCIMPLEND + +FCIMPL0(void*, StubHelpers::GetStubContext) +{ + FCALL_CONTRACT; + + FCUnique(0xa0); + UNREACHABLE_MSG_RET("This is a JIT intrinsic!"); +} +FCIMPLEND + +FCIMPL2(void, StubHelpers::LogPinnedArgument, MethodDesc *target, Object *pinnedArg) +{ + FCALL_CONTRACT; + + SIZE_T managedSize = 0; + + if (pinnedArg != NULL) + { + // Can pass null objects to interop, only check the size if the object is valid. + managedSize = pinnedArg->GetSize(); + } + + if (target != NULL) + { + STRESS_LOG3(LF_STUBS, LL_INFO100, "Managed object %#X with size '%#X' pinned for interop to Method [%pM]\n", pinnedArg, managedSize, target); + } + else + { + STRESS_LOG2(LF_STUBS, LL_INFO100, "Managed object %#X pinned for interop with size '%#X'", pinnedArg, managedSize); + } +} +FCIMPLEND + +#ifdef _WIN64 +FCIMPL0(void*, StubHelpers::GetStubContextAddr) +{ + FCALL_CONTRACT; + + FCUnique(0xa1); + UNREACHABLE_MSG("This is a JIT intrinsic!"); +} +FCIMPLEND +#endif // _WIN64 + +#ifdef MDA_SUPPORTED +FCIMPL0(void, StubHelpers::TriggerGCForMDA) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_0(); + TriggerGCForMDAInternal(); + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND +#endif // MDA_SUPPORTED + +FCIMPL1(DWORD, StubHelpers::CalcVaListSize, VARARGS *varargs) +{ + FCALL_CONTRACT; + + return VARARGS::CalcVaListSize(varargs); +} +FCIMPLEND + +#ifdef FEATURE_ARRAYSTUB_AS_IL +NOINLINE static void ArrayTypeCheckSlow(Object* element, PtrArray* arr) +{ + FC_INNER_PROLOG(StubHelpers::ArrayTypeCheck); + HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); + + if (!ObjIsInstanceOf(element, arr->GetArrayElementTypeHandle())) + COMPlusThrow(kArrayTypeMismatchException); + + HELPER_METHOD_FRAME_END(); + + FC_INNER_EPILOG(); +} + +FCIMPL2(void, StubHelpers::ArrayTypeCheck, Object* element, PtrArray* arr) +{ + FCALL_CONTRACT; + + if (ObjIsInstanceOfNoGC(element, arr->GetArrayElementTypeHandle()) == TypeHandle::CanCast) + return; + + FC_INNER_RETURN_VOID(ArrayTypeCheckSlow(element, arr)); +} +FCIMPLEND +#endif // FEATURE_ARRAYSTUB_AS_IL + +#ifdef FEATURE_STUBS_AS_IL +FCIMPL2(void, StubHelpers::MulticastDebuggerTraceHelper, Object* element, INT32 count) +{ + FCALL_CONTRACT; + FCUnique(0xa5); +} +FCIMPLEND +#endif // FEATURE_STUBS_AS_IL |