summaryrefslogtreecommitdiff
path: root/src/vm/stubhelpers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/stubhelpers.cpp')
-rw-r--r--src/vm/stubhelpers.cpp2176
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