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
36CQuickArray<StubHelpers::ByrefValidationEntry> StubHelpers::s_ByrefValidationEntries;
37SIZE_T StubHelpers::s_ByrefValidationIndex = 0;
38CrstStatic StubHelpers::s_ByrefValidationLock;
39
40// static
41void StubHelpers::Init()
42{
43 WRAPPER_NO_CONTRACT;
44 s_ByrefValidationLock.Init(CrstPinnedByrefValidation);
45}
46
47// static
48void 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
88MethodDesc *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
104void 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
137void 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
184FCIMPL1_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}
194FCIMPLEND
195
196FCIMPL1_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}
206FCIMPLEND
207
208FCIMPL4(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}
216FCIMPLEND
217
218FCIMPL3(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}
226FCIMPLEND
227
228FCIMPL2(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}
236FCIMPLEND
237
238#ifdef FEATURE_COMINTEROP
239
240FORCEINLINE 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
254NoNeedToClear:
255 }
256#endif // _TARGET_X86_
257}
258
259FORCEINLINE 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
279FORCEINLINE 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
292FORCEINLINE 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.
325FORCEINLINE 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
366FORCEINLINE 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
375NOINLINE 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.
421FCIMPL4(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}
454FCIMPLEND
455
456// This helper can handle only non-generic WinRT calls, does not support hosting/interception, and does not clear FP state.
457FCIMPL3(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}
482FCIMPLEND
483
484// This helper can handle only generic WinRT calls, does not support hosting, and does not clear FP state.
485FCIMPL3(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}
510FCIMPLEND
511
512// This helper can handle only delegate WinRT calls, does not support hosting, and does not clear FP state.
513FCIMPL3(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}
538FCIMPLEND
539
540#include <optdefault.h>
541
542
543NOINLINE 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
560FCIMPL2(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}
584FCIMPLEND
585
586NOINLINE 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.
615FCIMPL3(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}
647FCIMPLEND
648
649FCIMPL2(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}
666FCIMPLEND
667
668FCIMPL1(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}
681FCIMPLEND
682
683FCIMPL1(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}
691FCIMPLEND
692
693#include <optsmallperfcritical.h>
694FCIMPL4(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}
716FCIMPLEND
717
718FCIMPL4(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}
736FCIMPLEND
737
738void 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
754FCIMPL1(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}
786FCIMPLEND
787
788FCIMPL2(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}
807FCIMPLEND
808
809// A helper to convert an IP to object using special flags.
810FCIMPL1(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}
842FCIMPLEND
843
844FCIMPL2(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}
881FCIMPLEND
882
883FCIMPL2(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}
906FCIMPLEND
907
908FCIMPL1(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}
931FCIMPLEND
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).
935FCIMPL2(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}
962FCIMPLEND
963
964// Called from CLR-to-COM factory method stubs to get the outer IInspectable to pass
965// to the underlying factory object.
966FCIMPL2(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}
988FCIMPLEND
989
990#ifdef MDA_SUPPORTED
991FCIMPL2(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}
1005FCIMPLEND
1006#endif // MDA_SUPPORTED
1007
1008#endif // FEATURE_COMINTEROP
1009
1010FCIMPL0(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}
1019FCIMPLEND
1020
1021FCIMPL0(void, StubHelpers::ClearLastError)
1022{
1023 FCALL_CONTRACT;
1024
1025 ::SetLastError(0);
1026}
1027FCIMPLEND
1028
1029FCIMPL1(FC_BOOL_RET, StubHelpers::IsQCall, NDirectMethodDesc* pNMD)
1030{
1031 FCALL_CONTRACT;
1032 FC_RETURN_BOOL(pNMD->IsQCall());
1033}
1034FCIMPLEND
1035
1036NOINLINE 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>
1049FCIMPL1(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}
1061FCIMPLEND
1062#include <optdefault.h>
1063
1064FCIMPL1(void*, StubHelpers::GetNDirectTarget, NDirectMethodDesc* pNMD)
1065{
1066 FCALL_CONTRACT;
1067
1068 FCUnique(0xa2);
1069 return pNMD->GetNDirectTarget();
1070}
1071FCIMPLEND
1072
1073FCIMPL2(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}
1134FCIMPLEND
1135
1136
1137
1138FCIMPL2(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}
1146FCIMPLEND
1147
1148#ifdef FEATURE_COMINTEROP
1149FCIMPL1(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}
1197FCIMPLEND
1198
1199FCIMPL1(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}
1222FCIMPLEND
1223
1224class COMInterfaceMarshalerCallback : public ICOMInterfaceMarshalerCallback
1225{
1226public :
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
1319private :
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//
1329FCIMPL1(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}
1411FCIMPLEND
1412
1413
1414#endif
1415
1416#ifdef MDA_SUPPORTED
1417NOINLINE 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
1428FCIMPL1(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}
1443FCIMPLEND
1444#endif // MDA_SUPPORTED
1445
1446#ifdef PROFILING_SUPPORTED
1447FCIMPL3(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}
1525FCIMPLEND
1526
1527FCIMPL2(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}
1568FCIMPLEND
1569#endif // PROFILING_SUPPORTED
1570
1571FCIMPL1(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}
1586FCIMPLEND
1587
1588#ifdef FEATURE_COMINTEROP
1589FCIMPL4(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}
1642FCIMPLEND
1643#endif // FEATURE_COMINTEROP
1644
1645FCIMPL3(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}
1656FCIMPLEND
1657
1658FCIMPL2(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}
1669FCIMPLEND
1670
1671FCIMPL2(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}
1679FCIMPLEND
1680
1681FCIMPL1(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}
1696FCIMPLEND
1697
1698FCIMPL3(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}
1706FCIMPLEND
1707
1708FCIMPL2(void, StubHelpers::MarshalToManagedVaListInternal, va_list va, VARARGS* pArgIterator)
1709{
1710 FCALL_CONTRACT;
1711
1712 VARARGS::MarshalToManagedVaList(va, pArgIterator);
1713}
1714FCIMPLEND
1715
1716FCIMPL3(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}
1744FCIMPLEND
1745
1746FCIMPL3(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}
1800FCIMPLEND
1801
1802FCIMPL0(void*, StubHelpers::GetStubContext)
1803{
1804 FCALL_CONTRACT;
1805
1806 FCUnique(0xa0);
1807 UNREACHABLE_MSG_RET("This is a JIT intrinsic!");
1808}
1809FCIMPLEND
1810
1811FCIMPL2(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}
1832FCIMPLEND
1833
1834#ifdef _TARGET_64BIT_
1835FCIMPL0(void*, StubHelpers::GetStubContextAddr)
1836{
1837 FCALL_CONTRACT;
1838
1839 FCUnique(0xa1);
1840 UNREACHABLE_MSG("This is a JIT intrinsic!");
1841}
1842FCIMPLEND
1843#endif // _TARGET_64BIT_
1844
1845#ifdef MDA_SUPPORTED
1846FCIMPL0(void, StubHelpers::TriggerGCForMDA)
1847{
1848 FCALL_CONTRACT;
1849
1850 HELPER_METHOD_FRAME_BEGIN_0();
1851 TriggerGCForMDAInternal();
1852 HELPER_METHOD_FRAME_END();
1853}
1854FCIMPLEND
1855#endif // MDA_SUPPORTED
1856
1857FCIMPL1(DWORD, StubHelpers::CalcVaListSize, VARARGS *varargs)
1858{
1859 FCALL_CONTRACT;
1860
1861 return VARARGS::CalcVaListSize(varargs);
1862}
1863FCIMPLEND
1864
1865#ifdef FEATURE_ARRAYSTUB_AS_IL
1866NOINLINE 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
1879FCIMPL2(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}
1888FCIMPLEND
1889#endif // FEATURE_ARRAYSTUB_AS_IL
1890
1891#ifdef FEATURE_MULTICASTSTUB_AS_IL
1892FCIMPL2(void, StubHelpers::MulticastDebuggerTraceHelper, Object* element, INT32 count)
1893{
1894 FCALL_CONTRACT;
1895 FCUnique(0xa5);
1896}
1897FCIMPLEND
1898#endif // FEATURE_MULTICASTSTUB_AS_IL
1899