| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | // |
| 5 | // File: stubhelpers.cpp |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | |
| 10 | |
| 11 | #include "common.h" |
| 12 | |
| 13 | #include "mlinfo.h" |
| 14 | #include "stubhelpers.h" |
| 15 | #include "jitinterface.h" |
| 16 | #include "dllimport.h" |
| 17 | #include "fieldmarshaler.h" |
| 18 | #include "comdelegate.h" |
| 19 | #include "eventtrace.h" |
| 20 | #include "comdatetime.h" |
| 21 | #include "gcheaputilities.h" |
| 22 | #include "interoputil.h" |
| 23 | |
| 24 | #ifdef FEATURE_COMINTEROP |
| 25 | #include <oletls.h> |
| 26 | #include "olecontexthelpers.h" |
| 27 | #include "runtimecallablewrapper.h" |
| 28 | #include "comcallablewrapper.h" |
| 29 | #include "clrtocomcall.h" |
| 30 | #include "cominterfacemarshaler.h" |
| 31 | #include "winrttypenameconverter.h" |
| 32 | #endif |
| 33 | |
| 34 | #ifdef VERIFY_HEAP |
| 35 | |
| 36 | CQuickArray<StubHelpers::ByrefValidationEntry> StubHelpers::s_ByrefValidationEntries; |
| 37 | SIZE_T StubHelpers::s_ByrefValidationIndex = 0; |
| 38 | CrstStatic StubHelpers::s_ByrefValidationLock; |
| 39 | |
| 40 | // static |
| 41 | void StubHelpers::Init() |
| 42 | { |
| 43 | WRAPPER_NO_CONTRACT; |
| 44 | s_ByrefValidationLock.Init(CrstPinnedByrefValidation); |
| 45 | } |
| 46 | |
| 47 | // static |
| 48 | void StubHelpers::ValidateObjectInternal(Object *pObjUNSAFE, BOOL fValidateNextObj) |
| 49 | { |
| 50 | CONTRACTL |
| 51 | { |
| 52 | NOTHROW; |
| 53 | GC_NOTRIGGER; |
| 54 | MODE_ANY; |
| 55 | SO_TOLERANT; |
| 56 | } |
| 57 | CONTRACTL_END; |
| 58 | |
| 59 | _ASSERTE(GCHeapUtilities::GetGCHeap()->RuntimeStructuresValid()); |
| 60 | |
| 61 | // validate the object - there's no need to validate next object's |
| 62 | // header since we validate the next object explicitly below |
| 63 | pObjUNSAFE->Validate(/*bDeep=*/ TRUE, /*bVerifyNextHeader=*/ FALSE, /*bVerifySyncBlock=*/ TRUE); |
| 64 | |
| 65 | // and the next object as required |
| 66 | if (fValidateNextObj) |
| 67 | { |
| 68 | Object *nextObj = GCHeapUtilities::GetGCHeap()->NextObj(pObjUNSAFE); |
| 69 | if (nextObj != NULL) |
| 70 | { |
| 71 | // Note that the MethodTable of the object (i.e. the pointer at offset 0) can change from |
| 72 | // g_pFreeObjectMethodTable to NULL, from NULL to <legal-value>, or possibly also from |
| 73 | // g_pFreeObjectMethodTable to <legal-value> concurrently while executing this function. |
| 74 | // Once <legal-value> is seen, we believe that the object should pass the Validate check. |
| 75 | // We have to be careful and read the pointer only once to avoid "phantom reads". |
| 76 | MethodTable *pMT = VolatileLoad(nextObj->GetMethodTablePtr()); |
| 77 | if (pMT != NULL && pMT != g_pFreeObjectMethodTable) |
| 78 | { |
| 79 | // do *not* verify the next object's syncblock - the next object is not guaranteed to |
| 80 | // be "alive" so the finalizer thread may have already released its syncblock |
| 81 | nextObj->Validate(/*bDeep=*/ TRUE, /*bVerifyNextHeader=*/ FALSE, /*bVerifySyncBlock=*/ FALSE); |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | // static |
| 88 | MethodDesc *StubHelpers::ResolveInteropMethod(Object *pThisUNSAFE, MethodDesc *pMD) |
| 89 | { |
| 90 | WRAPPER_NO_CONTRACT; |
| 91 | |
| 92 | if (pMD == NULL && pThisUNSAFE != NULL) |
| 93 | { |
| 94 | // if this is a call via delegate, get its Invoke method |
| 95 | MethodTable *pMT = pThisUNSAFE->GetMethodTable(); |
| 96 | |
| 97 | _ASSERTE(pMT->IsDelegate()); |
| 98 | return ((DelegateEEClass *)pMT->GetClass())->GetInvokeMethod(); |
| 99 | } |
| 100 | return pMD; |
| 101 | } |
| 102 | |
| 103 | // static |
| 104 | void StubHelpers::FormatValidationMessage(MethodDesc *pMD, SString &ssErrorString) |
| 105 | { |
| 106 | CONTRACTL |
| 107 | { |
| 108 | THROWS; |
| 109 | GC_NOTRIGGER; |
| 110 | MODE_ANY; |
| 111 | } |
| 112 | CONTRACTL_END; |
| 113 | |
| 114 | ssErrorString.Append(W("Detected managed heap corruption, likely culprit is interop call through " )); |
| 115 | |
| 116 | if (pMD == NULL) |
| 117 | { |
| 118 | // the only case where we don't have interop MD is CALLI |
| 119 | ssErrorString.Append(W("CALLI." )); |
| 120 | } |
| 121 | else |
| 122 | { |
| 123 | ssErrorString.Append(W("method '" )); |
| 124 | |
| 125 | StackSString ssClassName; |
| 126 | pMD->GetMethodTable()->_GetFullyQualifiedNameForClass(ssClassName); |
| 127 | |
| 128 | ssErrorString.Append(ssClassName); |
| 129 | ssErrorString.Append(NAMESPACE_SEPARATOR_CHAR); |
| 130 | ssErrorString.AppendUTF8(pMD->GetName()); |
| 131 | |
| 132 | ssErrorString.Append(W("'." )); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | // static |
| 137 | void StubHelpers::ProcessByrefValidationList() |
| 138 | { |
| 139 | CONTRACTL |
| 140 | { |
| 141 | NOTHROW; |
| 142 | GC_NOTRIGGER; |
| 143 | MODE_ANY; |
| 144 | } |
| 145 | CONTRACTL_END; |
| 146 | |
| 147 | StackSString errorString; |
| 148 | ByrefValidationEntry entry = { NULL, NULL }; |
| 149 | |
| 150 | EX_TRY |
| 151 | { |
| 152 | AVInRuntimeImplOkayHolder AVOkay; |
| 153 | |
| 154 | // Process all byref validation entries we have saved since the last GC. Note that EE is suspended at |
| 155 | // this point so we don't have to take locks and we can safely call code:GCHeap.GetContainingObject. |
| 156 | for (SIZE_T i = 0; i < s_ByrefValidationIndex; i++) |
| 157 | { |
| 158 | entry = s_ByrefValidationEntries[i]; |
| 159 | |
| 160 | Object *pObjUNSAFE = GCHeapUtilities::GetGCHeap()->GetContainingObject(entry.pByref, false); |
| 161 | ValidateObjectInternal(pObjUNSAFE, TRUE); |
| 162 | } |
| 163 | } |
| 164 | EX_CATCH |
| 165 | { |
| 166 | EX_TRY |
| 167 | { |
| 168 | FormatValidationMessage(entry.pMD, errorString); |
| 169 | EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, errorString.GetUnicode()); |
| 170 | } |
| 171 | EX_CATCH |
| 172 | { |
| 173 | EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); |
| 174 | } |
| 175 | EX_END_CATCH_UNREACHABLE; |
| 176 | } |
| 177 | EX_END_CATCH_UNREACHABLE; |
| 178 | |
| 179 | s_ByrefValidationIndex = 0; |
| 180 | } |
| 181 | |
| 182 | #endif // VERIFY_HEAP |
| 183 | |
| 184 | FCIMPL1_V(double, StubHelpers::DateMarshaler__ConvertToNative, INT64 managedDate) |
| 185 | { |
| 186 | FCALL_CONTRACT; |
| 187 | |
| 188 | double retval = 0.0; |
| 189 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
| 190 | retval = COMDateTime::TicksToDoubleDate(managedDate); |
| 191 | HELPER_METHOD_FRAME_END(); |
| 192 | return retval; |
| 193 | } |
| 194 | FCIMPLEND |
| 195 | |
| 196 | FCIMPL1_V(INT64, StubHelpers::DateMarshaler__ConvertToManaged, double nativeDate) |
| 197 | { |
| 198 | FCALL_CONTRACT; |
| 199 | |
| 200 | INT64 retval = 0; |
| 201 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
| 202 | retval = COMDateTime::DoubleDateToTicks(nativeDate); |
| 203 | HELPER_METHOD_FRAME_END(); |
| 204 | return retval; |
| 205 | } |
| 206 | FCIMPLEND |
| 207 | |
| 208 | FCIMPL4(void, StubHelpers::ValueClassMarshaler__ConvertToNative, LPVOID pDest, LPVOID pSrc, MethodTable* pMT, OBJECTREF *ppCleanupWorkListOnStack) |
| 209 | { |
| 210 | FCALL_CONTRACT; |
| 211 | |
| 212 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 213 | FmtValueTypeUpdateNative(&pSrc, pMT, (BYTE*)pDest, ppCleanupWorkListOnStack); |
| 214 | HELPER_METHOD_FRAME_END(); |
| 215 | } |
| 216 | FCIMPLEND |
| 217 | |
| 218 | FCIMPL3(void, StubHelpers::ValueClassMarshaler__ConvertToManaged, LPVOID pDest, LPVOID pSrc, MethodTable* pMT) |
| 219 | { |
| 220 | FCALL_CONTRACT; |
| 221 | |
| 222 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 223 | FmtValueTypeUpdateCLR(&pDest, pMT, (BYTE*)pSrc); |
| 224 | HELPER_METHOD_FRAME_END_POLL(); |
| 225 | } |
| 226 | FCIMPLEND |
| 227 | |
| 228 | FCIMPL2(void, StubHelpers::ValueClassMarshaler__ClearNative, LPVOID pDest, MethodTable* pMT) |
| 229 | { |
| 230 | FCALL_CONTRACT; |
| 231 | |
| 232 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 233 | FmtClassDestroyNative(pDest, pMT); |
| 234 | HELPER_METHOD_FRAME_END_POLL(); |
| 235 | } |
| 236 | FCIMPLEND |
| 237 | |
| 238 | #ifdef FEATURE_COMINTEROP |
| 239 | |
| 240 | FORCEINLINE static void GetCOMIPFromRCW_ClearFP() |
| 241 | { |
| 242 | LIMITED_METHOD_CONTRACT; |
| 243 | |
| 244 | #ifdef _TARGET_X86_ |
| 245 | // As per ASURT 146699 we need to clear FP state before calling to COM |
| 246 | // the following sequence was previously generated to compiled ML stubs |
| 247 | // and is faster than _clearfp(). |
| 248 | __asm |
| 249 | { |
| 250 | fnstsw ax |
| 251 | and eax, 0x3F |
| 252 | jz NoNeedToClear |
| 253 | fnclex |
| 254 | NoNeedToClear: |
| 255 | } |
| 256 | #endif // _TARGET_X86_ |
| 257 | } |
| 258 | |
| 259 | FORCEINLINE static SOleTlsData *GetOrCreateOleTlsData() |
| 260 | { |
| 261 | LIMITED_METHOD_CONTRACT; |
| 262 | |
| 263 | SOleTlsData *pOleTlsData; |
| 264 | #ifdef _TARGET_X86_ |
| 265 | // This saves 1 memory instruction over NtCurretTeb()->ReservedForOle because |
| 266 | // NtCurrentTeb() reads _TEB.NtTib.Self which is the same as what FS:0 already |
| 267 | // points to. |
| 268 | pOleTlsData = (SOleTlsData *)(ULONG_PTR)__readfsdword(offsetof(TEB, ReservedForOle)); |
| 269 | #else // _TARGET_X86_ |
| 270 | pOleTlsData = (SOleTlsData *)NtCurrentTeb()->ReservedForOle; |
| 271 | #endif // _TARGET_X86_ |
| 272 | if (pOleTlsData == NULL) |
| 273 | { |
| 274 | pOleTlsData = (SOleTlsData *)SetupOleContext(); |
| 275 | } |
| 276 | return pOleTlsData; |
| 277 | } |
| 278 | |
| 279 | FORCEINLINE static void *GetCOMIPFromRCW_GetTargetNoInterception(IUnknown *pUnk, ComPlusCallInfo *pComInfo) |
| 280 | { |
| 281 | LIMITED_METHOD_CONTRACT; |
| 282 | |
| 283 | #ifdef _TARGET_X86_ |
| 284 | _ASSERTE(pComInfo->m_pInterceptStub == NULL || pComInfo->m_pInterceptStub == (LPVOID)-1); |
| 285 | _ASSERTE(!pComInfo->HasCopyCtorArgs()); |
| 286 | #endif // _TARGET_X86_ |
| 287 | |
| 288 | LPVOID *lpVtbl = *(LPVOID **)pUnk; |
| 289 | return lpVtbl[pComInfo->m_cachedComSlot]; |
| 290 | } |
| 291 | |
| 292 | FORCEINLINE static IUnknown *GetCOMIPFromRCW_GetIUnknownFromRCWCache(RCW *pRCW, MethodTable * pItfMT) |
| 293 | { |
| 294 | LIMITED_METHOD_CONTRACT; |
| 295 | |
| 296 | // The code in this helper is the "fast path" that used to be generated directly |
| 297 | // to compiled ML stubs. The idea is to aim for an efficient RCW cache hit. |
| 298 | SOleTlsData * pOleTlsData = GetOrCreateOleTlsData(); |
| 299 | |
| 300 | // test for free-threaded after testing for context match to optimize for apartment-bound objects |
| 301 | if (pOleTlsData->pCurrentCtx == pRCW->GetWrapperCtxCookie() || pRCW->IsFreeThreaded()) |
| 302 | { |
| 303 | for (int i = 0; i < INTERFACE_ENTRY_CACHE_SIZE; i++) |
| 304 | { |
| 305 | if (pRCW->m_aInterfaceEntries[i].m_pMT == pItfMT) |
| 306 | { |
| 307 | return pRCW->m_aInterfaceEntries[i].m_pUnknown; |
| 308 | } |
| 309 | } |
| 310 | } |
| 311 | |
| 312 | // also search the auxiliary cache if it's available |
| 313 | RCWAuxiliaryData *pAuxData = pRCW->m_pAuxiliaryData; |
| 314 | if (pAuxData != NULL) |
| 315 | { |
| 316 | LPVOID pCtxCookie = (pRCW->IsFreeThreaded() ? NULL : pOleTlsData->pCurrentCtx); |
| 317 | return pAuxData->FindInterfacePointer(pItfMT, pCtxCookie); |
| 318 | } |
| 319 | |
| 320 | return NULL; |
| 321 | } |
| 322 | |
| 323 | // Like GetCOMIPFromRCW_GetIUnknownFromRCWCache but also computes the target. This is a couple of instructions |
| 324 | // faster than GetCOMIPFromRCW_GetIUnknownFromRCWCache + GetCOMIPFromRCW_GetTargetNoInterception. |
| 325 | FORCEINLINE static IUnknown *GetCOMIPFromRCW_GetIUnknownFromRCWCache_NoInterception(RCW *pRCW, ComPlusCallInfo *pComInfo, void **ppTarget) |
| 326 | { |
| 327 | LIMITED_METHOD_CONTRACT; |
| 328 | |
| 329 | // The code in this helper is the "fast path" that used to be generated directly |
| 330 | // to compiled ML stubs. The idea is to aim for an efficient RCW cache hit. |
| 331 | SOleTlsData *pOleTlsData = GetOrCreateOleTlsData(); |
| 332 | MethodTable *pItfMT = pComInfo->m_pInterfaceMT; |
| 333 | |
| 334 | // test for free-threaded after testing for context match to optimize for apartment-bound objects |
| 335 | if (pOleTlsData->pCurrentCtx == pRCW->GetWrapperCtxCookie() || pRCW->IsFreeThreaded()) |
| 336 | { |
| 337 | for (int i = 0; i < INTERFACE_ENTRY_CACHE_SIZE; i++) |
| 338 | { |
| 339 | if (pRCW->m_aInterfaceEntries[i].m_pMT == pItfMT) |
| 340 | { |
| 341 | IUnknown *pUnk = pRCW->m_aInterfaceEntries[i].m_pUnknown; |
| 342 | _ASSERTE(pUnk != NULL); |
| 343 | *ppTarget = GetCOMIPFromRCW_GetTargetNoInterception(pUnk, pComInfo); |
| 344 | return pUnk; |
| 345 | } |
| 346 | } |
| 347 | } |
| 348 | |
| 349 | // also search the auxiliary cache if it's available |
| 350 | RCWAuxiliaryData *pAuxData = pRCW->m_pAuxiliaryData; |
| 351 | if (pAuxData != NULL) |
| 352 | { |
| 353 | LPVOID pCtxCookie = (pRCW->IsFreeThreaded() ? NULL : pOleTlsData->pCurrentCtx); |
| 354 | |
| 355 | IUnknown *pUnk = pAuxData->FindInterfacePointer(pItfMT, pCtxCookie); |
| 356 | if (pUnk != NULL) |
| 357 | { |
| 358 | *ppTarget = GetCOMIPFromRCW_GetTargetNoInterception(pUnk, pComInfo); |
| 359 | return pUnk; |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | return NULL; |
| 364 | } |
| 365 | |
| 366 | FORCEINLINE static void *GetCOMIPFromRCW_GetTarget(IUnknown *pUnk, ComPlusCallInfo *pComInfo) |
| 367 | { |
| 368 | LIMITED_METHOD_CONTRACT; |
| 369 | |
| 370 | |
| 371 | LPVOID *lpVtbl = *(LPVOID **)pUnk; |
| 372 | return lpVtbl[pComInfo->m_cachedComSlot]; |
| 373 | } |
| 374 | |
| 375 | NOINLINE static IUnknown* GetCOMIPFromRCWHelper(LPVOID pFCall, OBJECTREF pSrc, MethodDesc* pMD, void **ppTarget) |
| 376 | { |
| 377 | FC_INNER_PROLOG(pFCall); |
| 378 | |
| 379 | IUnknown *pIntf = NULL; |
| 380 | |
| 381 | // This is only called in IL stubs which are in CER, so we don't need to worry about ThreadAbort |
| 382 | 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); |
| 383 | |
| 384 | SafeComHolder<IUnknown> pRetUnk; |
| 385 | |
| 386 | ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD); |
| 387 | pRetUnk = ComObject::GetComIPFromRCWThrowing(&pSrc, pComInfo->m_pInterfaceMT); |
| 388 | |
| 389 | if (pFCall == StubHelpers::GetCOMIPFromRCW_WinRT || |
| 390 | pFCall == StubHelpers::GetCOMIPFromRCW_WinRTSharedGeneric || |
| 391 | pFCall == StubHelpers::GetCOMIPFromRCW_WinRTDelegate) |
| 392 | { |
| 393 | pRetUnk.Release(); |
| 394 | } |
| 395 | |
| 396 | |
| 397 | *ppTarget = GetCOMIPFromRCW_GetTarget(pRetUnk, pComInfo); |
| 398 | _ASSERTE(*ppTarget != NULL); |
| 399 | |
| 400 | GetCOMIPFromRCW_ClearFP(); |
| 401 | |
| 402 | pIntf = pRetUnk.Extract(); |
| 403 | |
| 404 | // No exception will be thrown here (including thread abort as it is delayed in IL stubs) |
| 405 | HELPER_METHOD_FRAME_END(); |
| 406 | |
| 407 | FC_INNER_EPILOG(); |
| 408 | return pIntf; |
| 409 | } |
| 410 | |
| 411 | //================================================================================================================== |
| 412 | // The GetCOMIPFromRCW helper exists in four specialized versions to optimize CLR->COM perf. Please be careful when |
| 413 | // changing this code as one of these methods is executed as part of every CLR->COM call so every instruction counts. |
| 414 | //================================================================================================================== |
| 415 | |
| 416 | |
| 417 | #include <optsmallperfcritical.h> |
| 418 | |
| 419 | // This helper can handle any CLR->COM call (classic COM, WinRT, WinRT delegate, WinRT generic), it supports hosting, |
| 420 | // and clears FP state on x86 for compatibility with VB6. |
| 421 | FCIMPL4(IUnknown*, StubHelpers::GetCOMIPFromRCW, Object* pSrcUNSAFE, MethodDesc* pMD, void **ppTarget, CLR_BOOL* pfNeedsRelease) |
| 422 | { |
| 423 | CONTRACTL |
| 424 | { |
| 425 | FCALL_CHECK; |
| 426 | PRECONDITION(pMD->IsComPlusCall() || pMD->IsGenericComPlusCall() || pMD->IsEEImpl()); |
| 427 | } |
| 428 | CONTRACTL_END; |
| 429 | |
| 430 | OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); |
| 431 | *pfNeedsRelease = false; |
| 432 | |
| 433 | ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD); |
| 434 | RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); |
| 435 | if (pRCW != NULL) |
| 436 | { |
| 437 | |
| 438 | IUnknown * pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache(pRCW, pComInfo->m_pInterfaceMT); |
| 439 | if (pUnk != NULL) |
| 440 | { |
| 441 | *ppTarget = GetCOMIPFromRCW_GetTarget(pUnk, pComInfo); |
| 442 | if (*ppTarget != NULL) |
| 443 | { |
| 444 | GetCOMIPFromRCW_ClearFP(); |
| 445 | return pUnk; |
| 446 | } |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ |
| 451 | *pfNeedsRelease = true; |
| 452 | FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW, pSrc, pMD, ppTarget)); |
| 453 | } |
| 454 | FCIMPLEND |
| 455 | |
| 456 | // This helper can handle only non-generic WinRT calls, does not support hosting/interception, and does not clear FP state. |
| 457 | FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW_WinRT, Object* pSrcUNSAFE, MethodDesc* pMD, void** ppTarget) |
| 458 | { |
| 459 | CONTRACTL |
| 460 | { |
| 461 | FCALL_CHECK; |
| 462 | PRECONDITION(pMD->IsComPlusCall()); |
| 463 | } |
| 464 | CONTRACTL_END; |
| 465 | |
| 466 | OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); |
| 467 | |
| 468 | ComPlusCallInfo *pComInfo = ((ComPlusCallMethodDesc *)pMD)->m_pComPlusCallInfo; |
| 469 | RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); |
| 470 | if (pRCW != NULL) |
| 471 | { |
| 472 | IUnknown *pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache_NoInterception(pRCW, pComInfo, ppTarget); |
| 473 | if (pUnk != NULL) |
| 474 | { |
| 475 | return pUnk; |
| 476 | } |
| 477 | } |
| 478 | |
| 479 | /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ |
| 480 | FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW_WinRT, pSrc, pMD, ppTarget)); |
| 481 | } |
| 482 | FCIMPLEND |
| 483 | |
| 484 | // This helper can handle only generic WinRT calls, does not support hosting, and does not clear FP state. |
| 485 | FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW_WinRTSharedGeneric, Object* pSrcUNSAFE, MethodDesc* pMD, void** ppTarget) |
| 486 | { |
| 487 | CONTRACTL |
| 488 | { |
| 489 | FCALL_CHECK; |
| 490 | PRECONDITION(pMD->IsGenericComPlusCall()); |
| 491 | } |
| 492 | CONTRACTL_END; |
| 493 | |
| 494 | OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); |
| 495 | |
| 496 | ComPlusCallInfo *pComInfo = pMD->AsInstantiatedMethodDesc()->IMD_GetComPlusCallInfo(); |
| 497 | RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); |
| 498 | if (pRCW != NULL) |
| 499 | { |
| 500 | IUnknown *pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache_NoInterception(pRCW, pComInfo, ppTarget); |
| 501 | if (pUnk != NULL) |
| 502 | { |
| 503 | return pUnk; |
| 504 | } |
| 505 | } |
| 506 | |
| 507 | /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ |
| 508 | FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW_WinRTSharedGeneric, pSrc, pMD, ppTarget)); |
| 509 | } |
| 510 | FCIMPLEND |
| 511 | |
| 512 | // This helper can handle only delegate WinRT calls, does not support hosting, and does not clear FP state. |
| 513 | FCIMPL3(IUnknown*, StubHelpers::GetCOMIPFromRCW_WinRTDelegate, Object* pSrcUNSAFE, MethodDesc* pMD, void** ppTarget) |
| 514 | { |
| 515 | CONTRACTL |
| 516 | { |
| 517 | FCALL_CHECK; |
| 518 | PRECONDITION(pMD->IsEEImpl()); |
| 519 | } |
| 520 | CONTRACTL_END; |
| 521 | |
| 522 | OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); |
| 523 | |
| 524 | ComPlusCallInfo *pComInfo = ((DelegateEEClass *)pMD->GetClass())->m_pComPlusCallInfo; |
| 525 | RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); |
| 526 | if (pRCW != NULL) |
| 527 | { |
| 528 | IUnknown *pUnk = GetCOMIPFromRCW_GetIUnknownFromRCWCache_NoInterception(pRCW, pComInfo, ppTarget); |
| 529 | if (pUnk != NULL) |
| 530 | { |
| 531 | return pUnk; |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | /* if we didn't find the COM interface pointer in the cache we will have to erect an HMF */ |
| 536 | FC_INNER_RETURN(IUnknown*, GetCOMIPFromRCWHelper(StubHelpers::GetCOMIPFromRCW_WinRTDelegate, pSrc, pMD, ppTarget)); |
| 537 | } |
| 538 | FCIMPLEND |
| 539 | |
| 540 | #include <optdefault.h> |
| 541 | |
| 542 | |
| 543 | NOINLINE static FC_BOOL_RET ShouldCallWinRTInterfaceHelper(RCW *pRCW, MethodTable *pItfMT) |
| 544 | { |
| 545 | FC_INNER_PROLOG(StubHelpers::ShouldCallWinRTInterface); |
| 546 | |
| 547 | bool result = false; |
| 548 | |
| 549 | HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); |
| 550 | |
| 551 | // call the GC-triggering version |
| 552 | result = pRCW->SupportsWinRTInteropInterface(pItfMT); |
| 553 | |
| 554 | HELPER_METHOD_FRAME_END(); |
| 555 | FC_INNER_EPILOG(); |
| 556 | |
| 557 | FC_RETURN_BOOL(result); |
| 558 | } |
| 559 | |
| 560 | FCIMPL2(FC_BOOL_RET, StubHelpers::ShouldCallWinRTInterface, Object *pSrcUNSAFE, MethodDesc *pMD) |
| 561 | { |
| 562 | FCALL_CONTRACT; |
| 563 | |
| 564 | OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); |
| 565 | |
| 566 | ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD); |
| 567 | RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); |
| 568 | if (pRCW == NULL) |
| 569 | { |
| 570 | // Pretend that we are not redirected WinRT type |
| 571 | // We'll throw InvalidComObjectException later in GetComIPFromRCW |
| 572 | return false; |
| 573 | } |
| 574 | |
| 575 | TypeHandle::CastResult result = pRCW->SupportsWinRTInteropInterfaceNoGC(pComInfo->m_pInterfaceMT); |
| 576 | switch (result) |
| 577 | { |
| 578 | case TypeHandle::CanCast: FC_RETURN_BOOL(true); |
| 579 | case TypeHandle::CannotCast: FC_RETURN_BOOL(false); |
| 580 | } |
| 581 | |
| 582 | FC_INNER_RETURN(FC_BOOL_RET, ShouldCallWinRTInterfaceHelper(pRCW, pComInfo->m_pInterfaceMT)); |
| 583 | } |
| 584 | FCIMPLEND |
| 585 | |
| 586 | NOINLINE static DelegateObject *GetTargetForAmbiguousVariantCallHelper(RCW *pRCW, MethodTable *pMT, BOOL fIsEnumerable, CLR_BOOL *pfUseString) |
| 587 | { |
| 588 | FC_INNER_PROLOG(StubHelpers::GetTargetForAmbiguousVariantCall); |
| 589 | |
| 590 | DelegateObject *pRetVal = NULL; |
| 591 | |
| 592 | HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); |
| 593 | |
| 594 | // Note that if the call succeeds, it will set the right OBJECTHANDLE/flags on the RCW so we won't have to do this |
| 595 | // next time. If the call fails, we don't care because it is an error and an exception will be thrown later. |
| 596 | SafeComHolder<IUnknown> pUnk = pRCW->GetComIPFromRCW(pMT); |
| 597 | |
| 598 | WinRTInterfaceRedirector::WinRTLegalStructureBaseType baseType = WinRTInterfaceRedirector::GetStructureBaseType(pMT->GetInstantiation()); |
| 599 | |
| 600 | BOOL fUseString = FALSE; |
| 601 | BOOL fUseT = FALSE; |
| 602 | pRetVal = (DelegateObject *)OBJECTREFToObject(pRCW->GetTargetForAmbiguousVariantCall(fIsEnumerable, baseType, &fUseString, &fUseT)); |
| 603 | |
| 604 | *pfUseString = !!fUseString; |
| 605 | |
| 606 | HELPER_METHOD_FRAME_END(); |
| 607 | FC_INNER_EPILOG(); |
| 608 | |
| 609 | return pRetVal; |
| 610 | } |
| 611 | |
| 612 | // Performs a run-time check to see how an ambiguous variant call on an RCW should be handled. Returns a delegate which should |
| 613 | // be called, or sets *pfUseString to true which means that the caller should use the <string> instantiation. If NULL is returned |
| 614 | // and *pfUseString is false, the caller should attempt to handle the call as usual. |
| 615 | FCIMPL3(DelegateObject*, StubHelpers::GetTargetForAmbiguousVariantCall, Object *pSrcUNSAFE, MethodTable *pMT, CLR_BOOL *pfUseString) |
| 616 | { |
| 617 | FCALL_CONTRACT; |
| 618 | |
| 619 | OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); |
| 620 | |
| 621 | RCW *pRCW = pSrc->PassiveGetSyncBlock()->GetInteropInfoNoCreate()->GetRawRCW(); |
| 622 | if (pRCW == NULL) |
| 623 | { |
| 624 | // ignore this - the call we'll attempt to make later will throw the right exception |
| 625 | *pfUseString = false; |
| 626 | return NULL; |
| 627 | } |
| 628 | |
| 629 | BOOL fIsEnumerable = pMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__IENUMERABLEGENERIC)); |
| 630 | _ASSERTE(fIsEnumerable || pMT->HasSameTypeDefAs(MscorlibBinder::GetExistingClass(CLASS__IREADONLYLISTGENERIC))); |
| 631 | |
| 632 | WinRTInterfaceRedirector::WinRTLegalStructureBaseType baseType = WinRTInterfaceRedirector::GetStructureBaseType(pMT->GetInstantiation()); |
| 633 | |
| 634 | BOOL fUseString = FALSE; |
| 635 | BOOL fUseT = FALSE; |
| 636 | DelegateObject *pRetVal = (DelegateObject *)OBJECTREFToObject(pRCW->GetTargetForAmbiguousVariantCall(fIsEnumerable, baseType, &fUseString, &fUseT)); |
| 637 | |
| 638 | if (pRetVal != NULL || fUseT || fUseString) |
| 639 | { |
| 640 | *pfUseString = !!fUseString; |
| 641 | return pRetVal; |
| 642 | } |
| 643 | |
| 644 | // we haven't seen QI for the interface yet, trigger it now |
| 645 | FC_INNER_RETURN(DelegateObject*, GetTargetForAmbiguousVariantCallHelper(pRCW, pMT, fIsEnumerable, pfUseString)); |
| 646 | } |
| 647 | FCIMPLEND |
| 648 | |
| 649 | FCIMPL2(void, StubHelpers::ObjectMarshaler__ConvertToNative, Object* pSrcUNSAFE, VARIANT* pDest) |
| 650 | { |
| 651 | FCALL_CONTRACT; |
| 652 | |
| 653 | OBJECTREF pSrc = ObjectToOBJECTREF(pSrcUNSAFE); |
| 654 | |
| 655 | HELPER_METHOD_FRAME_BEGIN_1(pSrc); |
| 656 | if (pDest->vt & VT_BYREF) |
| 657 | { |
| 658 | OleVariant::MarshalOleRefVariantForObject(&pSrc, pDest); |
| 659 | } |
| 660 | else |
| 661 | { |
| 662 | OleVariant::MarshalOleVariantForObject(&pSrc, pDest); |
| 663 | } |
| 664 | HELPER_METHOD_FRAME_END(); |
| 665 | } |
| 666 | FCIMPLEND |
| 667 | |
| 668 | FCIMPL1(Object*, StubHelpers::ObjectMarshaler__ConvertToManaged, VARIANT* pSrc) |
| 669 | { |
| 670 | FCALL_CONTRACT; |
| 671 | |
| 672 | OBJECTREF retVal = NULL; |
| 673 | HELPER_METHOD_FRAME_BEGIN_RET_1(retVal); |
| 674 | // The IL stub is going to call ObjectMarshaler__ClearNative() afterwards. |
| 675 | // If it doesn't it's a bug in ILObjectMarshaler. |
| 676 | OleVariant::MarshalObjectForOleVariant(pSrc, &retVal); |
| 677 | HELPER_METHOD_FRAME_END(); |
| 678 | |
| 679 | return OBJECTREFToObject(retVal); |
| 680 | } |
| 681 | FCIMPLEND |
| 682 | |
| 683 | FCIMPL1(void, StubHelpers::ObjectMarshaler__ClearNative, VARIANT* pSrc) |
| 684 | { |
| 685 | FCALL_CONTRACT; |
| 686 | |
| 687 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 688 | SafeVariantClear(pSrc); |
| 689 | HELPER_METHOD_FRAME_END(); |
| 690 | } |
| 691 | FCIMPLEND |
| 692 | |
| 693 | #include <optsmallperfcritical.h> |
| 694 | FCIMPL4(IUnknown*, StubHelpers::InterfaceMarshaler__ConvertToNative, Object* pObjUNSAFE, MethodTable* pItfMT, MethodTable* pClsMT, DWORD dwFlags) |
| 695 | { |
| 696 | FCALL_CONTRACT; |
| 697 | |
| 698 | if (NULL == pObjUNSAFE) |
| 699 | { |
| 700 | return NULL; |
| 701 | } |
| 702 | |
| 703 | IUnknown *pIntf = NULL; |
| 704 | OBJECTREF pObj = ObjectToOBJECTREF(pObjUNSAFE); |
| 705 | |
| 706 | // This is only called in IL stubs which are in CER, so we don't need to worry about ThreadAbort |
| 707 | HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_NO_THREAD_ABORT, pObj); |
| 708 | |
| 709 | pIntf = MarshalObjectToInterface(&pObj, pItfMT, pClsMT, dwFlags); |
| 710 | |
| 711 | // No exception will be thrown here (including thread abort as it is delayed in IL stubs) |
| 712 | HELPER_METHOD_FRAME_END(); |
| 713 | |
| 714 | return pIntf; |
| 715 | } |
| 716 | FCIMPLEND |
| 717 | |
| 718 | FCIMPL4(Object*, StubHelpers::InterfaceMarshaler__ConvertToManaged, IUnknown **ppUnk, MethodTable *pItfMT, MethodTable *pClsMT, DWORD dwFlags) |
| 719 | { |
| 720 | FCALL_CONTRACT; |
| 721 | |
| 722 | if (NULL == *ppUnk) |
| 723 | { |
| 724 | return NULL; |
| 725 | } |
| 726 | |
| 727 | OBJECTREF pObj = NULL; |
| 728 | HELPER_METHOD_FRAME_BEGIN_RET_1(pObj); |
| 729 | |
| 730 | UnmarshalObjectFromInterface(&pObj, ppUnk, pItfMT, pClsMT, dwFlags); |
| 731 | |
| 732 | HELPER_METHOD_FRAME_END(); |
| 733 | |
| 734 | return OBJECTREFToObject(pObj); |
| 735 | } |
| 736 | FCIMPLEND |
| 737 | |
| 738 | void QCALLTYPE StubHelpers::InterfaceMarshaler__ClearNative(IUnknown * pUnk) |
| 739 | { |
| 740 | QCALL_CONTRACT; |
| 741 | |
| 742 | BEGIN_QCALL_SO_TOLERANT; |
| 743 | |
| 744 | ULONG cbRef = SafeReleasePreemp(pUnk); |
| 745 | LogInteropRelease(pUnk, cbRef, "InterfaceMarshalerBase::ClearNative: In/Out release" ); |
| 746 | |
| 747 | END_QCALL_SO_TOLERANT; |
| 748 | } |
| 749 | #include <optdefault.h> |
| 750 | |
| 751 | |
| 752 | |
| 753 | |
| 754 | FCIMPL1(StringObject*, StubHelpers::UriMarshaler__GetRawUriFromNative, ABI::Windows::Foundation::IUriRuntimeClass* pIUriRC) |
| 755 | { |
| 756 | FCALL_CONTRACT; |
| 757 | |
| 758 | if (NULL == pIUriRC) |
| 759 | { |
| 760 | return NULL; |
| 761 | } |
| 762 | |
| 763 | STRINGREF strRef = NULL; |
| 764 | UINT32 cchRawUri = 0; |
| 765 | LPCWSTR pwszRawUri = NULL; |
| 766 | |
| 767 | HELPER_METHOD_FRAME_BEGIN_RET_1(strRef); |
| 768 | |
| 769 | WinRtString hsRawUriName; |
| 770 | |
| 771 | { |
| 772 | GCX_PREEMP(); |
| 773 | |
| 774 | // Get the RawUri string from the WinRT URI object |
| 775 | IfFailThrow(pIUriRC->get_RawUri(hsRawUriName.Address())); |
| 776 | |
| 777 | pwszRawUri = hsRawUriName.GetRawBuffer(&cchRawUri); |
| 778 | } |
| 779 | |
| 780 | strRef = StringObject::NewString(pwszRawUri, cchRawUri); |
| 781 | |
| 782 | HELPER_METHOD_FRAME_END(); |
| 783 | |
| 784 | return STRINGREFToObject(strRef); |
| 785 | } |
| 786 | FCIMPLEND |
| 787 | |
| 788 | FCIMPL2(IUnknown*, StubHelpers::UriMarshaler__CreateNativeUriInstance, WCHAR* pRawUri, UINT strLen) |
| 789 | { |
| 790 | CONTRACTL { |
| 791 | FCALL_CHECK; |
| 792 | PRECONDITION(CheckPointer(pRawUri)); |
| 793 | } |
| 794 | CONTRACTL_END; |
| 795 | |
| 796 | ABI::Windows::Foundation::IUriRuntimeClass* pIUriRC = NULL; |
| 797 | |
| 798 | HELPER_METHOD_FRAME_BEGIN_RET_0(); |
| 799 | |
| 800 | GCX_PREEMP(); |
| 801 | pIUriRC = CreateWinRTUri(pRawUri, strLen); |
| 802 | |
| 803 | HELPER_METHOD_FRAME_END(); |
| 804 | |
| 805 | return pIUriRC; |
| 806 | } |
| 807 | FCIMPLEND |
| 808 | |
| 809 | // A helper to convert an IP to object using special flags. |
| 810 | FCIMPL1(Object *, StubHelpers::InterfaceMarshaler__ConvertToManagedWithoutUnboxing, IUnknown *pNative) |
| 811 | { |
| 812 | FCALL_CONTRACT; |
| 813 | |
| 814 | OBJECTREF oref = NULL; |
| 815 | |
| 816 | HELPER_METHOD_FRAME_BEGIN_RET_1(oref); |
| 817 | |
| 818 | // |
| 819 | // Get a wrapper for pNative |
| 820 | // Note that we need to skip WinRT unboxing at this point because |
| 821 | // 1. We never know whether GetObjectRefFromComIP went through unboxing path. |
| 822 | // For example, user could just pass a IUnknown * to T and we'll happily convert that to T |
| 823 | // 2. If for some reason we end up getting something that does not implement IReference<T>, |
| 824 | // we'll get a nice message later when we do the cast in CLRIReferenceImpl.UnboxHelper |
| 825 | // |
| 826 | GetObjectRefFromComIP( |
| 827 | &oref, |
| 828 | pNative, // pUnk |
| 829 | g_pBaseCOMObject, // Use __ComObject |
| 830 | NULL, // pItfMT |
| 831 | ObjFromComIP::CLASS_IS_HINT | // No cast check - we'll do cast later |
| 832 | ObjFromComIP::UNIQUE_OBJECT | // Do not cache the object - To ensure that the unboxing code is called on this object always |
| 833 | // and the object is not retrieved from the cache as an __ComObject. |
| 834 | // Don't call GetRuntimeClassName - I just want a RCW of __ComObject |
| 835 | ObjFromComIP::IGNORE_WINRT_AND_SKIP_UNBOXING // Skip unboxing |
| 836 | ); |
| 837 | |
| 838 | HELPER_METHOD_FRAME_END(); |
| 839 | |
| 840 | return OBJECTREFToObject(oref); |
| 841 | } |
| 842 | FCIMPLEND |
| 843 | |
| 844 | FCIMPL2(StringObject *, StubHelpers::WinRTTypeNameConverter__ConvertToWinRTTypeName, |
| 845 | ReflectClassBaseObject *pTypeUNSAFE, CLR_BOOL *pbIsWinRTPrimitive) |
| 846 | { |
| 847 | CONTRACTL |
| 848 | { |
| 849 | FCALL_CHECK; |
| 850 | PRECONDITION(CheckPointer(pTypeUNSAFE)); |
| 851 | PRECONDITION(CheckPointer(pbIsWinRTPrimitive)); |
| 852 | } |
| 853 | CONTRACTL_END; |
| 854 | |
| 855 | REFLECTCLASSBASEREF refClass = (REFLECTCLASSBASEREF) pTypeUNSAFE; |
| 856 | STRINGREF refString= NULL; |
| 857 | HELPER_METHOD_FRAME_BEGIN_RET_2(refClass, refString); |
| 858 | |
| 859 | SString strWinRTTypeName; |
| 860 | bool bIsPrimitive; |
| 861 | if (WinRTTypeNameConverter::AppendWinRTTypeNameForManagedType( |
| 862 | refClass->GetType(), // thManagedType |
| 863 | strWinRTTypeName, // strWinRTTypeName to append |
| 864 | FALSE, // for type conversion, not for GetRuntimeClassName |
| 865 | &bIsPrimitive |
| 866 | )) |
| 867 | { |
| 868 | *pbIsWinRTPrimitive = bIsPrimitive; |
| 869 | refString = AllocateString(strWinRTTypeName); |
| 870 | } |
| 871 | else |
| 872 | { |
| 873 | *pbIsWinRTPrimitive = FALSE; |
| 874 | refString = NULL; |
| 875 | } |
| 876 | |
| 877 | HELPER_METHOD_FRAME_END(); |
| 878 | |
| 879 | return STRINGREFToObject(refString); |
| 880 | } |
| 881 | FCIMPLEND |
| 882 | |
| 883 | FCIMPL2(ReflectClassBaseObject *, StubHelpers::WinRTTypeNameConverter__GetTypeFromWinRTTypeName, StringObject *pWinRTTypeNameUNSAFE, CLR_BOOL *pbIsPrimitive) |
| 884 | { |
| 885 | CONTRACTL |
| 886 | { |
| 887 | FCALL_CHECK; |
| 888 | PRECONDITION(CheckPointer(pWinRTTypeNameUNSAFE)); |
| 889 | } |
| 890 | CONTRACTL_END; |
| 891 | |
| 892 | OBJECTREF refClass = NULL; |
| 893 | STRINGREF refString = ObjectToSTRINGREF(pWinRTTypeNameUNSAFE); |
| 894 | HELPER_METHOD_FRAME_BEGIN_RET_2(refClass, refString); |
| 895 | |
| 896 | bool isPrimitive; |
| 897 | TypeHandle th = WinRTTypeNameConverter::GetManagedTypeFromWinRTTypeName(refString->GetBuffer(), &isPrimitive); |
| 898 | *pbIsPrimitive = isPrimitive; |
| 899 | |
| 900 | refClass = th.GetManagedClassObject(); |
| 901 | |
| 902 | HELPER_METHOD_FRAME_END(); |
| 903 | |
| 904 | return (ReflectClassBaseObject *)OBJECTREFToObject(refClass); |
| 905 | } |
| 906 | FCIMPLEND |
| 907 | |
| 908 | FCIMPL1(MethodDesc*, StubHelpers::GetDelegateInvokeMethod, DelegateObject *pThisUNSAFE) |
| 909 | { |
| 910 | FCALL_CONTRACT; |
| 911 | |
| 912 | MethodDesc *pMD = NULL; |
| 913 | |
| 914 | OBJECTREF pThis = ObjectToOBJECTREF(pThisUNSAFE); |
| 915 | HELPER_METHOD_FRAME_BEGIN_RET_1(pThis); |
| 916 | |
| 917 | MethodTable *pDelMT = pThis->GetMethodTable(); |
| 918 | |
| 919 | pMD = COMDelegate::FindDelegateInvokeMethod(pDelMT); |
| 920 | if (pMD->IsSharedByGenericInstantiations()) |
| 921 | { |
| 922 | // we need the exact MethodDesc |
| 923 | pMD = InstantiatedMethodDesc::FindOrCreateExactClassMethod(pDelMT, pMD); |
| 924 | } |
| 925 | |
| 926 | HELPER_METHOD_FRAME_END(); |
| 927 | |
| 928 | _ASSERTE(pMD); |
| 929 | return pMD; |
| 930 | } |
| 931 | FCIMPLEND |
| 932 | |
| 933 | // Called from COM-to-CLR factory method stubs to get the return value (the delegating interface pointer |
| 934 | // corresponding to the default WinRT interface of the class which we are constructing). |
| 935 | FCIMPL2(IInspectable *, StubHelpers::GetWinRTFactoryReturnValue, Object *pThisUNSAFE, PCODE pCtorEntry) |
| 936 | { |
| 937 | FCALL_CONTRACT; |
| 938 | |
| 939 | IInspectable *pInsp = NULL; |
| 940 | |
| 941 | OBJECTREF pThis = ObjectToOBJECTREF(pThisUNSAFE); |
| 942 | HELPER_METHOD_FRAME_BEGIN_RET_1(pThis); |
| 943 | |
| 944 | // COM-to-CLR stubs use the target method entry point as their stub context |
| 945 | MethodDesc *pCtorMD = Entry2MethodDesc(pCtorEntry, NULL); |
| 946 | MethodTable *pClassMT = pCtorMD->GetMethodTable(); |
| 947 | |
| 948 | // make sure that we talk to the right CCW |
| 949 | ComCallWrapperTemplate *pTemplate = ComCallWrapperTemplate::GetTemplate(TypeHandle(pClassMT)); |
| 950 | CCWHolder pWrap = ComCallWrapper::InlineGetWrapper(&pThis, pTemplate); |
| 951 | |
| 952 | MethodTable *pDefaultItf = pClassMT->GetDefaultWinRTInterface(); |
| 953 | const IID &riid = (pDefaultItf == NULL ? IID_IInspectable : IID_NULL); |
| 954 | |
| 955 | pInsp = static_cast<IInspectable *>(ComCallWrapper::GetComIPFromCCW(pWrap, riid, pDefaultItf, |
| 956 | GetComIPFromCCW::CheckVisibility)); |
| 957 | |
| 958 | HELPER_METHOD_FRAME_END(); |
| 959 | |
| 960 | return pInsp; |
| 961 | } |
| 962 | FCIMPLEND |
| 963 | |
| 964 | // Called from CLR-to-COM factory method stubs to get the outer IInspectable to pass |
| 965 | // to the underlying factory object. |
| 966 | FCIMPL2(IInspectable *, StubHelpers::GetOuterInspectable, Object *pThisUNSAFE, MethodDesc *pCtorMD) |
| 967 | { |
| 968 | FCALL_CONTRACT; |
| 969 | |
| 970 | IInspectable *pInsp = NULL; |
| 971 | |
| 972 | OBJECTREF pThis = ObjectToOBJECTREF(pThisUNSAFE); |
| 973 | |
| 974 | if (pThis->GetMethodTable() != pCtorMD->GetMethodTable()) |
| 975 | { |
| 976 | // this is a composition scenario |
| 977 | HELPER_METHOD_FRAME_BEGIN_RET_1(pThis); |
| 978 | |
| 979 | // we don't have the "outer" yet, marshal the object |
| 980 | pInsp = static_cast<IInspectable *> |
| 981 | (MarshalObjectToInterface(&pThis, NULL, NULL, ItfMarshalInfo::ITF_MARSHAL_INSP_ITF | ItfMarshalInfo::ITF_MARSHAL_USE_BASIC_ITF)); |
| 982 | |
| 983 | HELPER_METHOD_FRAME_END(); |
| 984 | } |
| 985 | |
| 986 | return pInsp; |
| 987 | } |
| 988 | FCIMPLEND |
| 989 | |
| 990 | #ifdef MDA_SUPPORTED |
| 991 | FCIMPL2(ExceptionObject*, StubHelpers::TriggerExceptionSwallowedMDA, ExceptionObject* pExceptionUNSAFE, PCODE pManagedTarget) |
| 992 | { |
| 993 | FCALL_CONTRACT; |
| 994 | OBJECTREF pException = ObjectToOBJECTREF(pExceptionUNSAFE); |
| 995 | HELPER_METHOD_FRAME_BEGIN_RET_1(pException); |
| 996 | |
| 997 | // COM-to-CLR stubs use the target method entry point as their stub context |
| 998 | MethodDesc * pMD = Entry2MethodDesc(pManagedTarget, NULL); |
| 999 | |
| 1000 | MDA_TRIGGER_ASSISTANT(ExceptionSwallowedOnCallFromCom, ReportViolation(pMD, &pException)); |
| 1001 | |
| 1002 | HELPER_METHOD_FRAME_END(); |
| 1003 | return (ExceptionObject*)OBJECTREFToObject(pException); |
| 1004 | } |
| 1005 | FCIMPLEND |
| 1006 | #endif // MDA_SUPPORTED |
| 1007 | |
| 1008 | #endif // FEATURE_COMINTEROP |
| 1009 | |
| 1010 | FCIMPL0(void, StubHelpers::SetLastError) |
| 1011 | { |
| 1012 | // 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 |
| 1013 | DWORD lastError = ::GetLastError(); |
| 1014 | |
| 1015 | FCALL_CONTRACT; |
| 1016 | |
| 1017 | GetThread()->m_dwLastError = lastError; |
| 1018 | } |
| 1019 | FCIMPLEND |
| 1020 | |
| 1021 | FCIMPL0(void, StubHelpers::ClearLastError) |
| 1022 | { |
| 1023 | FCALL_CONTRACT; |
| 1024 | |
| 1025 | ::SetLastError(0); |
| 1026 | } |
| 1027 | FCIMPLEND |
| 1028 | |
| 1029 | FCIMPL1(FC_BOOL_RET, StubHelpers::IsQCall, NDirectMethodDesc* pNMD) |
| 1030 | { |
| 1031 | FCALL_CONTRACT; |
| 1032 | FC_RETURN_BOOL(pNMD->IsQCall()); |
| 1033 | } |
| 1034 | FCIMPLEND |
| 1035 | |
| 1036 | NOINLINE static void InitDeclaringTypeHelper(MethodTable *pMT) |
| 1037 | { |
| 1038 | FC_INNER_PROLOG(StubHelpers::InitDeclaringType); |
| 1039 | |
| 1040 | HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); |
| 1041 | pMT->CheckRunClassInitThrowing(); |
| 1042 | HELPER_METHOD_FRAME_END(); |
| 1043 | |
| 1044 | FC_INNER_EPILOG(); |
| 1045 | } |
| 1046 | |
| 1047 | // Triggers cctor of pNMD's declarer, similar to code:JIT_InitClass. |
| 1048 | #include <optsmallperfcritical.h> |
| 1049 | FCIMPL1(void, StubHelpers::InitDeclaringType, NDirectMethodDesc* pNMD) |
| 1050 | { |
| 1051 | FCALL_CONTRACT; |
| 1052 | |
| 1053 | MethodTable *pMT = pNMD->GetMethodTable(); |
| 1054 | _ASSERTE(!pMT->IsClassPreInited()); |
| 1055 | |
| 1056 | if (pMT->GetDomainLocalModule()->IsClassInitialized(pMT)) |
| 1057 | return; |
| 1058 | |
| 1059 | FC_INNER_RETURN_VOID(InitDeclaringTypeHelper(pMT)); |
| 1060 | } |
| 1061 | FCIMPLEND |
| 1062 | #include <optdefault.h> |
| 1063 | |
| 1064 | FCIMPL1(void*, StubHelpers::GetNDirectTarget, NDirectMethodDesc* pNMD) |
| 1065 | { |
| 1066 | FCALL_CONTRACT; |
| 1067 | |
| 1068 | FCUnique(0xa2); |
| 1069 | return pNMD->GetNDirectTarget(); |
| 1070 | } |
| 1071 | FCIMPLEND |
| 1072 | |
| 1073 | FCIMPL2(void*, StubHelpers::GetDelegateTarget, DelegateObject *pThisUNSAFE, UINT_PTR *ppStubArg) |
| 1074 | { |
| 1075 | PCODE pEntryPoint = NULL; |
| 1076 | |
| 1077 | #ifdef _DEBUG |
| 1078 | BEGIN_PRESERVE_LAST_ERROR; |
| 1079 | #endif |
| 1080 | |
| 1081 | CONTRACTL |
| 1082 | { |
| 1083 | FCALL_CHECK; |
| 1084 | PRECONDITION(CheckPointer(pThisUNSAFE)); |
| 1085 | } |
| 1086 | CONTRACTL_END; |
| 1087 | |
| 1088 | DELEGATEREF orefThis = (DELEGATEREF)ObjectToOBJECTREF(pThisUNSAFE); |
| 1089 | |
| 1090 | #if defined(_TARGET_X86_) |
| 1091 | // On x86 we wrap the call with a thunk that handles host notifications. |
| 1092 | SyncBlock *pSyncBlock = orefThis->PassiveGetSyncBlock(); |
| 1093 | if (pSyncBlock != NULL) |
| 1094 | { |
| 1095 | InteropSyncBlockInfo *pInteropInfo = pSyncBlock->GetInteropInfoNoCreate(); |
| 1096 | if (pInteropInfo != NULL) |
| 1097 | { |
| 1098 | // we return entry point to a stub that wraps the real target |
| 1099 | Stub *pInterceptStub = pInteropInfo->GetInterceptStub(); |
| 1100 | if (pInterceptStub != NULL) |
| 1101 | { |
| 1102 | pEntryPoint = pInterceptStub->GetEntryPoint(); |
| 1103 | } |
| 1104 | } |
| 1105 | } |
| 1106 | #endif // _TARGET_X86_ |
| 1107 | |
| 1108 | #if defined(_WIN64) |
| 1109 | UINT_PTR target = (UINT_PTR)orefThis->GetMethodPtrAux(); |
| 1110 | |
| 1111 | // See code:GenericPInvokeCalliHelper |
| 1112 | // The lowest bit is used to distinguish between MD and target on 64-bit. |
| 1113 | target = (target << 1) | 1; |
| 1114 | |
| 1115 | // On 64-bit we pass the real target to the stub-for-host through this out argument, |
| 1116 | // see IL code gen in NDirectStubLinker::DoNDirect for details. |
| 1117 | *ppStubArg = target; |
| 1118 | |
| 1119 | #elif defined(_TARGET_ARM_) |
| 1120 | // @ARMTODO: Nothing to do for ARM yet since we don't support the hosted path. |
| 1121 | #endif // _WIN64, _TARGET_ARM_ |
| 1122 | |
| 1123 | if (pEntryPoint == NULL) |
| 1124 | { |
| 1125 | pEntryPoint = orefThis->GetMethodPtrAux(); |
| 1126 | } |
| 1127 | |
| 1128 | #ifdef _DEBUG |
| 1129 | END_PRESERVE_LAST_ERROR; |
| 1130 | #endif |
| 1131 | |
| 1132 | return (PVOID)pEntryPoint; |
| 1133 | } |
| 1134 | FCIMPLEND |
| 1135 | |
| 1136 | |
| 1137 | |
| 1138 | FCIMPL2(void, StubHelpers::ThrowInteropParamException, UINT resID, UINT paramIdx) |
| 1139 | { |
| 1140 | FCALL_CONTRACT; |
| 1141 | |
| 1142 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 1143 | ::ThrowInteropParamException(resID, paramIdx); |
| 1144 | HELPER_METHOD_FRAME_END(); |
| 1145 | } |
| 1146 | FCIMPLEND |
| 1147 | |
| 1148 | #ifdef FEATURE_COMINTEROP |
| 1149 | FCIMPL1(void, StubHelpers::StubRegisterRCW, Object *unsafe_pThis) |
| 1150 | { |
| 1151 | FCALL_CONTRACT; |
| 1152 | |
| 1153 | OBJECTREF oref = ObjectToOBJECTREF(unsafe_pThis); |
| 1154 | HELPER_METHOD_FRAME_BEGIN_1(oref); |
| 1155 | |
| 1156 | #if defined(_DEBUG) && defined(FEATURE_MDA) |
| 1157 | // Make sure that we only get here because the MDA is turned on. |
| 1158 | MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup); |
| 1159 | _ASSERTE(mda != NULL); |
| 1160 | #endif // _DEBUG |
| 1161 | |
| 1162 | // RegisterRCW may throw OOM in which case we need to decrement the refcount on the RCW |
| 1163 | class RCWDecrementUseCountHolder |
| 1164 | { |
| 1165 | public: |
| 1166 | RCW *m_pRCW; |
| 1167 | |
| 1168 | RCWDecrementUseCountHolder(RCW *pRCW) |
| 1169 | { |
| 1170 | LIMITED_METHOD_CONTRACT; |
| 1171 | m_pRCW = pRCW; |
| 1172 | } |
| 1173 | |
| 1174 | ~RCWDecrementUseCountHolder() |
| 1175 | { |
| 1176 | WRAPPER_NO_CONTRACT; |
| 1177 | if (m_pRCW != NULL) |
| 1178 | { |
| 1179 | m_pRCW->DecrementUseCount(); |
| 1180 | } |
| 1181 | } |
| 1182 | }; |
| 1183 | |
| 1184 | RCWDecrementUseCountHolder holder(oref->GetSyncBlock()->GetInteropInfoNoCreate()->GetRCWAndIncrementUseCount()); |
| 1185 | if (holder.m_pRCW == NULL) |
| 1186 | { |
| 1187 | COMPlusThrow(kInvalidComObjectException, IDS_EE_COM_OBJECT_NO_LONGER_HAS_WRAPPER); |
| 1188 | } |
| 1189 | |
| 1190 | GET_THREAD()->RegisterRCW(holder.m_pRCW); |
| 1191 | |
| 1192 | // if we made it here, suppress the DecrementUseCount call |
| 1193 | holder.m_pRCW = NULL; |
| 1194 | |
| 1195 | HELPER_METHOD_FRAME_END(); |
| 1196 | } |
| 1197 | FCIMPLEND |
| 1198 | |
| 1199 | FCIMPL1(void, StubHelpers::StubUnregisterRCW, Object *unsafe_pThis) |
| 1200 | { |
| 1201 | FCALL_CONTRACT; |
| 1202 | |
| 1203 | OBJECTREF oref = ObjectToOBJECTREF(unsafe_pThis); |
| 1204 | HELPER_METHOD_FRAME_BEGIN_1(oref); |
| 1205 | |
| 1206 | #if defined(_DEBUG) && defined(FEATURE_MDA) |
| 1207 | // Make sure that we only get here because the MDA is turned on. |
| 1208 | MdaRaceOnRCWCleanup* mda = MDA_GET_ASSISTANT(RaceOnRCWCleanup); |
| 1209 | _ASSERTE(mda != NULL); |
| 1210 | #endif // _DEBUG |
| 1211 | |
| 1212 | RCW *pRCW = GET_THREAD()->UnregisterRCW(INDEBUG(oref->GetSyncBlock())); |
| 1213 | |
| 1214 | if (pRCW != NULL) |
| 1215 | { |
| 1216 | // Thread::RegisterRCW incremented the use count, decrement it now |
| 1217 | pRCW->DecrementUseCount(); |
| 1218 | } |
| 1219 | |
| 1220 | HELPER_METHOD_FRAME_END(); |
| 1221 | } |
| 1222 | FCIMPLEND |
| 1223 | |
| 1224 | class COMInterfaceMarshalerCallback : public ICOMInterfaceMarshalerCallback |
| 1225 | { |
| 1226 | public : |
| 1227 | COMInterfaceMarshalerCallback(Thread *pThread, LPVOID pCtxCookie) |
| 1228 | { |
| 1229 | CONTRACTL |
| 1230 | { |
| 1231 | NOTHROW; |
| 1232 | GC_TRIGGERS; |
| 1233 | MODE_ANY; |
| 1234 | } |
| 1235 | CONTRACTL_END; |
| 1236 | |
| 1237 | _ASSERTE(pThread != NULL); |
| 1238 | _ASSERTE(pCtxCookie != NULL); |
| 1239 | |
| 1240 | m_bIsFreeThreaded = false; |
| 1241 | m_pThread = pThread; |
| 1242 | m_pCtxCookie = pCtxCookie; |
| 1243 | |
| 1244 | m_bIsDCOMProxy = false; |
| 1245 | } |
| 1246 | |
| 1247 | virtual void OnRCWCreated(RCW *pRCW) |
| 1248 | { |
| 1249 | LIMITED_METHOD_CONTRACT; |
| 1250 | |
| 1251 | _ASSERTE(pRCW != NULL); |
| 1252 | |
| 1253 | if (pRCW->IsFreeThreaded()) |
| 1254 | m_bIsFreeThreaded = true; |
| 1255 | |
| 1256 | if (pRCW->IsDCOMProxy()) |
| 1257 | m_bIsDCOMProxy = true; |
| 1258 | } |
| 1259 | |
| 1260 | // Return true if ComInterfaceMarshaler should use this RCW |
| 1261 | // Return false if ComInterfaceMarshaler should just skip this RCW and proceed |
| 1262 | // to create a duplicate one instead |
| 1263 | virtual bool ShouldUseThisRCW(RCW *pRCW) |
| 1264 | { |
| 1265 | LIMITED_METHOD_CONTRACT; |
| 1266 | |
| 1267 | _ASSERTE(pRCW->SupportsIInspectable()); |
| 1268 | |
| 1269 | // Is this a free threaded RCW or a context-bound RCW created in the same context |
| 1270 | if (pRCW->IsFreeThreaded() || |
| 1271 | pRCW->GetWrapperCtxCookie() == m_pCtxCookie) |
| 1272 | { |
| 1273 | return true; |
| 1274 | } |
| 1275 | else |
| 1276 | { |
| 1277 | // |
| 1278 | // Now we get back a WinRT factory RCW created in a different context. This means the |
| 1279 | // factory is a singleton, and the returned IActivationFactory could be either one of |
| 1280 | // the following: |
| 1281 | // 1) A raw pointer, and it acts like a free threaded object |
| 1282 | // 2) A proxy that is used across different contexts. It might maintain a list of contexts |
| 1283 | // that it is marshaled to, and will fail to be called if it is not marshaled to this |
| 1284 | // context yet. |
| 1285 | // |
| 1286 | // In this case, it is unsafe to use this RCW in this context and we should proceed |
| 1287 | // to create a duplicated one instead. It might make sense to have a context-sensitive |
| 1288 | // RCW cache but I don't think this case will be common enough to justify it |
| 1289 | // |
| 1290 | return false; |
| 1291 | } |
| 1292 | } |
| 1293 | |
| 1294 | virtual void OnRCWCacheHit(RCW *pRCW) |
| 1295 | { |
| 1296 | LIMITED_METHOD_CONTRACT; |
| 1297 | |
| 1298 | if (pRCW->IsFreeThreaded()) |
| 1299 | m_bIsFreeThreaded = true; |
| 1300 | |
| 1301 | if (pRCW->IsDCOMProxy()) |
| 1302 | m_bIsDCOMProxy = true; |
| 1303 | } |
| 1304 | |
| 1305 | bool IsFreeThreaded() |
| 1306 | { |
| 1307 | LIMITED_METHOD_CONTRACT; |
| 1308 | |
| 1309 | return m_bIsFreeThreaded; |
| 1310 | } |
| 1311 | |
| 1312 | bool IsDCOMProxy() |
| 1313 | { |
| 1314 | LIMITED_METHOD_CONTRACT; |
| 1315 | |
| 1316 | return m_bIsDCOMProxy; |
| 1317 | } |
| 1318 | |
| 1319 | private : |
| 1320 | Thread *m_pThread; // Current thread |
| 1321 | LPVOID m_pCtxCookie; // Current context cookie |
| 1322 | bool m_bIsFreeThreaded; // Whether we got back the RCW from a different context |
| 1323 | bool m_bIsDCOMProxy; // Is this a proxy to an object in a different process |
| 1324 | }; |
| 1325 | |
| 1326 | // |
| 1327 | // Retrieve cached WinRT factory RCW or create a new one, according to the MethodDesc of the .ctor |
| 1328 | // |
| 1329 | FCIMPL1(Object*, StubHelpers::GetWinRTFactoryObject, MethodDesc *pCMD) |
| 1330 | { |
| 1331 | FCALL_CONTRACT; |
| 1332 | |
| 1333 | OBJECTREF refFactory = NULL; |
| 1334 | |
| 1335 | HELPER_METHOD_FRAME_BEGIN_RET_1(refFactory); |
| 1336 | |
| 1337 | MethodTable *pMTOfTypeToCreate = pCMD->GetMethodTable(); |
| 1338 | AppDomain *pDomain = GetAppDomain(); |
| 1339 | |
| 1340 | // |
| 1341 | // Look up cached WinRT factory according to type to create + current context cookie |
| 1342 | // For each type in AppDomain, we cache only the last WinRT factory object |
| 1343 | // We don't cache factory per context in order to avoid explosion of objects if there are |
| 1344 | // multiple STA apartments |
| 1345 | // |
| 1346 | // Note that if cached WinRT factory is FTM, we'll get it back regardless of the supplied cookie |
| 1347 | // |
| 1348 | LPVOID lpCtxCookie = GetCurrentCtxCookie(); |
| 1349 | refFactory = pDomain->LookupWinRTFactoryObject(pMTOfTypeToCreate, lpCtxCookie); |
| 1350 | if (refFactory == NULL) |
| 1351 | { |
| 1352 | // |
| 1353 | // Didn't find a cached factory that matches the context |
| 1354 | // Time to create a new factory and wrap it in a RCW |
| 1355 | // |
| 1356 | |
| 1357 | // |
| 1358 | // Creates a callback to checks for singleton WinRT factory during RCW creation |
| 1359 | // |
| 1360 | // If we get back an existing RCW from a different context, this callback |
| 1361 | // will make the RCW a context-agile (but not free-threaded) RCW. Being context-agile |
| 1362 | // in this case means RCW will not make any context transition. As long as we are only |
| 1363 | // calling this RCW from where we got it back (using IInspectable* as identity), we should |
| 1364 | // be fine (as we are supposed to call that pointer directly anyway) |
| 1365 | // |
| 1366 | // See code:COMInterfaceMarshalerCallback for more details |
| 1367 | // |
| 1368 | COMInterfaceMarshalerCallback callback(GET_THREAD(), lpCtxCookie); |
| 1369 | |
| 1370 | // |
| 1371 | // Get the activation factory instance for this WinRT type and create a RCW for it |
| 1372 | // |
| 1373 | GetNativeWinRTFactoryObject( |
| 1374 | pMTOfTypeToCreate, |
| 1375 | GET_THREAD(), |
| 1376 | ComPlusCallInfo::FromMethodDesc(pCMD)->m_pInterfaceMT, // Factory interface |
| 1377 | FALSE, // Don't need a unique RCW |
| 1378 | // it is only needed in WindowsRuntimeMarshal.GetActivationFactory API |
| 1379 | &callback, |
| 1380 | &refFactory); |
| 1381 | |
| 1382 | // |
| 1383 | // If this is free-threaded factory RCW, set lpCtxCookie = NULL, which means |
| 1384 | // this RCW can be used anywhere |
| 1385 | // Otherwise, we can only use this RCW from current thread |
| 1386 | // |
| 1387 | if (callback.IsFreeThreaded()) |
| 1388 | lpCtxCookie = NULL; |
| 1389 | |
| 1390 | // Cache the result in the AD-wide cache, unless this is a proxy to a DCOM object on Apollo. In the |
| 1391 | // Apollo app model, out of process WinRT servers can have lifetimes independent of the application, |
| 1392 | // and the cache may wind up with stale pointers if we save proxies to OOP factories. In addition, |
| 1393 | // their app model is such that OOP WinRT objects cannot rely on having static state as they can be |
| 1394 | // forcibly terminated at any point. Therefore, not caching an OOP WinRT factory object in Apollo |
| 1395 | // does not lead to correctness issues. |
| 1396 | // |
| 1397 | // This is not the same on the desktop, where OOP objects may contain static state, and therefore |
| 1398 | // we need to keep them alive. |
| 1399 | #ifdef FEATURE_WINDOWSPHONE |
| 1400 | if (!callback.IsDCOMProxy()) |
| 1401 | #endif // FEATURE_WINDOWSPHONE |
| 1402 | { |
| 1403 | pDomain->CacheWinRTFactoryObject(pMTOfTypeToCreate, &refFactory, lpCtxCookie); |
| 1404 | } |
| 1405 | } |
| 1406 | |
| 1407 | HELPER_METHOD_FRAME_END(); |
| 1408 | |
| 1409 | return OBJECTREFToObject(refFactory); |
| 1410 | } |
| 1411 | FCIMPLEND |
| 1412 | |
| 1413 | |
| 1414 | #endif |
| 1415 | |
| 1416 | #ifdef MDA_SUPPORTED |
| 1417 | NOINLINE static void CheckCollectedDelegateMDAHelper(UMEntryThunk *pEntryThunk) |
| 1418 | { |
| 1419 | FC_INNER_PROLOG(StubHelpers::CheckCollectedDelegateMDA); |
| 1420 | HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); |
| 1421 | |
| 1422 | CallbackOnCollectedDelegateHelper(pEntryThunk); |
| 1423 | |
| 1424 | HELPER_METHOD_FRAME_END(); |
| 1425 | FC_INNER_EPILOG(); |
| 1426 | } |
| 1427 | |
| 1428 | FCIMPL1(void, StubHelpers::CheckCollectedDelegateMDA, LPVOID pEntryThunk) |
| 1429 | { |
| 1430 | CONTRACTL |
| 1431 | { |
| 1432 | FCALL_CHECK; |
| 1433 | PRECONDITION(pEntryThunk != NULL); |
| 1434 | } |
| 1435 | CONTRACTL_END; |
| 1436 | |
| 1437 | if (MDA_GET_ASSISTANT(CallbackOnCollectedDelegate) == NULL) |
| 1438 | return; |
| 1439 | |
| 1440 | // keep this FCall as fast as possible for the "MDA is off" case |
| 1441 | FC_INNER_RETURN_VOID(CheckCollectedDelegateMDAHelper((UMEntryThunk *)pEntryThunk)); |
| 1442 | } |
| 1443 | FCIMPLEND |
| 1444 | #endif // MDA_SUPPORTED |
| 1445 | |
| 1446 | #ifdef PROFILING_SUPPORTED |
| 1447 | FCIMPL3(SIZE_T, StubHelpers::ProfilerBeginTransitionCallback, SIZE_T pSecretParam, Thread* pThread, Object* unsafe_pThis) |
| 1448 | { |
| 1449 | FCALL_CONTRACT; |
| 1450 | |
| 1451 | // We can get here with an ngen image generated with "/prof", |
| 1452 | // even if the profiler doesn't want to track transitions. |
| 1453 | if (!CORProfilerTrackTransitions()) |
| 1454 | { |
| 1455 | return NULL; |
| 1456 | } |
| 1457 | |
| 1458 | MethodDesc* pRealMD = NULL; |
| 1459 | |
| 1460 | BEGIN_PRESERVE_LAST_ERROR; |
| 1461 | |
| 1462 | // We must transition to preemptive GC mode before calling out to the profiler, |
| 1463 | // and the transition requires us to set up a HMF. |
| 1464 | DELEGATEREF dref = (DELEGATEREF)ObjectToOBJECTREF(unsafe_pThis); |
| 1465 | HELPER_METHOD_FRAME_BEGIN_RET_1(dref); |
| 1466 | |
| 1467 | bool fReverseInterop = false; |
| 1468 | |
| 1469 | if (NULL == pThread) |
| 1470 | { |
| 1471 | // This is our signal for the reverse interop cases. |
| 1472 | fReverseInterop = true; |
| 1473 | pThread = GET_THREAD(); |
| 1474 | // the secret param in this casee is the UMEntryThunk |
| 1475 | pRealMD = ((UMEntryThunk*)pSecretParam)->GetMethod(); |
| 1476 | } |
| 1477 | else |
| 1478 | if (pSecretParam == 0) |
| 1479 | { |
| 1480 | // Secret param is null. This is the calli pinvoke case or the unmanaged delegate case. |
| 1481 | // We have an unmanaged target address but no MD. For the unmanaged delegate case, we can |
| 1482 | // still retrieve the MD by looking at the "this" object. |
| 1483 | |
| 1484 | if (dref == NULL) |
| 1485 | { |
| 1486 | // calli pinvoke case |
| 1487 | pRealMD = NULL; |
| 1488 | } |
| 1489 | else |
| 1490 | { |
| 1491 | // unmanaged delegate case |
| 1492 | MethodTable* pMT = dref->GetMethodTable(); |
| 1493 | _ASSERTE(pMT->IsDelegate()); |
| 1494 | |
| 1495 | EEClass * pClass = pMT->GetClass(); |
| 1496 | pRealMD = ((DelegateEEClass*)pClass)->GetInvokeMethod(); |
| 1497 | _ASSERTE(pRealMD); |
| 1498 | } |
| 1499 | } |
| 1500 | else |
| 1501 | { |
| 1502 | // This is either the COM interop or the pinvoke case. |
| 1503 | pRealMD = (MethodDesc*)pSecretParam; |
| 1504 | } |
| 1505 | |
| 1506 | { |
| 1507 | GCX_PREEMP_THREAD_EXISTS(pThread); |
| 1508 | |
| 1509 | if (fReverseInterop) |
| 1510 | { |
| 1511 | ProfilerUnmanagedToManagedTransitionMD(pRealMD, COR_PRF_TRANSITION_CALL); |
| 1512 | } |
| 1513 | else |
| 1514 | { |
| 1515 | ProfilerManagedToUnmanagedTransitionMD(pRealMD, COR_PRF_TRANSITION_CALL); |
| 1516 | } |
| 1517 | } |
| 1518 | |
| 1519 | HELPER_METHOD_FRAME_END(); |
| 1520 | |
| 1521 | END_PRESERVE_LAST_ERROR; |
| 1522 | |
| 1523 | return (SIZE_T)pRealMD; |
| 1524 | } |
| 1525 | FCIMPLEND |
| 1526 | |
| 1527 | FCIMPL2(void, StubHelpers::ProfilerEndTransitionCallback, MethodDesc* pRealMD, Thread* pThread) |
| 1528 | { |
| 1529 | FCALL_CONTRACT; |
| 1530 | |
| 1531 | // We can get here with an ngen image generated with "/prof", |
| 1532 | // even if the profiler doesn't want to track transitions. |
| 1533 | if (!CORProfilerTrackTransitions()) |
| 1534 | { |
| 1535 | return; |
| 1536 | } |
| 1537 | |
| 1538 | BEGIN_PRESERVE_LAST_ERROR; |
| 1539 | |
| 1540 | // We must transition to preemptive GC mode before calling out to the profiler, |
| 1541 | // and the transition requires us to set up a HMF. |
| 1542 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 1543 | { |
| 1544 | bool fReverseInterop = false; |
| 1545 | |
| 1546 | if (NULL == pThread) |
| 1547 | { |
| 1548 | // if pThread is null, we are doing reverse interop |
| 1549 | pThread = GET_THREAD(); |
| 1550 | fReverseInterop = true; |
| 1551 | } |
| 1552 | |
| 1553 | GCX_PREEMP_THREAD_EXISTS(pThread); |
| 1554 | |
| 1555 | if (fReverseInterop) |
| 1556 | { |
| 1557 | ProfilerManagedToUnmanagedTransitionMD(pRealMD, COR_PRF_TRANSITION_RETURN); |
| 1558 | } |
| 1559 | else |
| 1560 | { |
| 1561 | ProfilerUnmanagedToManagedTransitionMD(pRealMD, COR_PRF_TRANSITION_RETURN); |
| 1562 | } |
| 1563 | } |
| 1564 | HELPER_METHOD_FRAME_END(); |
| 1565 | |
| 1566 | END_PRESERVE_LAST_ERROR; |
| 1567 | } |
| 1568 | FCIMPLEND |
| 1569 | #endif // PROFILING_SUPPORTED |
| 1570 | |
| 1571 | FCIMPL1(Object*, StubHelpers::GetHRExceptionObject, HRESULT hr) |
| 1572 | { |
| 1573 | FCALL_CONTRACT; |
| 1574 | |
| 1575 | OBJECTREF oThrowable = NULL; |
| 1576 | |
| 1577 | HELPER_METHOD_FRAME_BEGIN_RET_1(oThrowable); |
| 1578 | { |
| 1579 | // GetExceptionForHR uses equivalant logic as COMPlusThrowHR |
| 1580 | GetExceptionForHR(hr, &oThrowable); |
| 1581 | } |
| 1582 | HELPER_METHOD_FRAME_END(); |
| 1583 | |
| 1584 | return OBJECTREFToObject(oThrowable); |
| 1585 | } |
| 1586 | FCIMPLEND |
| 1587 | |
| 1588 | #ifdef FEATURE_COMINTEROP |
| 1589 | FCIMPL4(Object*, StubHelpers::GetCOMHRExceptionObject, HRESULT hr, MethodDesc *pMD, Object *unsafe_pThis, CLR_BOOL fForWinRT) |
| 1590 | { |
| 1591 | FCALL_CONTRACT; |
| 1592 | |
| 1593 | OBJECTREF oThrowable = NULL; |
| 1594 | |
| 1595 | // get 'this' |
| 1596 | OBJECTREF oref = ObjectToOBJECTREF(unsafe_pThis); |
| 1597 | |
| 1598 | HELPER_METHOD_FRAME_BEGIN_RET_2(oref, oThrowable); |
| 1599 | { |
| 1600 | IErrorInfo *pErrInfo = NULL; |
| 1601 | |
| 1602 | IRestrictedErrorInfo *pResErrorInfo = NULL; |
| 1603 | BOOL bHasNonCLRLanguageErrorObject = FALSE; |
| 1604 | |
| 1605 | if (fForWinRT) |
| 1606 | { |
| 1607 | SafeGetRestrictedErrorInfo(&pResErrorInfo); |
| 1608 | if (pResErrorInfo != NULL) |
| 1609 | { |
| 1610 | // If we have a restricted error Info lets try and find the corresponding errorInfo, |
| 1611 | // bHasNonCLRLanguageErrorObject can be TRUE|FALSE depending on whether we have an associtated LanguageExceptionObject |
| 1612 | // and whether it is CLR exceptionObject => bHasNonCLRLanguageErrorObject = FALSE; |
| 1613 | // or whether it is a non-CLRExceptionObject => bHasNonCLRLanguageErrorObject = TRUE; |
| 1614 | pErrInfo = GetCorrepondingErrorInfo_WinRT(hr, pResErrorInfo, &bHasNonCLRLanguageErrorObject); |
| 1615 | } |
| 1616 | } |
| 1617 | if (pErrInfo == NULL && pMD != NULL) |
| 1618 | { |
| 1619 | // Retrieve the interface method table. |
| 1620 | MethodTable *pItfMT = ComPlusCallInfo::FromMethodDesc(pMD)->m_pInterfaceMT; |
| 1621 | |
| 1622 | // Get IUnknown pointer for this interface on this object |
| 1623 | IUnknown* pUnk = ComObject::GetComIPFromRCW(&oref, pItfMT); |
| 1624 | if (pUnk != NULL) |
| 1625 | { |
| 1626 | // Check to see if the component supports error information for this interface. |
| 1627 | IID ItfIID; |
| 1628 | pItfMT->GetGuid(&ItfIID, TRUE); |
| 1629 | pErrInfo = GetSupportedErrorInfo(pUnk, ItfIID, !fForWinRT); |
| 1630 | |
| 1631 | DWORD cbRef = SafeRelease(pUnk); |
| 1632 | LogInteropRelease(pUnk, cbRef, "IUnk to QI for ISupportsErrorInfo" ); |
| 1633 | } |
| 1634 | } |
| 1635 | |
| 1636 | GetExceptionForHR(hr, pErrInfo, !fForWinRT, &oThrowable, pResErrorInfo, bHasNonCLRLanguageErrorObject); |
| 1637 | } |
| 1638 | HELPER_METHOD_FRAME_END(); |
| 1639 | |
| 1640 | return OBJECTREFToObject(oThrowable); |
| 1641 | } |
| 1642 | FCIMPLEND |
| 1643 | #endif // FEATURE_COMINTEROP |
| 1644 | |
| 1645 | FCIMPL3(void, StubHelpers::FmtClassUpdateNativeInternal, Object* pObjUNSAFE, BYTE* pbNative, OBJECTREF *ppCleanupWorkListOnStack) |
| 1646 | { |
| 1647 | FCALL_CONTRACT; |
| 1648 | |
| 1649 | OBJECTREF pObj = ObjectToOBJECTREF(pObjUNSAFE); |
| 1650 | HELPER_METHOD_FRAME_BEGIN_1(pObj); |
| 1651 | |
| 1652 | FmtClassUpdateNative(&pObj, pbNative, ppCleanupWorkListOnStack); |
| 1653 | |
| 1654 | HELPER_METHOD_FRAME_END(); |
| 1655 | } |
| 1656 | FCIMPLEND |
| 1657 | |
| 1658 | FCIMPL2(void, StubHelpers::FmtClassUpdateCLRInternal, Object* pObjUNSAFE, BYTE* pbNative) |
| 1659 | { |
| 1660 | FCALL_CONTRACT; |
| 1661 | |
| 1662 | OBJECTREF pObj = ObjectToOBJECTREF(pObjUNSAFE); |
| 1663 | HELPER_METHOD_FRAME_BEGIN_1(pObj); |
| 1664 | |
| 1665 | FmtClassUpdateCLR(&pObj, pbNative); |
| 1666 | |
| 1667 | HELPER_METHOD_FRAME_END(); |
| 1668 | } |
| 1669 | FCIMPLEND |
| 1670 | |
| 1671 | FCIMPL2(void, StubHelpers::LayoutDestroyNativeInternal, BYTE* pbNative, MethodTable* pMT) |
| 1672 | { |
| 1673 | FCALL_CONTRACT; |
| 1674 | |
| 1675 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 1676 | LayoutDestroyNative(pbNative, pMT); |
| 1677 | HELPER_METHOD_FRAME_END(); |
| 1678 | } |
| 1679 | FCIMPLEND |
| 1680 | |
| 1681 | FCIMPL1(Object*, StubHelpers::AllocateInternal, EnregisteredTypeHandle pRegisteredTypeHnd) |
| 1682 | { |
| 1683 | FCALL_CONTRACT; |
| 1684 | |
| 1685 | TypeHandle typeHnd = TypeHandle::FromPtr(pRegisteredTypeHnd); |
| 1686 | OBJECTREF objRet = NULL; |
| 1687 | HELPER_METHOD_FRAME_BEGIN_RET_1(objRet); |
| 1688 | |
| 1689 | MethodTable* pMT = typeHnd.GetMethodTable(); |
| 1690 | objRet = pMT->Allocate(); |
| 1691 | |
| 1692 | HELPER_METHOD_FRAME_END(); |
| 1693 | |
| 1694 | return OBJECTREFToObject(objRet); |
| 1695 | } |
| 1696 | FCIMPLEND |
| 1697 | |
| 1698 | FCIMPL3(void, StubHelpers::MarshalToUnmanagedVaListInternal, va_list va, DWORD cbVaListSize, const VARARGS* pArgIterator) |
| 1699 | { |
| 1700 | FCALL_CONTRACT; |
| 1701 | |
| 1702 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 1703 | VARARGS::MarshalToUnmanagedVaList(va, cbVaListSize, pArgIterator); |
| 1704 | HELPER_METHOD_FRAME_END(); |
| 1705 | } |
| 1706 | FCIMPLEND |
| 1707 | |
| 1708 | FCIMPL2(void, StubHelpers::MarshalToManagedVaListInternal, va_list va, VARARGS* pArgIterator) |
| 1709 | { |
| 1710 | FCALL_CONTRACT; |
| 1711 | |
| 1712 | VARARGS::MarshalToManagedVaList(va, pArgIterator); |
| 1713 | } |
| 1714 | FCIMPLEND |
| 1715 | |
| 1716 | FCIMPL3(void, StubHelpers::ValidateObject, Object *pObjUNSAFE, MethodDesc *pMD, Object *pThisUNSAFE) |
| 1717 | { |
| 1718 | FCALL_CONTRACT; |
| 1719 | |
| 1720 | #ifdef VERIFY_HEAP |
| 1721 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 1722 | |
| 1723 | StackSString errorString; |
| 1724 | EX_TRY |
| 1725 | { |
| 1726 | AVInRuntimeImplOkayHolder AVOkay; |
| 1727 | // don't validate the next object if a BGC is in progress. we can race with background |
| 1728 | // sweep which could make the next object a Free object underneath us if it's dead. |
| 1729 | ValidateObjectInternal(pObjUNSAFE, !(GCHeapUtilities::GetGCHeap()->IsConcurrentGCInProgress())); |
| 1730 | } |
| 1731 | EX_CATCH |
| 1732 | { |
| 1733 | FormatValidationMessage(ResolveInteropMethod(pThisUNSAFE, pMD), errorString); |
| 1734 | EEPOLICY_HANDLE_FATAL_ERROR_WITH_MESSAGE(COR_E_EXECUTIONENGINE, errorString.GetUnicode()); |
| 1735 | } |
| 1736 | EX_END_CATCH_UNREACHABLE; |
| 1737 | |
| 1738 | HELPER_METHOD_FRAME_END(); |
| 1739 | #else // VERIFY_HEAP |
| 1740 | FCUnique(0xa3); |
| 1741 | UNREACHABLE_MSG("No validation support without VERIFY_HEAP" ); |
| 1742 | #endif // VERIFY_HEAP |
| 1743 | } |
| 1744 | FCIMPLEND |
| 1745 | |
| 1746 | FCIMPL3(void, StubHelpers::ValidateByref, void *pByref, MethodDesc *pMD, Object *pThisUNSAFE) |
| 1747 | { |
| 1748 | FCALL_CONTRACT; |
| 1749 | |
| 1750 | #ifdef VERIFY_HEAP |
| 1751 | // We cannot validate byrefs at this point as code:GCHeap.GetContainingObject could potentially race |
| 1752 | // with allocations on other threads. We'll just remember this byref along with the interop MD and |
| 1753 | // perform the validation on next GC (see code:StubHelpers.ProcessByrefValidationList). |
| 1754 | |
| 1755 | // Skip byref if is not pointing inside managed heap |
| 1756 | if (!GCHeapUtilities::GetGCHeap()->IsHeapPointer(pByref)) |
| 1757 | { |
| 1758 | return; |
| 1759 | } |
| 1760 | ByrefValidationEntry entry; |
| 1761 | entry.pByref = pByref; |
| 1762 | entry.pMD = ResolveInteropMethod(pThisUNSAFE, pMD); |
| 1763 | |
| 1764 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 1765 | |
| 1766 | SIZE_T NumOfEntries = 0; |
| 1767 | { |
| 1768 | CrstHolder ch(&s_ByrefValidationLock); |
| 1769 | |
| 1770 | if (s_ByrefValidationIndex >= s_ByrefValidationEntries.Size()) |
| 1771 | { |
| 1772 | // The validation list grows as necessary, for simplicity we never shrink it. |
| 1773 | SIZE_T newSize; |
| 1774 | if (!ClrSafeInt<SIZE_T>::multiply(s_ByrefValidationIndex, 2, newSize) || |
| 1775 | !ClrSafeInt<SIZE_T>::addition(newSize, 1, newSize)) |
| 1776 | { |
| 1777 | ThrowHR(COR_E_OVERFLOW); |
| 1778 | } |
| 1779 | |
| 1780 | s_ByrefValidationEntries.ReSizeThrows(newSize); |
| 1781 | _ASSERTE(s_ByrefValidationIndex < s_ByrefValidationEntries.Size()); |
| 1782 | } |
| 1783 | |
| 1784 | s_ByrefValidationEntries[s_ByrefValidationIndex] = entry; |
| 1785 | NumOfEntries = ++s_ByrefValidationIndex; |
| 1786 | } |
| 1787 | |
| 1788 | if (NumOfEntries > BYREF_VALIDATION_LIST_MAX_SIZE) |
| 1789 | { |
| 1790 | // if the list is too big, trigger GC now |
| 1791 | GCHeapUtilities::GetGCHeap()->GarbageCollect(0); |
| 1792 | } |
| 1793 | |
| 1794 | HELPER_METHOD_FRAME_END(); |
| 1795 | #else // VERIFY_HEAP |
| 1796 | FCUnique(0xa4); |
| 1797 | UNREACHABLE_MSG("No validation support without VERIFY_HEAP" ); |
| 1798 | #endif // VERIFY_HEAP |
| 1799 | } |
| 1800 | FCIMPLEND |
| 1801 | |
| 1802 | FCIMPL0(void*, StubHelpers::GetStubContext) |
| 1803 | { |
| 1804 | FCALL_CONTRACT; |
| 1805 | |
| 1806 | FCUnique(0xa0); |
| 1807 | UNREACHABLE_MSG_RET("This is a JIT intrinsic!" ); |
| 1808 | } |
| 1809 | FCIMPLEND |
| 1810 | |
| 1811 | FCIMPL2(void, StubHelpers::LogPinnedArgument, MethodDesc *target, Object *pinnedArg) |
| 1812 | { |
| 1813 | FCALL_CONTRACT; |
| 1814 | |
| 1815 | SIZE_T managedSize = 0; |
| 1816 | |
| 1817 | if (pinnedArg != NULL) |
| 1818 | { |
| 1819 | // Can pass null objects to interop, only check the size if the object is valid. |
| 1820 | managedSize = pinnedArg->GetSize(); |
| 1821 | } |
| 1822 | |
| 1823 | if (target != NULL) |
| 1824 | { |
| 1825 | STRESS_LOG3(LF_STUBS, LL_INFO100, "Managed object %#X with size '%#X' pinned for interop to Method [%pM]\n" , pinnedArg, managedSize, target); |
| 1826 | } |
| 1827 | else |
| 1828 | { |
| 1829 | STRESS_LOG2(LF_STUBS, LL_INFO100, "Managed object %#X pinned for interop with size '%#X'" , pinnedArg, managedSize); |
| 1830 | } |
| 1831 | } |
| 1832 | FCIMPLEND |
| 1833 | |
| 1834 | #ifdef _TARGET_64BIT_ |
| 1835 | FCIMPL0(void*, StubHelpers::GetStubContextAddr) |
| 1836 | { |
| 1837 | FCALL_CONTRACT; |
| 1838 | |
| 1839 | FCUnique(0xa1); |
| 1840 | UNREACHABLE_MSG("This is a JIT intrinsic!" ); |
| 1841 | } |
| 1842 | FCIMPLEND |
| 1843 | #endif // _TARGET_64BIT_ |
| 1844 | |
| 1845 | #ifdef MDA_SUPPORTED |
| 1846 | FCIMPL0(void, StubHelpers::TriggerGCForMDA) |
| 1847 | { |
| 1848 | FCALL_CONTRACT; |
| 1849 | |
| 1850 | HELPER_METHOD_FRAME_BEGIN_0(); |
| 1851 | TriggerGCForMDAInternal(); |
| 1852 | HELPER_METHOD_FRAME_END(); |
| 1853 | } |
| 1854 | FCIMPLEND |
| 1855 | #endif // MDA_SUPPORTED |
| 1856 | |
| 1857 | FCIMPL1(DWORD, StubHelpers::CalcVaListSize, VARARGS *varargs) |
| 1858 | { |
| 1859 | FCALL_CONTRACT; |
| 1860 | |
| 1861 | return VARARGS::CalcVaListSize(varargs); |
| 1862 | } |
| 1863 | FCIMPLEND |
| 1864 | |
| 1865 | #ifdef FEATURE_ARRAYSTUB_AS_IL |
| 1866 | NOINLINE static void ArrayTypeCheckSlow(Object* element, PtrArray* arr) |
| 1867 | { |
| 1868 | FC_INNER_PROLOG(StubHelpers::ArrayTypeCheck); |
| 1869 | HELPER_METHOD_FRAME_BEGIN_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2); |
| 1870 | |
| 1871 | if (!ObjIsInstanceOf(element, arr->GetArrayElementTypeHandle())) |
| 1872 | COMPlusThrow(kArrayTypeMismatchException); |
| 1873 | |
| 1874 | HELPER_METHOD_FRAME_END(); |
| 1875 | |
| 1876 | FC_INNER_EPILOG(); |
| 1877 | } |
| 1878 | |
| 1879 | FCIMPL2(void, StubHelpers::ArrayTypeCheck, Object* element, PtrArray* arr) |
| 1880 | { |
| 1881 | FCALL_CONTRACT; |
| 1882 | |
| 1883 | if (ObjIsInstanceOfNoGC(element, arr->GetArrayElementTypeHandle()) == TypeHandle::CanCast) |
| 1884 | return; |
| 1885 | |
| 1886 | FC_INNER_RETURN_VOID(ArrayTypeCheckSlow(element, arr)); |
| 1887 | } |
| 1888 | FCIMPLEND |
| 1889 | #endif // FEATURE_ARRAYSTUB_AS_IL |
| 1890 | |
| 1891 | #ifdef FEATURE_MULTICASTSTUB_AS_IL |
| 1892 | FCIMPL2(void, StubHelpers::MulticastDebuggerTraceHelper, Object* element, INT32 count) |
| 1893 | { |
| 1894 | FCALL_CONTRACT; |
| 1895 | FCUnique(0xa5); |
| 1896 | } |
| 1897 | FCIMPLEND |
| 1898 | #endif // FEATURE_MULTICASTSTUB_AS_IL |
| 1899 | |