text: // 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 "eventtrace.h"
  #include "comdatetime.h"
  #include "gcheaputilities.h"
  #include "interoputil.h"
  
  #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(GCHeapUtilities::GetGCHeap()->RuntimeStructuresValid());
  
  	// 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 = GCHeapUtilities::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())->GetInvokeMethod();
      }
      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 = GCHeapUtilities::GetGCHeap()->GetContainingObject(entry.pByref, false);
              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_V(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;
  
  
      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();
      }
  
  
      *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
          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;
      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;
      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();
  
      // See code:GenericPInvokeCalliHelper
      // 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;
  
  #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
  
  
  
  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)->GetInvokeMethod();
              _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, !(GCHeapUtilities::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 (!GCHeapUtilities::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
          GCHeapUtilities::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 _TARGET_64BIT_
  FCIMPL0(void*, StubHelpers::GetStubContextAddr)
  {
      FCALL_CONTRACT;
  
      FCUnique(0xa1);
      UNREACHABLE_MSG("This is a JIT intrinsic!");
  }
  FCIMPLEND
  #endif // _TARGET_64BIT_
  
  #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_MULTICASTSTUB_AS_IL
  FCIMPL2(void, StubHelpers::MulticastDebuggerTraceHelper, Object* element, INT32 count)
  {
      FCALL_CONTRACT;
      FCUnique(0xa5);
  }
  FCIMPLEND
  #endif // FEATURE_MULTICASTSTUB_AS_IL
  