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 | |