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: COMDelegate.cpp
6//
7
8// This module contains the implementation of the native methods for the
9// Delegate class.
10//
11
12
13#include "common.h"
14#include "comdelegate.h"
15#include "invokeutil.h"
16#include "excep.h"
17#include "class.h"
18#include "field.h"
19#include "dllimportcallback.h"
20#include "dllimport.h"
21#include "eeconfig.h"
22#include "mdaassistants.h"
23#include "cgensys.h"
24#include "asmconstants.h"
25#include "virtualcallstub.h"
26#include "callingconvention.h"
27#include "customattribute.h"
28#include "typestring.h"
29#include "../md/compiler/custattr.h"
30#ifdef FEATURE_COMINTEROP
31#include "comcallablewrapper.h"
32#endif // FEATURE_COMINTEROP
33
34#define DELEGATE_MARKER_UNMANAGEDFPTR -1
35
36
37#ifndef DACCESS_COMPILE
38
39#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
40
41// ShuffleOfs not needed
42
43#elif defined(_TARGET_X86_)
44
45// Return an encoded shuffle entry describing a general register or stack offset that needs to be shuffled.
46static UINT16 ShuffleOfs(INT ofs, UINT stackSizeDelta = 0)
47{
48 STANDARD_VM_CONTRACT;
49
50 if (TransitionBlock::IsStackArgumentOffset(ofs))
51 {
52 ofs = (ofs - TransitionBlock::GetOffsetOfReturnAddress()) + stackSizeDelta;
53
54 if (ofs >= ShuffleEntry::REGMASK)
55 {
56 // method takes too many stack args
57 COMPlusThrow(kNotSupportedException);
58 }
59 }
60 else
61 {
62 ofs -= TransitionBlock::GetOffsetOfArgumentRegisters();
63 ofs |= ShuffleEntry::REGMASK;
64 }
65
66 return static_cast<UINT16>(ofs);
67}
68
69#else // Portable default implementation
70
71// Iterator for extracting shuffle entries for argument desribed by an ArgLocDesc.
72// Used when calculating shuffle array entries in GenerateShuffleArray below.
73class ShuffleIterator
74{
75 // Argument location description
76 ArgLocDesc* m_argLocDesc;
77
78#if defined(UNIX_AMD64_ABI)
79 // Current eightByte used for struct arguments in registers
80 int m_currentEightByte;
81#endif
82 // Current general purpose register index (relative to the ArgLocDesc::m_idxGenReg)
83 int m_currentGenRegIndex;
84 // Current floating point register index (relative to the ArgLocDesc::m_idxFloatReg)
85 int m_currentFloatRegIndex;
86 // Current stack slot index (relative to the ArgLocDesc::m_idxStack)
87 int m_currentStackSlotIndex;
88
89#if defined(UNIX_AMD64_ABI)
90 // Get next shuffle offset for struct passed in registers. There has to be at least one offset left.
91 UINT16 GetNextOfsInStruct()
92 {
93 EEClass* eeClass = m_argLocDesc->m_eeClass;
94 _ASSERTE(eeClass != NULL);
95
96 if (m_currentEightByte < eeClass->GetNumberEightBytes())
97 {
98 SystemVClassificationType eightByte = eeClass->GetEightByteClassification(m_currentEightByte);
99 unsigned int eightByteSize = eeClass->GetEightByteSize(m_currentEightByte);
100
101 m_currentEightByte++;
102
103 int index;
104 UINT16 mask = ShuffleEntry::REGMASK;
105
106 if (eightByte == SystemVClassificationTypeSSE)
107 {
108 _ASSERTE(m_currentFloatRegIndex < m_argLocDesc->m_cFloatReg);
109 index = m_argLocDesc->m_idxFloatReg + m_currentFloatRegIndex;
110 m_currentFloatRegIndex++;
111
112 mask |= ShuffleEntry::FPREGMASK;
113 if (eightByteSize == 4)
114 {
115 mask |= ShuffleEntry::FPSINGLEMASK;
116 }
117 }
118 else
119 {
120 _ASSERTE(m_currentGenRegIndex < m_argLocDesc->m_cGenReg);
121 index = m_argLocDesc->m_idxGenReg + m_currentGenRegIndex;
122 m_currentGenRegIndex++;
123 }
124
125 return (UINT16)index | mask;
126 }
127
128 // There are no more offsets to get, the caller should not have called us
129 _ASSERTE(false);
130 return 0;
131 }
132#endif // UNIX_AMD64_ABI
133
134public:
135
136 // Construct the iterator for the ArgLocDesc
137 ShuffleIterator(ArgLocDesc* argLocDesc)
138 :
139 m_argLocDesc(argLocDesc),
140#if defined(UNIX_AMD64_ABI)
141 m_currentEightByte(0),
142#endif
143 m_currentGenRegIndex(0),
144 m_currentFloatRegIndex(0),
145 m_currentStackSlotIndex(0)
146 {
147 }
148
149 // Check if there are more offsets to shuffle
150 bool HasNextOfs()
151 {
152 return (m_currentGenRegIndex < m_argLocDesc->m_cGenReg) ||
153#if defined(UNIX_AMD64_ABI)
154 (m_currentFloatRegIndex < m_argLocDesc->m_cFloatReg) ||
155#endif
156 (m_currentStackSlotIndex < m_argLocDesc->m_cStack);
157 }
158
159 // Get next offset to shuffle. There has to be at least one offset left.
160 UINT16 GetNextOfs()
161 {
162 int index;
163
164#if defined(UNIX_AMD64_ABI)
165
166 // Check if the argLocDesc is for a struct in registers
167 EEClass* eeClass = m_argLocDesc->m_eeClass;
168 if (m_argLocDesc->m_eeClass != 0)
169 {
170 return GetNextOfsInStruct();
171 }
172
173 // Shuffle float registers first
174 if (m_currentFloatRegIndex < m_argLocDesc->m_cFloatReg)
175 {
176 index = m_argLocDesc->m_idxFloatReg + m_currentFloatRegIndex;
177 m_currentFloatRegIndex++;
178
179 return (UINT16)index | ShuffleEntry::REGMASK | ShuffleEntry::FPREGMASK;
180 }
181#endif // UNIX_AMD64_ABI
182
183 // Shuffle any registers first (the order matters since otherwise we could end up shuffling a stack slot
184 // over a register we later need to shuffle down as well).
185 if (m_currentGenRegIndex < m_argLocDesc->m_cGenReg)
186 {
187 index = m_argLocDesc->m_idxGenReg + m_currentGenRegIndex;
188 m_currentGenRegIndex++;
189
190 return (UINT16)index | ShuffleEntry::REGMASK;
191 }
192
193 // If we get here we must have at least one stack slot left to shuffle (this method should only be called
194 // when AnythingToShuffle(pArg) == true).
195 if (m_currentStackSlotIndex < m_argLocDesc->m_cStack)
196 {
197 index = m_argLocDesc->m_idxStack + m_currentStackSlotIndex;
198 m_currentStackSlotIndex++;
199
200 // Delegates cannot handle overly large argument stacks due to shuffle entry encoding limitations.
201 if (index >= ShuffleEntry::REGMASK)
202 {
203 COMPlusThrow(kNotSupportedException);
204 }
205
206 return (UINT16)index;
207 }
208
209 // There are no more offsets to get, the caller should not have called us
210 _ASSERTE(false);
211 return 0;
212 }
213};
214
215#endif
216
217#if defined(UNIX_AMD64_ABI)
218// Return an index of argument slot. First indices are reserved for general purpose registers,
219// the following ones for float registers and then the rest for stack slots.
220// This index is independent of how many registers are actually used to pass arguments.
221int GetNormalizedArgumentSlotIndex(UINT16 offset)
222{
223 int index;
224
225 if (offset & ShuffleEntry::FPREGMASK)
226 {
227 index = NUM_ARGUMENT_REGISTERS + (offset & ShuffleEntry::OFSREGMASK);
228 }
229 else if (offset & ShuffleEntry::REGMASK)
230 {
231 index = offset & ShuffleEntry::OFSREGMASK;
232 }
233 else
234 {
235 // stack slot
236 index = NUM_ARGUMENT_REGISTERS + NUM_FLOAT_ARGUMENT_REGISTERS + (offset & ShuffleEntry::OFSMASK);
237 }
238
239 return index;
240}
241#endif // UNIX_AMD64_ABI
242
243VOID GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, SArray<ShuffleEntry> * pShuffleEntryArray)
244{
245 STANDARD_VM_CONTRACT;
246
247 ShuffleEntry entry;
248 ZeroMemory(&entry, sizeof(entry));
249
250#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
251 MetaSig msig(pInvoke);
252 ArgIterator argit(&msig);
253
254 if (argit.HasRetBuffArg())
255 {
256 if (!pTargetMeth->IsStatic())
257 {
258 // Use ELEMENT_TYPE_END to signal the special handling required by
259 // instance method with return buffer. "this" needs to come from
260 // the first argument.
261 entry.argtype = ELEMENT_TYPE_END;
262 pShuffleEntryArray->Append(entry);
263
264 msig.NextArgNormalized();
265 }
266 else
267 {
268 entry.argtype = ELEMENT_TYPE_PTR;
269 pShuffleEntryArray->Append(entry);
270 }
271 }
272
273 CorElementType sigType;
274
275 while ((sigType = msig.NextArgNormalized()) != ELEMENT_TYPE_END)
276 {
277 ZeroMemory(&entry, sizeof(entry));
278 entry.argtype = sigType;
279 pShuffleEntryArray->Append(entry);
280 }
281
282 ZeroMemory(&entry, sizeof(entry));
283 entry.srcofs = ShuffleEntry::SENTINEL;
284 pShuffleEntryArray->Append(entry);
285
286#elif defined(_TARGET_X86_)
287 // Must create independent msigs to prevent the argiterators from
288 // interfering with other.
289 MetaSig sSigSrc(pInvoke);
290 MetaSig sSigDst(pTargetMeth);
291
292 _ASSERTE(sSigSrc.HasThis());
293
294 ArgIterator sArgPlacerSrc(&sSigSrc);
295 ArgIterator sArgPlacerDst(&sSigDst);
296
297 UINT stackSizeSrc = sArgPlacerSrc.SizeOfArgStack();
298 UINT stackSizeDst = sArgPlacerDst.SizeOfArgStack();
299
300 if (stackSizeDst > stackSizeSrc)
301 {
302 // we can drop arguments but we can never make them up - this is definitely not allowed
303 COMPlusThrow(kVerificationException);
304 }
305
306 UINT stackSizeDelta;
307
308#ifdef UNIX_X86_ABI
309 // Stack does not shrink as UNIX_X86_ABI uses CDECL (instead of STDCALL).
310 stackSizeDelta = 0;
311#else
312 stackSizeDelta = stackSizeSrc - stackSizeDst;
313#endif
314
315 INT ofsSrc, ofsDst;
316
317 // if the function is non static we need to place the 'this' first
318 if (!pTargetMeth->IsStatic())
319 {
320 entry.srcofs = ShuffleOfs(sArgPlacerSrc.GetNextOffset());
321 entry.dstofs = ShuffleEntry::REGMASK | 4;
322 pShuffleEntryArray->Append(entry);
323 }
324 else if (sArgPlacerSrc.HasRetBuffArg())
325 {
326 // the first register is used for 'this'
327 entry.srcofs = ShuffleOfs(sArgPlacerSrc.GetRetBuffArgOffset());
328 entry.dstofs = ShuffleOfs(sArgPlacerDst.GetRetBuffArgOffset(), stackSizeDelta);
329 if (entry.srcofs != entry.dstofs)
330 pShuffleEntryArray->Append(entry);
331 }
332
333 while (TransitionBlock::InvalidOffset != (ofsSrc = sArgPlacerSrc.GetNextOffset()))
334 {
335 ofsDst = sArgPlacerDst.GetNextOffset();
336
337 int cbSize = sArgPlacerDst.GetArgSize();
338
339 do
340 {
341 entry.srcofs = ShuffleOfs(ofsSrc);
342 entry.dstofs = ShuffleOfs(ofsDst, stackSizeDelta);
343
344 ofsSrc += STACK_ELEM_SIZE;
345 ofsDst += STACK_ELEM_SIZE;
346
347 if (entry.srcofs != entry.dstofs)
348 pShuffleEntryArray->Append(entry);
349
350 cbSize -= STACK_ELEM_SIZE;
351 }
352 while (cbSize > 0);
353 }
354
355 if (stackSizeDelta != 0)
356 {
357 // Emit code to move the return address
358 entry.srcofs = 0; // retaddress is assumed to be at esp
359 entry.dstofs = static_cast<UINT16>(stackSizeDelta);
360 pShuffleEntryArray->Append(entry);
361 }
362
363 entry.srcofs = ShuffleEntry::SENTINEL;
364 entry.dstofs = static_cast<UINT16>(stackSizeDelta);
365 pShuffleEntryArray->Append(entry);
366
367#else // Portable default implementation
368 MetaSig sSigSrc(pInvoke);
369 MetaSig sSigDst(pTargetMeth);
370
371 // Initialize helpers that determine how each argument for the source and destination signatures is placed
372 // in registers or on the stack.
373 ArgIterator sArgPlacerSrc(&sSigSrc);
374 ArgIterator sArgPlacerDst(&sSigDst);
375
376 INT ofsSrc;
377 INT ofsDst;
378 ArgLocDesc sArgSrc;
379 ArgLocDesc sArgDst;
380
381#if defined(UNIX_AMD64_ABI)
382 int argSlots = NUM_FLOAT_ARGUMENT_REGISTERS + NUM_ARGUMENT_REGISTERS + sArgPlacerSrc.SizeOfArgStack() / sizeof(size_t);
383#endif // UNIX_AMD64_ABI
384
385 // If the target method in non-static (this happens for open instance delegates), we need to account for
386 // the implicit this parameter.
387 if (sSigDst.HasThis())
388 {
389 // The this pointer is an implicit argument for the destination signature. But on the source side it's
390 // just another regular argument and needs to be iterated over by sArgPlacerSrc and the MetaSig.
391 sArgPlacerSrc.GetArgLoc(sArgPlacerSrc.GetNextOffset(), &sArgSrc);
392
393 sArgPlacerSrc.GetThisLoc(&sArgDst);
394
395 ShuffleIterator iteratorSrc(&sArgSrc);
396 ShuffleIterator iteratorDst(&sArgDst);
397
398 entry.srcofs = iteratorSrc.GetNextOfs();
399 entry.dstofs = iteratorDst.GetNextOfs();
400 pShuffleEntryArray->Append(entry);
401 }
402
403 // Handle any return buffer argument.
404 if (sArgPlacerDst.HasRetBuffArg())
405 {
406 // The return buffer argument is implicit in both signatures.
407
408#if !defined(_TARGET_ARM64_) || !defined(CALLDESCR_RETBUFFARGREG)
409 // The ifdef above disables this code if the ret buff arg is always in the same register, which
410 // means that we don't need to do any shuffling for it.
411
412 sArgPlacerSrc.GetRetBuffArgLoc(&sArgSrc);
413 sArgPlacerDst.GetRetBuffArgLoc(&sArgDst);
414
415 ShuffleIterator iteratorSrc(&sArgSrc);
416 ShuffleIterator iteratorDst(&sArgDst);
417
418 entry.srcofs = iteratorSrc.GetNextOfs();
419 entry.dstofs = iteratorDst.GetNextOfs();
420
421 // Depending on the type of target method (static vs instance) the return buffer argument may end up
422 // in the same register in both signatures. So we only commit the entry (by moving the entry pointer
423 // along) in the case where it's not a no-op (i.e. the source and destination ops are different).
424 if (entry.srcofs != entry.dstofs)
425 pShuffleEntryArray->Append(entry);
426#endif // !defined(_TARGET_ARM64_) || !defined(CALLDESCR_RETBUFFARGREG)
427 }
428
429 // Iterate all the regular arguments. mapping source registers and stack locations to the corresponding
430 // destination locations.
431 while ((ofsSrc = sArgPlacerSrc.GetNextOffset()) != TransitionBlock::InvalidOffset)
432 {
433 ofsDst = sArgPlacerDst.GetNextOffset();
434
435 // Find the argument location mapping for both source and destination signature. A single argument can
436 // occupy a floating point register, a general purpose register, a pair of registers of any kind or
437 // a stack slot.
438 sArgPlacerSrc.GetArgLoc(ofsSrc, &sArgSrc);
439 sArgPlacerDst.GetArgLoc(ofsDst, &sArgDst);
440
441 ShuffleIterator iteratorSrc(&sArgSrc);
442 ShuffleIterator iteratorDst(&sArgDst);
443
444 // Shuffle each slot in the argument (register or stack slot) from source to destination.
445 while (iteratorSrc.HasNextOfs())
446 {
447 // Locate the next slot to shuffle in the source and destination and encode the transfer into a
448 // shuffle entry.
449 entry.srcofs = iteratorSrc.GetNextOfs();
450 entry.dstofs = iteratorDst.GetNextOfs();
451
452 // Only emit this entry if it's not a no-op (i.e. the source and destination locations are
453 // different).
454 if (entry.srcofs != entry.dstofs)
455 pShuffleEntryArray->Append(entry);
456 }
457
458 // We should have run out of slots to shuffle in the destination at the same time as the source.
459 _ASSERTE(!iteratorDst.HasNextOfs());
460 }
461
462#if defined(UNIX_AMD64_ABI)
463 // The Unix AMD64 ABI can cause a struct to be passed on stack for the source and in registers for the destination.
464 // That can cause some arguments that are passed on stack for the destination to be passed in registers in the source.
465 // An extreme example of that is e.g.:
466 // void fn(int, int, int, int, int, struct {int, double}, double, double, double, double, double, double, double, double, double, double)
467 // For this signature, the shuffle needs to move slots as follows (please note the "forward" movement of xmm registers):
468 // RDI->RSI, RDX->RCX, R8->RDX, R9->R8, stack[0]->R9, xmm0->xmm1, xmm1->xmm2, ... xmm6->xmm7, xmm7->stack[0], stack[1]->xmm0, stack[2]->stack[1], stack[3]->stack[2]
469 // To prevent overwriting of slots before they are moved, we need to sort the move operations.
470
471 NewArrayHolder<bool> filledSlots = new bool[argSlots];
472
473 bool reordered;
474 do
475 {
476 reordered = false;
477
478 for (int i = 0; i < argSlots; i++)
479 {
480 filledSlots[i] = false;
481 }
482 for (int i = 0; i < pShuffleEntryArray->GetCount(); i++)
483 {
484 entry = (*pShuffleEntryArray)[i];
485
486 // If the slot that we are moving the argument to was filled in already, we need to move this entry in front
487 // of the entry that filled it in.
488 if (filledSlots[GetNormalizedArgumentSlotIndex(entry.srcofs)])
489 {
490 int j;
491 for (j = i; (*pShuffleEntryArray)[j].dstofs != entry.srcofs; j--)
492 (*pShuffleEntryArray)[j] = (*pShuffleEntryArray)[j - 1];
493
494 (*pShuffleEntryArray)[j] = entry;
495 reordered = true;
496 }
497
498 filledSlots[GetNormalizedArgumentSlotIndex(entry.dstofs)] = true;
499 }
500 }
501 while (reordered);
502#endif // UNIX_AMD64_ABI
503
504 entry.srcofs = ShuffleEntry::SENTINEL;
505 entry.dstofs = 0;
506 pShuffleEntryArray->Append(entry);
507#endif
508}
509
510
511ShuffleThunkCache *COMDelegate::m_pShuffleThunkCache = NULL;
512MulticastStubCache *COMDelegate::m_pSecureDelegateStubCache = NULL;
513MulticastStubCache *COMDelegate::m_pMulticastStubCache = NULL;
514
515CrstStatic COMDelegate::s_DelegateToFPtrHashCrst;
516PtrHashMap* COMDelegate::s_pDelegateToFPtrHash = NULL;
517
518
519// One time init.
520void COMDelegate::Init()
521{
522 CONTRACTL
523 {
524 THROWS;
525 GC_NOTRIGGER;
526 MODE_ANY;
527 }
528 CONTRACTL_END;
529
530 s_DelegateToFPtrHashCrst.Init(CrstDelegateToFPtrHash, CRST_UNSAFE_ANYMODE);
531
532 s_pDelegateToFPtrHash = ::new PtrHashMap();
533
534 LockOwner lock = {&COMDelegate::s_DelegateToFPtrHashCrst, IsOwnerOfCrst};
535 s_pDelegateToFPtrHash->Init(TRUE, &lock);
536
537 m_pShuffleThunkCache = new ShuffleThunkCache(SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap());
538 m_pMulticastStubCache = new MulticastStubCache();
539 m_pSecureDelegateStubCache = new MulticastStubCache();
540}
541
542#ifdef FEATURE_COMINTEROP
543ComPlusCallInfo * COMDelegate::PopulateComPlusCallInfo(MethodTable * pDelMT)
544{
545 CONTRACTL
546 {
547 THROWS;
548 GC_TRIGGERS;
549 MODE_ANY;
550 }
551 CONTRACTL_END;
552
553 DelegateEEClass * pClass = (DelegateEEClass *)pDelMT->GetClass();
554
555 // set up the ComPlusCallInfo if it does not exist already
556 if (pClass->m_pComPlusCallInfo == NULL)
557 {
558 LoaderHeap *pHeap = pDelMT->GetLoaderAllocator()->GetHighFrequencyHeap();
559 ComPlusCallInfo *pTemp = (ComPlusCallInfo *)(void *)pHeap->AllocMem(S_SIZE_T(sizeof(ComPlusCallInfo)));
560
561 pTemp->m_cachedComSlot = ComMethodTable::GetNumExtraSlots(ifVtable);
562 pTemp->InitStackArgumentSize();
563
564 InterlockedCompareExchangeT(EnsureWritablePages(&pClass->m_pComPlusCallInfo), pTemp, NULL);
565 }
566
567 *EnsureWritablePages(&pClass->m_pComPlusCallInfo->m_pInterfaceMT) = pDelMT;
568
569 return pClass->m_pComPlusCallInfo;
570}
571#endif // FEATURE_COMINTEROP
572
573// We need a LoaderHeap that lives at least as long as the DelegateEEClass, but ideally no longer
574LoaderHeap *DelegateEEClass::GetStubHeap()
575{
576 return GetInvokeMethod()->GetLoaderAllocator()->GetStubHeap();
577}
578
579
580Stub* COMDelegate::SetupShuffleThunk(MethodTable * pDelMT, MethodDesc *pTargetMeth)
581{
582 CONTRACTL
583 {
584 THROWS;
585 GC_TRIGGERS;
586 MODE_ANY;
587 INJECT_FAULT(COMPlusThrowOM());
588 }
589 CONTRACTL_END;
590
591 GCX_PREEMP();
592
593 DelegateEEClass * pClass = (DelegateEEClass *)pDelMT->GetClass();
594
595 MethodDesc *pMD = pClass->GetInvokeMethod();
596
597 StackSArray<ShuffleEntry> rShuffleEntryArray;
598 GenerateShuffleArray(pMD, pTargetMeth, &rShuffleEntryArray);
599
600 ShuffleThunkCache* pShuffleThunkCache = m_pShuffleThunkCache;
601
602 LoaderAllocator* pLoaderAllocator = pDelMT->GetLoaderAllocator();
603 if (pLoaderAllocator->IsCollectible())
604 {
605 pShuffleThunkCache = ((AssemblyLoaderAllocator*)pLoaderAllocator)->GetShuffleThunkCache();
606 }
607
608 Stub* pShuffleThunk = pShuffleThunkCache->Canonicalize((const BYTE *)&rShuffleEntryArray[0]);
609 if (!pShuffleThunk)
610 {
611 COMPlusThrowOM();
612 }
613
614 g_IBCLogger.LogEEClassCOWTableAccess(pDelMT);
615
616 EnsureWritablePages(pClass);
617
618 if (!pTargetMeth->IsStatic() && pTargetMeth->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
619 {
620 if (FastInterlockCompareExchangePointer(&pClass->m_pInstRetBuffCallStub, pShuffleThunk, NULL ) != NULL)
621 {
622 pShuffleThunk->DecRef();
623 pShuffleThunk = pClass->m_pInstRetBuffCallStub;
624 }
625 }
626 else
627 {
628 if (FastInterlockCompareExchangePointer(&pClass->m_pStaticCallStub, pShuffleThunk, NULL ) != NULL)
629 {
630 pShuffleThunk->DecRef();
631 pShuffleThunk = pClass->m_pStaticCallStub;
632 }
633 }
634
635 return pShuffleThunk;
636}
637
638
639#ifndef CROSSGEN_COMPILE
640
641static PCODE GetVirtualCallStub(MethodDesc *method, TypeHandle scopeType)
642{
643 CONTRACTL
644 {
645 THROWS;
646 GC_TRIGGERS;
647 MODE_ANY;
648 INJECT_FAULT(COMPlusThrowOM()); // from MetaSig::SizeOfArgStack
649 }
650 CONTRACTL_END;
651
652 //TODO: depending on what we decide for generics method we may want to move this check to better places
653 if (method->IsGenericMethodDefinition() || method->HasMethodInstantiation())
654 {
655 COMPlusThrow(kNotSupportedException);
656 }
657
658 // need to grab a virtual dispatch stub
659 // method can be on a canonical MethodTable, we need to allocate the stub on the loader allocator associated with the exact type instantiation.
660 VirtualCallStubManager *pVirtualStubManager = scopeType.GetMethodTable()->GetLoaderAllocator()->GetVirtualCallStubManager();
661 PCODE pTargetCall = pVirtualStubManager->GetCallStub(scopeType, method);
662 _ASSERTE(pTargetCall);
663 return pTargetCall;
664}
665
666FCIMPL5(FC_BOOL_RET, COMDelegate::BindToMethodName,
667 Object *refThisUNSAFE,
668 Object *targetUNSAFE,
669 ReflectClassBaseObject *pMethodTypeUNSAFE,
670 StringObject* methodNameUNSAFE,
671 int flags)
672{
673 FCALL_CONTRACT;
674
675 struct _gc
676 {
677 DELEGATEREF refThis;
678 OBJECTREF target;
679 STRINGREF methodName;
680 REFLECTCLASSBASEREF refMethodType;
681 } gc;
682
683 gc.refThis = (DELEGATEREF) ObjectToOBJECTREF(refThisUNSAFE);
684 gc.target = (OBJECTREF) targetUNSAFE;
685 gc.methodName = (STRINGREF) methodNameUNSAFE;
686 gc.refMethodType = (REFLECTCLASSBASEREF) ObjectToOBJECTREF(pMethodTypeUNSAFE);
687
688 TypeHandle methodType = gc.refMethodType->GetType();
689
690 MethodDesc *pMatchingMethod = NULL;
691
692 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
693
694 // Caching of MethodDescs (impl and decl) for MethodTable slots provided significant
695 // performance gain in some reflection emit scenarios.
696 MethodTable::AllowMethodDataCaching();
697
698 TypeHandle targetType((gc.target != NULL) ? gc.target->GetMethodTable() : NULL);
699 // get the invoke of the delegate
700 MethodTable * pDelegateType = gc.refThis->GetMethodTable();
701 MethodDesc* pInvokeMeth = COMDelegate::FindDelegateInvokeMethod(pDelegateType);
702 _ASSERTE(pInvokeMeth);
703
704 //
705 // now loop through the methods looking for a match
706 //
707
708 // get the name in UTF8 format
709 SString wszName(SString::Literal, gc.methodName->GetBuffer());
710 StackScratchBuffer utf8Name;
711 LPCUTF8 szNameStr = wszName.GetUTF8(utf8Name);
712
713 // pick a proper compare function
714 typedef int (__cdecl *UTF8StringCompareFuncPtr)(const char *, const char *);
715 UTF8StringCompareFuncPtr StrCompFunc = (flags & DBF_CaselessMatching) ? stricmpUTF8 : strcmp;
716
717 // search the type hierarchy
718 MethodTable *pMTOrig = methodType.GetMethodTable()->GetCanonicalMethodTable();
719 for (MethodTable *pMT = pMTOrig; pMT != NULL; pMT = pMT->GetParentMethodTable())
720 {
721 MethodTable::MethodIterator it(pMT);
722 it.MoveToEnd();
723 for (; it.IsValid() && (pMT == pMTOrig || !it.IsVirtual()); it.Prev())
724 {
725 MethodDesc *pCurMethod = it.GetDeclMethodDesc();
726
727 // We can't match generic methods (since no instantiation information has been provided).
728 if (pCurMethod->IsGenericMethodDefinition())
729 continue;
730
731 if ((pCurMethod != NULL) && (StrCompFunc(szNameStr, pCurMethod->GetName()) == 0))
732 {
733 // found a matching string, get an associated method desc if needed
734 // Use unboxing stubs for instance and virtual methods on value types.
735 // If this is a open delegate to an instance method BindToMethod will rebind it to the non-unboxing method.
736 // Open delegate
737 // Static: never use unboxing stub
738 // BindToMethodInfo/Name will bind to the non-unboxing stub. BindToMethod will reinforce that.
739 // Instance: We only support binding to an unboxed value type reference here, so we must never use an unboxing stub
740 // BindToMethodInfo/Name will bind to the unboxing stub. BindToMethod will rebind to the non-unboxing stub.
741 // Virtual: trivial (not allowed)
742 // Closed delegate
743 // Static: never use unboxing stub
744 // BindToMethodInfo/Name will bind to the non-unboxing stub.
745 // Instance: always use unboxing stub
746 // BindToMethodInfo/Name will bind to the unboxing stub.
747 // Virtual: always use unboxing stub
748 // BindToMethodInfo/Name will bind to the unboxing stub.
749
750 pCurMethod =
751 MethodDesc::FindOrCreateAssociatedMethodDesc(pCurMethod,
752 methodType.GetMethodTable(),
753 (!pCurMethod->IsStatic() && pCurMethod->GetMethodTable()->IsValueType()),
754 pCurMethod->GetMethodInstantiation(),
755 false /* do not allow code with a shared-code calling convention to be returned */,
756 true /* Ensure that methods on generic interfaces are returned as instantiated method descs */);
757 BOOL fIsOpenDelegate;
758 if (!COMDelegate::IsMethodDescCompatible((gc.target == NULL) ? TypeHandle() : gc.target->GetTrueTypeHandle(),
759 methodType,
760 pCurMethod,
761 gc.refThis->GetTypeHandle(),
762 pInvokeMeth,
763 flags,
764 &fIsOpenDelegate))
765 {
766 // Signature doesn't match, skip.
767 continue;
768 }
769
770 // Found the target that matches the signature and satisfies security transparency rules
771 // Initialize the delegate to point to the target method.
772 BindToMethod(&gc.refThis,
773 &gc.target,
774 pCurMethod,
775 methodType.GetMethodTable(),
776 fIsOpenDelegate,
777 TRUE);
778
779 pMatchingMethod = pCurMethod;
780 goto done;
781 }
782 }
783 }
784 done:
785 ;
786 HELPER_METHOD_FRAME_END();
787
788 FC_RETURN_BOOL(pMatchingMethod != NULL);
789}
790FCIMPLEND
791
792
793FCIMPL5(FC_BOOL_RET, COMDelegate::BindToMethodInfo, Object* refThisUNSAFE, Object* targetUNSAFE, ReflectMethodObject *pMethodUNSAFE, ReflectClassBaseObject *pMethodTypeUNSAFE, int flags)
794{
795 FCALL_CONTRACT;
796
797 BOOL result = TRUE;
798
799 struct _gc
800 {
801 DELEGATEREF refThis;
802 OBJECTREF refFirstArg;
803 REFLECTCLASSBASEREF refMethodType;
804 REFLECTMETHODREF refMethod;
805 } gc;
806
807 gc.refThis = (DELEGATEREF) ObjectToOBJECTREF(refThisUNSAFE);
808 gc.refFirstArg = ObjectToOBJECTREF(targetUNSAFE);
809 gc.refMethodType = (REFLECTCLASSBASEREF) ObjectToOBJECTREF(pMethodTypeUNSAFE);
810 gc.refMethod = (REFLECTMETHODREF) ObjectToOBJECTREF(pMethodUNSAFE);
811
812 MethodTable *pMethMT = gc.refMethodType->GetType().GetMethodTable();
813 MethodDesc *method = gc.refMethod->GetMethod();
814
815 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
816
817 // Assert to track down VS#458689.
818 _ASSERTE(gc.refThis != gc.refFirstArg);
819
820 // A generic method had better be instantiated (we can't dispatch to an uninstantiated one).
821 if (method->IsGenericMethodDefinition())
822 COMPlusThrow(kArgumentException, W("Arg_DlgtTargMeth"));
823
824 // get the invoke of the delegate
825 MethodTable * pDelegateType = gc.refThis->GetMethodTable();
826 MethodDesc* pInvokeMeth = COMDelegate::FindDelegateInvokeMethod(pDelegateType);
827 _ASSERTE(pInvokeMeth);
828
829 // See the comment in BindToMethodName
830 method =
831 MethodDesc::FindOrCreateAssociatedMethodDesc(method,
832 pMethMT,
833 (!method->IsStatic() && pMethMT->IsValueType()),
834 method->GetMethodInstantiation(),
835 false /* do not allow code with a shared-code calling convention to be returned */,
836 true /* Ensure that methods on generic interfaces are returned as instantiated method descs */);
837
838 BOOL fIsOpenDelegate;
839 if (COMDelegate::IsMethodDescCompatible((gc.refFirstArg == NULL) ? TypeHandle() : gc.refFirstArg->GetTrueTypeHandle(),
840 TypeHandle(pMethMT),
841 method,
842 gc.refThis->GetTypeHandle(),
843 pInvokeMeth,
844 flags,
845 &fIsOpenDelegate))
846 {
847 // Initialize the delegate to point to the target method.
848 BindToMethod(&gc.refThis,
849 &gc.refFirstArg,
850 method,
851 pMethMT,
852 fIsOpenDelegate,
853 !(flags & DBF_SkipSecurityChecks));
854 }
855 else
856 result = FALSE;
857
858 HELPER_METHOD_FRAME_END();
859
860 FC_RETURN_BOOL(result);
861}
862FCIMPLEND
863
864// This method is called (in the late bound case only) once a target method has been decided on. All the consistency checks
865// (signature matching etc.) have been done at this point and the only major reason we could fail now is on security grounds
866// (someone trying to create a delegate over a method that's not visible to them for instance). This method will initialize the
867// delegate (wrapping it in a secure delegate if necessary). Upon return the delegate should be ready for invocation.
868void COMDelegate::BindToMethod(DELEGATEREF *pRefThis,
869 OBJECTREF *pRefFirstArg,
870 MethodDesc *pTargetMethod,
871 MethodTable *pExactMethodType,
872 BOOL fIsOpenDelegate,
873 BOOL fCheckSecurity)
874{
875 CONTRACTL
876 {
877 THROWS;
878 GC_TRIGGERS;
879 MODE_COOPERATIVE;
880 PRECONDITION(CheckPointer(pRefThis));
881 PRECONDITION(CheckPointer(pRefFirstArg, NULL_OK));
882 PRECONDITION(CheckPointer(pTargetMethod));
883 PRECONDITION(CheckPointer(pExactMethodType));
884 }
885 CONTRACTL_END;
886
887 // We might have to wrap the delegate in a secure delegate depending on the location of the target method. The following local
888 // keeps track of the real (i.e. non-secure) delegate whether or not this is required.
889 DELEGATEREF refRealDelegate = NULL;
890 GCPROTECT_BEGIN(refRealDelegate);
891
892 // Security checks (i.e. whether the creator of the delegate is allowed to access the target method) are the norm. They are only
893 // disabled when:
894 // 1. this is called by deserialization to recreate an existing delegate instance, where such checks are unwarranted.
895 // 2. this is called from DynamicMethod.CreateDelegate which doesn't need access check.
896 if (fCheckSecurity)
897 {
898 MethodTable *pInstanceMT = pExactMethodType;
899 bool targetPossiblyRemoted = false;
900
901 if (fIsOpenDelegate)
902 {
903 _ASSERTE(pRefFirstArg == NULL || *pRefFirstArg == NULL);
904
905 }
906 else
907 {
908 // closed-static is OK and we can check the target in the closed-instance case
909 pInstanceMT = (*pRefFirstArg == NULL ? NULL : (*pRefFirstArg)->GetMethodTable());
910 }
911
912 RefSecContext sCtx(InvokeUtil::GetInvocationAccessCheckType(targetPossiblyRemoted));
913
914 // Check visibility of the target method. If it's an instance method, we have to pass the type
915 // of the instance being accessed which we get from the first argument or from the method itself.
916 // The type of the instance is necessary for visibility checks of protected methods.
917 InvokeUtil::CheckAccessMethod(&sCtx,
918 pExactMethodType,
919 pTargetMethod->IsStatic() ? NULL : pInstanceMT,
920 pTargetMethod);
921 }
922
923 // If we didn't wrap the real delegate in a secure delegate then the real delegate is the one passed in.
924 if (refRealDelegate == NULL)
925 {
926 if (NeedsWrapperDelegate(pTargetMethod))
927 refRealDelegate = CreateSecureDelegate(*pRefThis, NULL, pTargetMethod);
928 else
929 refRealDelegate = *pRefThis;
930 }
931
932 pTargetMethod->EnsureActive();
933
934 if (fIsOpenDelegate)
935 {
936 _ASSERTE(pRefFirstArg == NULL || *pRefFirstArg == NULL);
937
938 // Open delegates use themselves as the target (which handily allows their shuffle thunks to locate additional data at
939 // invocation time).
940 refRealDelegate->SetTarget(refRealDelegate);
941
942 // We need to shuffle arguments for open delegates since the first argument on the calling side is not meaningful to the
943 // callee.
944 MethodTable * pDelegateMT = (*pRefThis)->GetMethodTable();
945 DelegateEEClass *pDelegateClass = (DelegateEEClass*)pDelegateMT->GetClass();
946 Stub *pShuffleThunk = NULL;
947
948 // Look for a thunk cached on the delegate class first. Note we need a different thunk for instance methods with a
949 // hidden return buffer argument because the extra argument switches place with the target when coming from the caller.
950 if (!pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
951 pShuffleThunk = pDelegateClass->m_pInstRetBuffCallStub;
952 else
953 pShuffleThunk = pDelegateClass->m_pStaticCallStub;
954
955 // If we haven't already setup a shuffle thunk go do it now (which will cache the result automatically).
956 if (!pShuffleThunk)
957 pShuffleThunk = SetupShuffleThunk(pDelegateMT, pTargetMethod);
958
959 // Indicate that the delegate will jump to the shuffle thunk rather than directly to the target method.
960 refRealDelegate->SetMethodPtr(pShuffleThunk->GetEntryPoint());
961
962 // Use stub dispatch for all virtuals.
963 // <TODO> Investigate not using this for non-interface virtuals. </TODO>
964 // The virtual dispatch stub doesn't work on unboxed value type objects which don't have MT pointers.
965 // Since open instance delegates on value type methods require unboxed objects we cannot use the
966 // virtual dispatch stub for them. On the other hand, virtual methods on value types don't need
967 // to be dispatched because value types cannot be derived. So we treat them like non-virtual methods.
968 if (pTargetMethod->IsVirtual() && !pTargetMethod->GetMethodTable()->IsValueType())
969 {
970 // Since this is an open delegate over a virtual method we cannot virtualize the call target now. So the shuffle thunk
971 // needs to jump to another stub (this time provided by the VirtualStubManager) that will virtualize the call at
972 // runtime.
973 PCODE pTargetCall = GetVirtualCallStub(pTargetMethod, TypeHandle(pExactMethodType));
974 refRealDelegate->SetMethodPtrAux(pTargetCall);
975 refRealDelegate->SetInvocationCount((INT_PTR)(void *)pTargetMethod);
976 }
977 else
978 {
979 // <TODO> If VSD isn't compiled in this gives the wrong result for virtuals (we need run time virtualization). </TODO>
980 // Reflection or the code in BindToMethodName will pass us the unboxing stub for non-static methods on value types. But
981 // for open invocation on value type methods the actual reference will be passed so we need the unboxed method desc
982 // instead.
983 if (pTargetMethod->IsUnboxingStub())
984 {
985 // We want a MethodDesc which is not an unboxing stub, but is an instantiating stub if needed.
986 pTargetMethod = MethodDesc::FindOrCreateAssociatedMethodDesc(
987 pTargetMethod,
988 pExactMethodType,
989 FALSE /* don't want unboxing entry point */,
990 pTargetMethod->GetMethodInstantiation(),
991 FALSE /* don't want MD that requires inst. arguments */,
992 true /* Ensure that methods on generic interfaces are returned as instantiated method descs */);
993 }
994
995 // The method must not require any extra hidden instantiation arguments.
996 _ASSERTE(!pTargetMethod->RequiresInstArg());
997
998 // Note that it is important to cache pTargetCode in local variable to avoid GC hole.
999 // GetMultiCallableAddrOfCode() can trigger GC.
1000 PCODE pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode();
1001 refRealDelegate->SetMethodPtrAux(pTargetCode);
1002 }
1003 }
1004 else
1005 {
1006 PCODE pTargetCode = NULL;
1007
1008 // For virtual methods we can (and should) virtualize the call now (so we don't have to insert a thunk to do so at runtime).
1009 // <TODO>
1010 // Remove the following if we decide we won't cope with this case on late bound.
1011 // We can get virtual delegates closed over null through this code path, so be careful to handle that case (no need to
1012 // virtualize since we're just going to throw NullRefException at invocation time).
1013 // </TODO>
1014 if (pTargetMethod->IsVirtual() &&
1015 *pRefFirstArg != NULL &&
1016 pTargetMethod->GetMethodTable() != (*pRefFirstArg)->GetMethodTable())
1017 pTargetCode = pTargetMethod->GetMultiCallableAddrOfVirtualizedCode(pRefFirstArg, pTargetMethod->GetMethodTable());
1018 else
1019#ifdef HAS_THISPTR_RETBUF_PRECODE
1020 if (pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1021 pTargetCode = pTargetMethod->GetLoaderAllocatorForCode()->GetFuncPtrStubs()->GetFuncPtrStub(pTargetMethod, PRECODE_THISPTR_RETBUF);
1022 else
1023#endif // HAS_THISPTR_RETBUF_PRECODE
1024 pTargetCode = pTargetMethod->GetMultiCallableAddrOfCode();
1025 _ASSERTE(pTargetCode);
1026
1027 refRealDelegate->SetTarget(*pRefFirstArg);
1028 refRealDelegate->SetMethodPtr(pTargetCode);
1029 }
1030
1031 LoaderAllocator *pLoaderAllocator = pTargetMethod->GetLoaderAllocator();
1032
1033 if (pLoaderAllocator->IsCollectible())
1034 refRealDelegate->SetMethodBase(pLoaderAllocator->GetExposedObject());
1035
1036 GCPROTECT_END();
1037}
1038
1039// Marshals a managed method to an unmanaged callback provided the
1040// managed method is static and it's parameters require no marshalling.
1041PCODE COMDelegate::ConvertToCallback(MethodDesc* pMD)
1042{
1043 CONTRACTL
1044 {
1045 THROWS;
1046 GC_TRIGGERS;
1047 INJECT_FAULT(COMPlusThrowOM());
1048 }
1049 CONTRACTL_END;
1050
1051 PCODE pCode = NULL;
1052
1053 // only static methods are allowed
1054 if (!pMD->IsStatic())
1055 COMPlusThrow(kNotSupportedException, W("NotSupported_NonStaticMethod"));
1056
1057 // no generic methods
1058 if (pMD->IsGenericMethodDefinition())
1059 COMPlusThrow(kNotSupportedException, W("NotSupported_GenericMethod"));
1060
1061 // Arguments
1062 if (NDirect::MarshalingRequired(pMD, pMD->GetSig(), pMD->GetModule()))
1063 COMPlusThrow(kNotSupportedException, W("NotSupported_NonBlittableTypes"));
1064
1065 // Get UMEntryThunk from the thunk cache.
1066 UMEntryThunk *pUMEntryThunk = pMD->GetLoaderAllocator()->GetUMEntryThunkCache()->GetUMEntryThunk(pMD);
1067
1068#if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
1069
1070 // System.Runtime.InteropServices.NativeCallableAttribute
1071 BYTE* pData = NULL;
1072 LONG cData = 0;
1073 CorPinvokeMap callConv = (CorPinvokeMap)0;
1074
1075 HRESULT hr = pMD->GetMDImport()->GetCustomAttributeByName(pMD->GetMemberDef(), g_NativeCallableAttribute, (const VOID **)(&pData), (ULONG *)&cData);
1076 IfFailThrow(hr);
1077
1078 if (cData > 0)
1079 {
1080 CustomAttributeParser ca(pData, cData);
1081 // NativeCallable has two optional named arguments CallingConvention and EntryPoint.
1082 CaNamedArg namedArgs[2];
1083 CaTypeCtor caType(SERIALIZATION_TYPE_STRING);
1084 // First, the void constructor.
1085 IfFailThrow(ParseKnownCaArgs(ca, NULL, 0));
1086
1087 // Now the optional named properties
1088 namedArgs[0].InitI4FieldEnum("CallingConvention", "System.Runtime.InteropServices.CallingConvention", (ULONG)callConv);
1089 namedArgs[1].Init("EntryPoint", SERIALIZATION_TYPE_STRING, caType);
1090 IfFailThrow(ParseKnownCaNamedArgs(ca, namedArgs, lengthof(namedArgs)));
1091
1092 callConv = (CorPinvokeMap)(namedArgs[0].val.u4 << 8);
1093 // Let UMThunkMarshalInfo choose the default if calling convension not definied.
1094 if (namedArgs[0].val.type.tag != SERIALIZATION_TYPE_UNDEFINED)
1095 {
1096 UMThunkMarshInfo* pUMThunkMarshalInfo = pUMEntryThunk->GetUMThunkMarshInfo();
1097 pUMThunkMarshalInfo->SetCallingConvention(callConv);
1098 }
1099}
1100#endif //_TARGET_X86_ && !FEATURE_STUBS_AS_IL
1101
1102 pCode = (PCODE)pUMEntryThunk->GetCode();
1103 _ASSERTE(pCode != NULL);
1104 return pCode;
1105}
1106
1107// Marshals a delegate to a unmanaged callback.
1108LPVOID COMDelegate::ConvertToCallback(OBJECTREF pDelegateObj)
1109{
1110 CONTRACTL
1111 {
1112 THROWS;
1113 GC_TRIGGERS;
1114 MODE_COOPERATIVE;
1115
1116 INJECT_FAULT(COMPlusThrowOM());
1117 }
1118 CONTRACTL_END;
1119
1120 if (!pDelegateObj)
1121 return NULL;
1122
1123 DELEGATEREF pDelegate = (DELEGATEREF) pDelegateObj;
1124
1125 PCODE pCode;
1126 GCPROTECT_BEGIN(pDelegate);
1127
1128 MethodTable* pMT = pDelegate->GetMethodTable();
1129 DelegateEEClass* pClass = (DelegateEEClass*)(pMT->GetClass());
1130
1131 if (pMT->HasInstantiation())
1132 COMPlusThrowArgumentException(W("delegate"), W("Argument_NeedNonGenericType"));
1133
1134 // If we are a delegate originally created from an unmanaged function pointer, we will simply return
1135 // that function pointer.
1136 if (DELEGATE_MARKER_UNMANAGEDFPTR == pDelegate->GetInvocationCount())
1137 {
1138 pCode = pDelegate->GetMethodPtrAux();
1139 }
1140 else
1141 {
1142 UMEntryThunk* pUMEntryThunk = NULL;
1143 SyncBlock* pSyncBlock = pDelegate->GetSyncBlock();
1144
1145 InteropSyncBlockInfo* pInteropInfo = pSyncBlock->GetInteropInfo();
1146
1147 pUMEntryThunk = (UMEntryThunk*)pInteropInfo->GetUMEntryThunk();
1148
1149 if (!pUMEntryThunk)
1150 {
1151
1152 UMThunkMarshInfo *pUMThunkMarshInfo = pClass->m_pUMThunkMarshInfo;
1153 MethodDesc *pInvokeMeth = FindDelegateInvokeMethod(pMT);
1154
1155 if (!pUMThunkMarshInfo)
1156 {
1157 GCX_PREEMP();
1158
1159 pUMThunkMarshInfo = new UMThunkMarshInfo();
1160 pUMThunkMarshInfo->LoadTimeInit(pInvokeMeth);
1161
1162 g_IBCLogger.LogEEClassCOWTableAccess(pMT);
1163 EnsureWritablePages(pClass);
1164 if (FastInterlockCompareExchangePointer(&(pClass->m_pUMThunkMarshInfo),
1165 pUMThunkMarshInfo,
1166 NULL ) != NULL)
1167 {
1168 delete pUMThunkMarshInfo;
1169 pUMThunkMarshInfo = pClass->m_pUMThunkMarshInfo;
1170 }
1171 }
1172
1173 _ASSERTE(pUMThunkMarshInfo != NULL);
1174 _ASSERTE(pUMThunkMarshInfo == pClass->m_pUMThunkMarshInfo);
1175
1176 pUMEntryThunk = UMEntryThunk::CreateUMEntryThunk();
1177 Holder<UMEntryThunk *, DoNothing, UMEntryThunk::FreeUMEntryThunk> umHolder;
1178 umHolder.Assign(pUMEntryThunk);
1179
1180 // multicast. go thru Invoke
1181 OBJECTHANDLE objhnd = GetAppDomain()->CreateLongWeakHandle(pDelegate);
1182 _ASSERTE(objhnd != NULL);
1183
1184 // This target should not ever be used. We are storing it in the thunk for better diagnostics of "call on collected delegate" crashes.
1185 PCODE pManagedTargetForDiagnostics = (pDelegate->GetMethodPtrAux() != NULL) ? pDelegate->GetMethodPtrAux() : pDelegate->GetMethodPtr();
1186
1187 // MethodDesc is passed in for profiling to know the method desc of target
1188 pUMEntryThunk->LoadTimeInit(
1189 pManagedTargetForDiagnostics,
1190 objhnd,
1191 pUMThunkMarshInfo, pInvokeMeth,
1192 GetAppDomain()->GetId());
1193
1194#ifdef FEATURE_WINDOWSPHONE
1195 // Perform the runtime initialization lazily for better startup time. Lazy initialization
1196 // has worse diagnostic experience (the invalid marshaling directive exception is thrown
1197 // lazily on the first call instead of during delegate creation), but it should be ok
1198 // for CoreCLR on phone because of reverse p-invoke is for internal use only.
1199#else
1200 {
1201 GCX_PREEMP();
1202
1203 pUMEntryThunk->RunTimeInit();
1204 }
1205#endif
1206
1207 if (!pInteropInfo->SetUMEntryThunk(pUMEntryThunk))
1208 {
1209 pUMEntryThunk = (UMEntryThunk*)pInteropInfo->GetUMEntryThunk();
1210 }
1211 else
1212 {
1213 umHolder.SuppressRelease();
1214 // Insert the delegate handle / UMEntryThunk* into the hash
1215 LPVOID key = (LPVOID)pUMEntryThunk;
1216
1217 // Assert that the entry isn't already in the hash.
1218 _ASSERTE((LPVOID)INVALIDENTRY == COMDelegate::s_pDelegateToFPtrHash->LookupValue((UPTR)key, 0));
1219
1220 {
1221 CrstHolder ch(&COMDelegate::s_DelegateToFPtrHashCrst);
1222 COMDelegate::s_pDelegateToFPtrHash->InsertValue((UPTR)key, pUMEntryThunk->GetObjectHandle());
1223 }
1224 }
1225
1226 _ASSERTE(pUMEntryThunk != NULL);
1227 _ASSERTE(pUMEntryThunk == (UMEntryThunk*)pInteropInfo->GetUMEntryThunk());
1228
1229 }
1230 pCode = (PCODE)pUMEntryThunk->GetCode();
1231 }
1232
1233 GCPROTECT_END();
1234 return (LPVOID)pCode;
1235}
1236
1237// Marshals an unmanaged callback to Delegate
1238//static
1239OBJECTREF COMDelegate::ConvertToDelegate(LPVOID pCallback, MethodTable* pMT)
1240{
1241 CONTRACTL
1242 {
1243 THROWS;
1244 GC_TRIGGERS;
1245 MODE_COOPERATIVE;
1246 }
1247 CONTRACTL_END;
1248
1249 if (!pCallback)
1250 {
1251 return NULL;
1252 }
1253
1254 //////////////////////////////////////////////////////////////////////////////////////////////////////////
1255 // Check if this callback was originally a managed method passed out to unmanaged code.
1256 //
1257
1258 UMEntryThunk* pUMEntryThunk = NULL;
1259
1260#ifdef MDA_SUPPORTED
1261 if (MDA_GET_ASSISTANT(InvalidFunctionPointerInDelegate))
1262 {
1263 EX_TRY
1264 {
1265 AVInRuntimeImplOkayHolder AVOkay;
1266 pUMEntryThunk = UMEntryThunk::Decode(pCallback);
1267 }
1268 EX_CATCH
1269 {
1270 MDA_TRIGGER_ASSISTANT(InvalidFunctionPointerInDelegate, ReportViolation(pCallback));
1271 }
1272 EX_END_CATCH(SwallowAllExceptions)
1273 }
1274 else
1275#endif // MDA_SUPPORTED
1276 {
1277 pUMEntryThunk = UMEntryThunk::Decode(pCallback);
1278 }
1279
1280 // Lookup the callsite in the hash, if found, we can map this call back to its managed function.
1281 // Otherwise, we'll treat this as an unmanaged callsite.
1282 // Make sure that the pointer doesn't have the value of 1 which is our hash table deleted item marker.
1283 LPVOID DelegateHnd = (pUMEntryThunk != NULL) && ((UPTR)pUMEntryThunk != (UPTR)1)
1284 ? COMDelegate::s_pDelegateToFPtrHash->LookupValue((UPTR)pUMEntryThunk, 0)
1285 : (LPVOID)INVALIDENTRY;
1286
1287 if (DelegateHnd != (LPVOID)INVALIDENTRY)
1288 {
1289 // Found a managed callsite
1290 OBJECTREF pDelegate = NULL;
1291 GCPROTECT_BEGIN(pDelegate);
1292
1293 pDelegate = ObjectFromHandle((OBJECTHANDLE)DelegateHnd);
1294
1295 // Make sure we're not trying to sneak into another domain.
1296 SyncBlock* pSyncBlock = pDelegate->GetSyncBlock();
1297 _ASSERTE(pSyncBlock);
1298
1299 InteropSyncBlockInfo* pInteropInfo = pSyncBlock->GetInteropInfo();
1300 _ASSERTE(pInteropInfo);
1301
1302 pUMEntryThunk = (UMEntryThunk*)pInteropInfo->GetUMEntryThunk();
1303 _ASSERTE(pUMEntryThunk);
1304
1305 if (pUMEntryThunk->GetDomainId() != GetAppDomain()->GetId())
1306 COMPlusThrow(kNotSupportedException, W("NotSupported_DelegateMarshalToWrongDomain"));
1307
1308 GCPROTECT_END();
1309 return pDelegate;
1310 }
1311
1312
1313 //////////////////////////////////////////////////////////////////////////////////////////////////////////
1314 // This is an unmanaged callsite. We need to create a new delegate.
1315 //
1316 // The delegate's invoke method will point to a call thunk.
1317 // The call thunk will internally shuffle the args, set up a DelegateTransitionFrame, marshal the args,
1318 // call the UM Function located at m_pAuxField, unmarshal the args, and return.
1319 // Invoke -> CallThunk -> ShuffleThunk -> Frame -> Marshal -> Call AuxField -> UnMarshal
1320
1321 DelegateEEClass* pClass = (DelegateEEClass*)pMT->GetClass();
1322 MethodDesc* pMD = FindDelegateInvokeMethod(pMT);
1323
1324 PREFIX_ASSUME(pClass != NULL);
1325
1326 //////////////////////////////////////////////////////////////////////////////////////////////////////////
1327 // Get or create the marshaling stub information
1328 //
1329
1330 PCODE pMarshalStub = pClass->m_pMarshalStub;
1331 if (pMarshalStub == NULL)
1332 {
1333 GCX_PREEMP();
1334
1335 pMarshalStub = GetStubForInteropMethod(pMD, 0, &(pClass->m_pForwardStubMD));
1336
1337 // Save this new stub on the DelegateEEClass.
1338 EnsureWritablePages(dac_cast<PVOID>(&pClass->m_pMarshalStub), sizeof(PCODE));
1339 InterlockedCompareExchangeT<PCODE>(&pClass->m_pMarshalStub, pMarshalStub, NULL);
1340
1341 pMarshalStub = pClass->m_pMarshalStub;
1342 }
1343
1344 // The IL marshaling stub performs the function of the shuffle thunk - it simply omits 'this' in
1345 // the call to unmanaged code. The stub recovers the unmanaged target from the delegate instance.
1346
1347 _ASSERTE(pMarshalStub != NULL);
1348
1349 //////////////////////////////////////////////////////////////////////////////////////////////////////////
1350 // Wire up the stubs to the new delegate instance.
1351 //
1352
1353 LOG((LF_INTEROP, LL_INFO10000, "Created delegate for function pointer: entrypoint: %p\n", pMarshalStub));
1354
1355 // Create the new delegate
1356 DELEGATEREF delObj = (DELEGATEREF) pMT->Allocate();
1357
1358 {
1359 // delObj is not protected
1360 GCX_NOTRIGGER();
1361
1362 // Wire up the unmanaged call stub to the delegate.
1363 delObj->SetTarget(delObj); // We are the "this" object
1364
1365 // For X86, we save the entry point in the delegate's method pointer and the UM Callsite in the aux pointer.
1366 delObj->SetMethodPtr(pMarshalStub);
1367 delObj->SetMethodPtrAux((PCODE)pCallback);
1368
1369 // Also, mark this delegate as an unmanaged function pointer wrapper.
1370 delObj->SetInvocationCount(DELEGATE_MARKER_UNMANAGEDFPTR);
1371 }
1372
1373#if defined(_TARGET_X86_)
1374 GCPROTECT_BEGIN(delObj);
1375
1376 Stub *pInterceptStub = NULL;
1377
1378 {
1379 GCX_PREEMP();
1380
1381 MethodDesc *pStubMD = pClass->m_pForwardStubMD;
1382 _ASSERTE(pStubMD != NULL && pStubMD->IsILStub());
1383
1384#if defined(MDA_SUPPORTED)
1385 if (MDA_GET_ASSISTANT(PInvokeStackImbalance))
1386 {
1387 pInterceptStub = GenerateStubForMDA(pMD, pStubMD, pCallback, pInterceptStub);
1388 }
1389#endif // MDA_SUPPORTED
1390 }
1391
1392 if (pInterceptStub != NULL)
1393 {
1394 // install the outer-most stub to sync block
1395 SyncBlock *pSyncBlock = delObj->GetSyncBlock();
1396
1397 InteropSyncBlockInfo *pInteropInfo = pSyncBlock->GetInteropInfo();
1398 VERIFY(pInteropInfo->SetInterceptStub(pInterceptStub));
1399 }
1400
1401 GCPROTECT_END();
1402#endif // _TARGET_X86_
1403
1404 return delObj;
1405}
1406
1407#ifdef FEATURE_COMINTEROP
1408// Marshals a WinRT delegate interface pointer to a managed Delegate
1409//static
1410OBJECTREF COMDelegate::ConvertWinRTInterfaceToDelegate(IUnknown *pIdentity, MethodTable* pMT)
1411{
1412 CONTRACTL
1413 {
1414 THROWS;
1415 GC_TRIGGERS;
1416 MODE_COOPERATIVE;
1417 PRECONDITION(CheckPointer(pIdentity));
1418 PRECONDITION(CheckPointer(pMT));
1419 }
1420 CONTRACTL_END;
1421
1422 MethodDesc* pMD = FindDelegateInvokeMethod(pMT);
1423
1424 if (pMD->IsSharedByGenericInstantiations())
1425 {
1426 // we need an exact MD to represent the call
1427 pMD = InstantiatedMethodDesc::FindOrCreateExactClassMethod(pMT, pMD);
1428 }
1429 else
1430 {
1431 // set up ComPlusCallInfo
1432 PopulateComPlusCallInfo(pMT);
1433 }
1434
1435 ComPlusCallInfo *pComInfo = ComPlusCallInfo::FromMethodDesc(pMD);
1436 PCODE pMarshalStub = (pComInfo == NULL ? NULL : pComInfo->m_pILStub);
1437
1438 if (pMarshalStub == NULL)
1439 {
1440 GCX_PREEMP();
1441
1442 DWORD dwStubFlags = NDIRECTSTUB_FL_COM | NDIRECTSTUB_FL_WINRT | NDIRECTSTUB_FL_WINRTDELEGATE;
1443
1444 pMarshalStub = GetStubForInteropMethod(pMD, dwStubFlags);
1445
1446 // At this point we must have a non-NULL ComPlusCallInfo
1447 pComInfo = ComPlusCallInfo::FromMethodDesc(pMD);
1448 _ASSERTE(pComInfo != NULL);
1449
1450 // Save this new stub on the ComPlusCallInfo
1451 InterlockedCompareExchangeT<PCODE>(EnsureWritablePages(&pComInfo->m_pILStub), pMarshalStub, NULL);
1452
1453 pMarshalStub = pComInfo->m_pILStub;
1454 }
1455
1456 _ASSERTE(pMarshalStub != NULL);
1457
1458 //////////////////////////////////////////////////////////////////////////////////////////////////////////
1459 // Wire up the stub to the new delegate instance.
1460 //
1461
1462 LOG((LF_INTEROP, LL_INFO10000, "Created delegate for WinRT interface: pUnk: %p\n", pIdentity));
1463
1464 // Create the new delegate
1465 DELEGATEREF delObj = (DELEGATEREF) pMT->Allocate();
1466
1467 {
1468 // delObj is not protected
1469 GCX_NOTRIGGER();
1470
1471 // Wire up the unmanaged call stub to the delegate.
1472 delObj->SetTarget(delObj); // We are the "this" object
1473
1474 // We save the entry point in the delegate's method pointer and the identity pUnk in the aux pointer.
1475 delObj->SetMethodPtr(pMarshalStub);
1476 delObj->SetMethodPtrAux((PCODE)pIdentity);
1477
1478 // Also, mark this delegate as an unmanaged function pointer wrapper.
1479 delObj->SetInvocationCount(DELEGATE_MARKER_UNMANAGEDFPTR);
1480 }
1481
1482 return delObj;
1483}
1484#endif // FEATURE_COMINTEROP
1485
1486void COMDelegate::ValidateDelegatePInvoke(MethodDesc* pMD)
1487{
1488 CONTRACTL
1489 {
1490 THROWS;
1491 GC_TRIGGERS;
1492 MODE_ANY;
1493
1494 PRECONDITION(CheckPointer(pMD));
1495 }
1496 CONTRACTL_END;
1497
1498 if (pMD->IsSynchronized())
1499 COMPlusThrow(kTypeLoadException, IDS_EE_NOSYNCHRONIZED);
1500
1501 if (pMD->MethodDesc::IsVarArg())
1502 COMPlusThrow(kNotSupportedException, IDS_EE_VARARG_NOT_SUPPORTED);
1503}
1504
1505// static
1506PCODE COMDelegate::GetStubForILStub(EEImplMethodDesc* pDelegateMD, MethodDesc** ppStubMD, DWORD dwStubFlags)
1507{
1508 CONTRACT(PCODE)
1509 {
1510 STANDARD_VM_CHECK;
1511
1512 PRECONDITION(CheckPointer(pDelegateMD));
1513 POSTCONDITION(RETVAL != NULL);
1514 }
1515 CONTRACT_END;
1516
1517 ValidateDelegatePInvoke(pDelegateMD);
1518
1519 dwStubFlags |= NDIRECTSTUB_FL_DELEGATE;
1520
1521 RETURN NDirect::GetStubForILStub(pDelegateMD, ppStubMD, dwStubFlags);
1522}
1523
1524#endif // CROSSGEN_COMPILE
1525
1526
1527// static
1528MethodDesc* COMDelegate::GetILStubMethodDesc(EEImplMethodDesc* pDelegateMD, DWORD dwStubFlags)
1529{
1530 STANDARD_VM_CONTRACT;
1531
1532 MethodTable *pMT = pDelegateMD->GetMethodTable();
1533
1534#ifdef FEATURE_COMINTEROP
1535 if (pMT->IsWinRTDelegate())
1536 {
1537 dwStubFlags |= NDIRECTSTUB_FL_COM | NDIRECTSTUB_FL_WINRT | NDIRECTSTUB_FL_WINRTDELEGATE;
1538 }
1539 else
1540#endif // FEATURE_COMINTEROP
1541 {
1542 dwStubFlags |= NDIRECTSTUB_FL_DELEGATE;
1543 }
1544
1545 PInvokeStaticSigInfo sigInfo(pDelegateMD);
1546 return NDirect::CreateCLRToNativeILStub(&sigInfo, dwStubFlags, pDelegateMD);
1547}
1548
1549
1550#ifndef CROSSGEN_COMPILE
1551
1552FCIMPL2(FC_BOOL_RET, COMDelegate::CompareUnmanagedFunctionPtrs, Object *refDelegate1UNSAFE, Object *refDelegate2UNSAFE)
1553{
1554 CONTRACTL
1555 {
1556 FCALL_CHECK;
1557 PRECONDITION(refDelegate1UNSAFE != NULL);
1558 PRECONDITION(refDelegate2UNSAFE != NULL);
1559 }
1560 CONTRACTL_END;
1561
1562 DELEGATEREF refD1 = (DELEGATEREF) ObjectToOBJECTREF(refDelegate1UNSAFE);
1563 DELEGATEREF refD2 = (DELEGATEREF) ObjectToOBJECTREF(refDelegate2UNSAFE);
1564 BOOL ret = FALSE;
1565
1566 // Make sure this is an unmanaged function pointer wrapped in a delegate.
1567 CONSISTENCY_CHECK(DELEGATE_MARKER_UNMANAGEDFPTR == refD1->GetInvocationCount());
1568 CONSISTENCY_CHECK(DELEGATE_MARKER_UNMANAGEDFPTR == refD2->GetInvocationCount());
1569
1570 ret = (refD1->GetMethodPtr() == refD2->GetMethodPtr() &&
1571 refD1->GetMethodPtrAux() == refD2->GetMethodPtrAux());
1572
1573 FC_RETURN_BOOL(ret);
1574}
1575FCIMPLEND
1576
1577
1578void COMDelegate::RemoveEntryFromFPtrHash(UPTR key)
1579{
1580 WRAPPER_NO_CONTRACT;
1581
1582 // Remove this entry from the lookup hash.
1583 CrstHolder ch(&COMDelegate::s_DelegateToFPtrHashCrst);
1584 COMDelegate::s_pDelegateToFPtrHash->DeleteValue(key, NULL);
1585}
1586
1587FCIMPL2(PCODE, COMDelegate::GetCallStub, Object* refThisUNSAFE, PCODE method)
1588{
1589 FCALL_CONTRACT;
1590
1591 PCODE target = NULL;
1592
1593 DELEGATEREF refThis = (DELEGATEREF)ObjectToOBJECTREF(refThisUNSAFE);
1594 HELPER_METHOD_FRAME_BEGIN_RET_1(refThis);
1595 MethodDesc *pMeth = MethodTable::GetMethodDescForSlotAddress((PCODE)method);
1596 _ASSERTE(pMeth);
1597 _ASSERTE(!pMeth->IsStatic() && pMeth->IsVirtual());
1598 target = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable()));
1599 refThis->SetInvocationCount((INT_PTR)(void*)pMeth);
1600 HELPER_METHOD_FRAME_END();
1601 return target;
1602}
1603FCIMPLEND
1604
1605FCIMPL3(PCODE, COMDelegate::AdjustTarget, Object* refThisUNSAFE, Object* targetUNSAFE, PCODE method)
1606{
1607 FCALL_CONTRACT;
1608
1609 if (targetUNSAFE == NULL)
1610 FCThrow(kArgumentNullException);
1611
1612 OBJECTREF refThis = ObjectToOBJECTREF(refThisUNSAFE);
1613 OBJECTREF target = ObjectToOBJECTREF(targetUNSAFE);
1614
1615 HELPER_METHOD_FRAME_BEGIN_RET_2(refThis, target);
1616
1617 _ASSERTE(refThis);
1618 _ASSERTE(method);
1619
1620 MethodTable *pMT = target->GetMethodTable();
1621
1622 MethodDesc *pMeth = Entry2MethodDesc(method, pMT);
1623 _ASSERTE(pMeth);
1624 _ASSERTE(!pMeth->IsStatic());
1625
1626 // close delegates
1627 MethodTable* pMTTarg = target->GetMethodTable();
1628 MethodTable* pMTMeth = pMeth->GetMethodTable();
1629
1630 BOOL isComObject = false;
1631
1632#ifdef FEATURE_COMINTEROP
1633 isComObject = pMTTarg->IsComObjectType();
1634#endif // FEATURE_COMINTEROP
1635
1636 MethodDesc *pCorrectedMethod = pMeth;
1637
1638 if (pMTMeth != pMTTarg)
1639 {
1640 //They cast to an interface before creating the delegate, so we now need
1641 //to figure out where this actually lives before we continue.
1642 //<TODO>@perf: Grovelling with a signature is really slow. Speed this up.</TODO>
1643 if (pCorrectedMethod->IsInterface())
1644 {
1645 // No need to resolve the interface based method desc to a class based
1646 // one for COM objects because we invoke directly thru the interface MT.
1647 if (!isComObject)
1648 {
1649 // <TODO>it looks like we need to pass an ownerType in here.
1650 // Why can we take a delegate to an interface method anyway? </TODO>
1651 //
1652 pCorrectedMethod = pMTTarg->FindDispatchSlotForInterfaceMD(pCorrectedMethod, TRUE /* throwOnConflict */).GetMethodDesc();
1653 _ASSERTE(pCorrectedMethod != NULL);
1654 }
1655 }
1656 }
1657
1658 // Use the Unboxing stub for value class methods, since the value
1659 // class is constructed using the boxed instance.
1660 if (pMTTarg->IsValueType() && !pCorrectedMethod->IsUnboxingStub())
1661 {
1662 // those should have been ruled out at jit time (code:COMDelegate::GetDelegateCtor)
1663 _ASSERTE((pMTMeth != g_pValueTypeClass) && (pMTMeth != g_pObjectClass));
1664 pCorrectedMethod->CheckRestore();
1665 pCorrectedMethod = pMTTarg->GetBoxedEntryPointMD(pCorrectedMethod);
1666 _ASSERTE(pCorrectedMethod != NULL);
1667 }
1668
1669 if (pMeth != pCorrectedMethod)
1670 {
1671 method = pCorrectedMethod->GetMultiCallableAddrOfCode();
1672 }
1673 HELPER_METHOD_FRAME_END();
1674
1675 return method;
1676}
1677FCIMPLEND
1678
1679#if defined(_MSC_VER) && !defined(FEATURE_PAL)
1680// VC++ Compiler intrinsic.
1681extern "C" void * _ReturnAddress(void);
1682#endif // _MSC_VER && !FEATURE_PAL
1683
1684// This is the single constructor for all Delegates. The compiler
1685// doesn't provide an implementation of the Delegate constructor. We
1686// provide that implementation through an ECall call to this method.
1687FCIMPL3(void, COMDelegate::DelegateConstruct, Object* refThisUNSAFE, Object* targetUNSAFE, PCODE method)
1688{
1689 FCALL_CONTRACT;
1690
1691 struct _gc
1692 {
1693 DELEGATEREF refThis;
1694 OBJECTREF target;
1695 } gc;
1696
1697 gc.refThis = (DELEGATEREF) ObjectToOBJECTREF(refThisUNSAFE);
1698 gc.target = (OBJECTREF) targetUNSAFE;
1699
1700 HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
1701
1702 // via reflection you can pass in just about any value for the method.
1703 // we can do some basic verification up front to prevent EE exceptions.
1704 if (method == NULL)
1705 COMPlusThrowArgumentNull(W("method"));
1706
1707 _ASSERTE(gc.refThis);
1708 _ASSERTE(method);
1709
1710 // programmers could feed garbage data to DelegateConstruct().
1711 // It's difficult to validate a method code pointer, but at least we'll
1712 // try to catch the easy garbage.
1713 _ASSERTE(isMemoryReadable(method, 1));
1714
1715 MethodTable *pMTTarg = NULL;
1716
1717 if (gc.target != NULL)
1718 {
1719 pMTTarg = gc.target->GetMethodTable();
1720 }
1721
1722 MethodDesc *pMethOrig = Entry2MethodDesc(method, pMTTarg);
1723 MethodDesc *pMeth = pMethOrig;
1724
1725 MethodTable* pDelMT = gc.refThis->GetMethodTable();
1726
1727 LOG((LF_STUBS, LL_INFO1000, "In DelegateConstruct: for delegate type %s binding to method %s::%s%s, static = %d\n",
1728 pDelMT->GetDebugClassName(),
1729 pMeth->m_pszDebugClassName, pMeth->m_pszDebugMethodName, pMeth->m_pszDebugMethodSignature, pMeth->IsStatic()));
1730
1731 _ASSERTE(pMeth);
1732
1733#ifdef _DEBUG
1734 // Assert that everything is OK...This is not some bogus
1735 // address...Very unlikely that the code below would work
1736 // for a random address in memory....
1737 MethodTable* p = pMeth->GetMethodTable();
1738 _ASSERTE(p);
1739 _ASSERTE(p->ValidateWithPossibleAV());
1740#endif // _DEBUG
1741
1742 if (Nullable::IsNullableType(pMeth->GetMethodTable()))
1743 COMPlusThrow(kNotSupportedException);
1744
1745 DelegateEEClass *pDelCls = (DelegateEEClass*)pDelMT->GetClass();
1746 MethodDesc *pDelegateInvoke = COMDelegate::FindDelegateInvokeMethod(pDelMT);
1747
1748 MetaSig invokeSig(pDelegateInvoke);
1749 MetaSig methodSig(pMeth);
1750 UINT invokeArgCount = invokeSig.NumFixedArgs();
1751 UINT methodArgCount = methodSig.NumFixedArgs();
1752 BOOL isStatic = pMeth->IsStatic();
1753 if (!isStatic)
1754 {
1755 methodArgCount++; // count 'this'
1756 }
1757
1758 if (NeedsWrapperDelegate(pMeth))
1759 gc.refThis = CreateSecureDelegate(gc.refThis, NULL, pMeth);
1760
1761 if (pMeth->GetLoaderAllocator()->IsCollectible())
1762 gc.refThis->SetMethodBase(pMeth->GetLoaderAllocator()->GetExposedObject());
1763
1764 // Open delegates.
1765 if (invokeArgCount == methodArgCount)
1766 {
1767 // set the target
1768 gc.refThis->SetTarget(gc.refThis);
1769
1770 // set the shuffle thunk
1771 Stub *pShuffleThunk = NULL;
1772 if (!pMeth->IsStatic() && pMeth->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1773 pShuffleThunk = pDelCls->m_pInstRetBuffCallStub;
1774 else
1775 pShuffleThunk = pDelCls->m_pStaticCallStub;
1776 if (!pShuffleThunk)
1777 pShuffleThunk = SetupShuffleThunk(pDelMT, pMeth);
1778
1779 gc.refThis->SetMethodPtr(pShuffleThunk->GetEntryPoint());
1780
1781 // set the ptr aux according to what is needed, if virtual need to call make virtual stub dispatch
1782 if (!pMeth->IsStatic() && pMeth->IsVirtual() && !pMeth->GetMethodTable()->IsValueType())
1783 {
1784 PCODE pTargetCall = GetVirtualCallStub(pMeth, TypeHandle(pMeth->GetMethodTable()));
1785 gc.refThis->SetMethodPtrAux(pTargetCall);
1786 gc.refThis->SetInvocationCount((INT_PTR)(void *)pMeth);
1787 }
1788 else
1789 {
1790 gc.refThis->SetMethodPtrAux(method);
1791 }
1792 }
1793 else
1794 {
1795 MethodTable* pMTMeth = pMeth->GetMethodTable();
1796
1797 if (!pMeth->IsStatic())
1798 {
1799 if (pMTTarg)
1800 {
1801 // We can skip the demand if SuppressUnmanagedCodePermission is present on the class,
1802 // or in the case where we are setting up a delegate for a COM event sink
1803 // we can skip the check if the source interface is defined in fully trusted code
1804 // we can skip the check if the source interface is a disp-only interface
1805 BOOL isComObject = false;
1806#ifdef FEATURE_COMINTEROP
1807 isComObject = pMTTarg->IsComObjectType();
1808#endif // FEATURE_COMINTEROP
1809
1810 if (pMTMeth != pMTTarg)
1811 {
1812 // They cast to an interface before creating the delegate, so we now need
1813 // to figure out where this actually lives before we continue.
1814 // <TODO>@perf: We whould never be using this path to invoke on an interface -
1815 // that should always be resolved when we are creating the delegate </TODO>
1816 if (pMeth->IsInterface())
1817 {
1818 // No need to resolve the interface based method desc to a class based
1819 // one for COM objects because we invoke directly thru the interface MT.
1820 if (!isComObject)
1821 {
1822 // <TODO>it looks like we need to pass an ownerType in here.
1823 // Why can we take a delegate to an interface method anyway? </TODO>
1824 //
1825 MethodDesc * pDispatchSlotMD = pMTTarg->FindDispatchSlotForInterfaceMD(pMeth, TRUE /* throwOnConflict */).GetMethodDesc();
1826 if (pDispatchSlotMD == NULL)
1827 {
1828 COMPlusThrow(kArgumentException, W("Arg_DlgtTargMeth"));
1829 }
1830
1831 if (pMeth->HasMethodInstantiation())
1832 {
1833 pMeth = MethodDesc::FindOrCreateAssociatedMethodDesc(
1834 pDispatchSlotMD,
1835 pMTTarg,
1836 (!pDispatchSlotMD->IsStatic() && pMTTarg->IsValueType()),
1837 pMeth->GetMethodInstantiation(),
1838 FALSE /* allowInstParam */);
1839 }
1840 else
1841 {
1842 pMeth = pDispatchSlotMD;
1843 }
1844 }
1845 }
1846 }
1847
1848 g_IBCLogger.LogMethodTableAccess(pMTTarg);
1849
1850 // Use the Unboxing stub for value class methods, since the value
1851 // class is constructed using the boxed instance.
1852 //
1853 // <NICE> We could get the JIT to recognise all delegate creation sequences and
1854 // ensure the thing is always an BoxedEntryPointStub anyway </NICE>
1855
1856 if (pMTMeth->IsValueType() && !pMeth->IsUnboxingStub())
1857 {
1858 // If these are Object/ValueType.ToString().. etc,
1859 // don't need an unboxing Stub.
1860
1861 if ((pMTMeth != g_pValueTypeClass)
1862 && (pMTMeth != g_pObjectClass))
1863 {
1864 pMeth->CheckRestore();
1865 pMeth = pMTTarg->GetBoxedEntryPointMD(pMeth);
1866 _ASSERTE(pMeth != NULL);
1867 }
1868 }
1869 // Only update the code address if we've decided to go to a different target...
1870 // <NICE> We should make sure the code address that the JIT provided to us is always the right one anyway,
1871 // so we don't have to do all this mucking about. </NICE>
1872 if (pMeth != pMethOrig)
1873 {
1874 method = pMeth->GetMultiCallableAddrOfCode();
1875 }
1876 }
1877
1878 if (gc.target == NULL)
1879 {
1880 COMPlusThrow(kArgumentException, W("Arg_DlgtNullInst"));
1881 }
1882 }
1883#ifdef HAS_THISPTR_RETBUF_PRECODE
1884 else if (pMeth->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
1885 method = pMeth->GetLoaderAllocatorForCode()->GetFuncPtrStubs()->GetFuncPtrStub(pMeth, PRECODE_THISPTR_RETBUF);
1886#endif // HAS_THISPTR_RETBUF_PRECODE
1887
1888 gc.refThis->SetTarget(gc.target);
1889 gc.refThis->SetMethodPtr((PCODE)(void *)method);
1890 }
1891 HELPER_METHOD_FRAME_END();
1892}
1893FCIMPLEND
1894
1895MethodDesc *COMDelegate::GetMethodDesc(OBJECTREF orDelegate)
1896{
1897 CONTRACTL
1898 {
1899 THROWS;
1900 GC_TRIGGERS;
1901 MODE_COOPERATIVE;
1902 }
1903 CONTRACTL_END;
1904
1905 MethodDesc *pMethodHandle = NULL;
1906
1907 DELEGATEREF thisDel = (DELEGATEREF) orDelegate;
1908 DELEGATEREF innerDel = NULL;
1909
1910 INT_PTR count = thisDel->GetInvocationCount();
1911 if (count != 0)
1912 {
1913 // this is one of the following:
1914 // - multicast - _invocationList is Array && _invocationCount != 0
1915 // - unamanaged ftn ptr - _invocationList == NULL && _invocationCount == -1
1916 // - secure delegate - _invocationList is Delegate && _invocationCount != NULL
1917 // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc)
1918 // or _invocationList points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Secure Delegate)
1919 // in the secure delegate case we want to unwrap and return the method desc of the inner delegate
1920 // in the other cases we return the method desc for the invoke
1921 innerDel = (DELEGATEREF) thisDel->GetInvocationList();
1922 bool fOpenVirtualDelegate = false;
1923
1924 if (innerDel != NULL)
1925 {
1926 MethodTable *pMT = innerDel->GetMethodTable();
1927 if (pMT->IsDelegate())
1928 return GetMethodDesc(innerDel);
1929 if (!pMT->IsArray())
1930 {
1931 // must be a virtual one
1932 fOpenVirtualDelegate = true;
1933 }
1934 }
1935 else
1936 {
1937 if (count != DELEGATE_MARKER_UNMANAGEDFPTR)
1938 {
1939 // must be a virtual one
1940 fOpenVirtualDelegate = true;
1941 }
1942 }
1943
1944 if (fOpenVirtualDelegate)
1945 pMethodHandle = (MethodDesc*)thisDel->GetInvocationCount();
1946 else
1947 pMethodHandle = FindDelegateInvokeMethod(thisDel->GetMethodTable());
1948 }
1949 else
1950 {
1951 // Next, check for an open delegate
1952 PCODE code = thisDel->GetMethodPtrAux();
1953
1954 if (code != NULL)
1955 {
1956 // Note that MethodTable::GetMethodDescForSlotAddress is significantly faster than Entry2MethodDesc
1957 pMethodHandle = MethodTable::GetMethodDescForSlotAddress(code);
1958 }
1959 else
1960 {
1961 MethodTable * pMT = NULL;
1962
1963 // Must be a normal delegate
1964 code = thisDel->GetMethodPtr();
1965
1966 OBJECTREF orThis = thisDel->GetTarget();
1967 if (orThis!=NULL)
1968 {
1969 pMT = orThis->GetMethodTable();
1970 }
1971
1972 pMethodHandle = Entry2MethodDesc(code, pMT);
1973 }
1974 }
1975
1976 _ASSERTE(pMethodHandle);
1977 return pMethodHandle;
1978}
1979
1980OBJECTREF COMDelegate::GetTargetObject(OBJECTREF obj)
1981{
1982 CONTRACTL
1983 {
1984 THROWS;
1985 GC_NOTRIGGER;
1986 MODE_COOPERATIVE;
1987 }
1988 CONTRACTL_END;
1989
1990 OBJECTREF targetObject = NULL;
1991
1992 DELEGATEREF thisDel = (DELEGATEREF) obj;
1993 OBJECTREF innerDel = NULL;
1994
1995 if (thisDel->GetInvocationCount() != 0)
1996 {
1997 // this is one of the following:
1998 // - multicast
1999 // - unmanaged ftn ptr
2000 // - secure delegate
2001 // - virtual delegate - _invocationList == null && _invocationCount == (target MethodDesc)
2002 // or _invocationList points to a LoaderAllocator/DynamicResolver (inner open virtual delegate of a Secure Delegate)
2003 // in the secure delegate case we want to unwrap and return the object of the inner delegate
2004 innerDel = (DELEGATEREF) thisDel->GetInvocationList();
2005 if (innerDel != NULL)
2006 {
2007 MethodTable *pMT = innerDel->GetMethodTable();
2008 if (pMT->IsDelegate())
2009 {
2010 targetObject = GetTargetObject(innerDel);
2011 }
2012 }
2013 }
2014
2015 if (targetObject == NULL)
2016 targetObject = thisDel->GetTarget();
2017
2018 return targetObject;
2019}
2020
2021BOOL COMDelegate::IsTrueMulticastDelegate(OBJECTREF delegate)
2022{
2023 CONTRACTL
2024 {
2025 THROWS;
2026 GC_NOTRIGGER;
2027 MODE_COOPERATIVE;
2028 }
2029 CONTRACTL_END;
2030
2031 BOOL isMulticast = FALSE;
2032
2033 size_t invocationCount = ((DELEGATEREF)delegate)->GetInvocationCount();
2034 if (invocationCount)
2035 {
2036 OBJECTREF invocationList = ((DELEGATEREF)delegate)->GetInvocationList();
2037 if (invocationList != NULL)
2038 {
2039 MethodTable *pMT = invocationList->GetMethodTable();
2040 isMulticast = pMT->IsArray();
2041 }
2042 }
2043
2044 return isMulticast;
2045}
2046
2047PCODE COMDelegate::TheDelegateInvokeStub()
2048{
2049 CONTRACT(PCODE)
2050 {
2051 STANDARD_VM_CHECK;
2052 POSTCONDITION(RETVAL != NULL);
2053 }
2054 CONTRACT_END;
2055
2056#if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
2057 static PCODE s_pInvokeStub;
2058
2059 if (s_pInvokeStub == NULL)
2060 {
2061 CPUSTUBLINKER sl;
2062 sl.EmitDelegateInvoke();
2063 // Process-wide singleton stub that never unloads
2064 Stub *pCandidate = sl.Link(SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap(), NEWSTUB_FL_MULTICAST);
2065
2066 if (InterlockedCompareExchangeT<PCODE>(&s_pInvokeStub, pCandidate->GetEntryPoint(), NULL) != NULL)
2067 {
2068 // if we are here someone managed to set the stub before us so we release the current
2069 pCandidate->DecRef();
2070 }
2071 }
2072
2073 RETURN s_pInvokeStub;
2074#else
2075 RETURN GetEEFuncEntryPoint(SinglecastDelegateInvokeStub);
2076#endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
2077}
2078
2079// Get the cpu stub for a delegate invoke.
2080PCODE COMDelegate::GetInvokeMethodStub(EEImplMethodDesc* pMD)
2081{
2082 CONTRACT(PCODE)
2083 {
2084 STANDARD_VM_CHECK;
2085 POSTCONDITION(RETVAL != NULL);
2086
2087 INJECT_FAULT(COMPlusThrowOM());
2088 }
2089 CONTRACT_END;
2090
2091 PCODE ret = NULL;
2092 MethodTable * pDelMT = pMD->GetMethodTable();
2093 DelegateEEClass* pClass = (DelegateEEClass*) pDelMT->GetClass();
2094
2095 if (pMD == pClass->GetInvokeMethod())
2096 {
2097 // Validate the invoke method, which at the moment just means checking the calling convention
2098
2099 if (*pMD->GetSig() != (IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_DEFAULT))
2100 COMPlusThrow(kInvalidProgramException);
2101
2102 ret = COMDelegate::TheDelegateInvokeStub();
2103 }
2104 else
2105 {
2106
2107 // Since we do not support asynchronous delegates in CoreCLR, we much ensure that it was indeed a async delegate call
2108 // and not an invalid-delegate-layout condition.
2109 //
2110 // If the call was indeed for async delegate invocation, we will just throw an exception.
2111 if ((pMD == pClass->GetBeginInvokeMethod()) || (pMD == pClass->GetEndInvokeMethod()))
2112 {
2113 COMPlusThrow(kPlatformNotSupportedException);
2114 }
2115
2116
2117 _ASSERTE(!"Bad Delegate layout");
2118 COMPlusThrow(kInvalidProgramException);
2119 }
2120
2121 RETURN ret;
2122}
2123
2124FCIMPL1(Object*, COMDelegate::InternalAlloc, ReflectClassBaseObject * pTargetUNSAFE)
2125{
2126 FCALL_CONTRACT;
2127
2128 REFLECTCLASSBASEREF refTarget = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTargetUNSAFE);
2129 OBJECTREF refRetVal = NULL;
2130 TypeHandle targetTH = refTarget->GetType();
2131 HELPER_METHOD_FRAME_BEGIN_RET_1(refTarget);
2132
2133 _ASSERTE(targetTH.GetMethodTable() != NULL && targetTH.GetMethodTable()->IsDelegate());
2134
2135 refRetVal = targetTH.GetMethodTable()->Allocate();
2136
2137 HELPER_METHOD_FRAME_END();
2138 return OBJECTREFToObject(refRetVal);
2139}
2140FCIMPLEND
2141
2142FCIMPL1(Object*, COMDelegate::InternalAllocLike, Object* pThis)
2143{
2144 FCALL_CONTRACT;
2145
2146 OBJECTREF refRetVal = NULL;
2147 HELPER_METHOD_FRAME_BEGIN_RET_NOPOLL();
2148
2149 _ASSERTE(pThis->GetMethodTable() != NULL && pThis->GetMethodTable()->IsDelegate());
2150
2151 refRetVal = pThis->GetMethodTable()->AllocateNoChecks();
2152
2153 HELPER_METHOD_FRAME_END();
2154 return OBJECTREFToObject(refRetVal);
2155}
2156FCIMPLEND
2157
2158FCIMPL2(FC_BOOL_RET, COMDelegate::InternalEqualTypes, Object* pThis, Object *pThat)
2159{
2160 FCALL_CONTRACT;
2161
2162 MethodTable *pThisMT = pThis->GetMethodTable();
2163 MethodTable *pThatMT = pThat->GetMethodTable();
2164
2165 _ASSERTE(pThisMT != NULL && pThisMT->IsDelegate());
2166 _ASSERTE(pThatMT != NULL);
2167
2168 BOOL bResult = (pThisMT == pThatMT);
2169
2170 if (!bResult)
2171 {
2172 HELPER_METHOD_FRAME_BEGIN_RET_0();
2173 bResult = pThisMT->IsEquivalentTo(pThatMT);
2174 HELPER_METHOD_FRAME_END();
2175 }
2176
2177 FC_RETURN_BOOL(bResult);
2178}
2179FCIMPLEND
2180
2181#endif // CROSSGEN_COMPILE
2182
2183BOOL COMDelegate::NeedsWrapperDelegate(MethodDesc* pTargetMD)
2184{
2185 LIMITED_METHOD_CONTRACT;
2186
2187#ifdef _TARGET_ARM_
2188 // For arm VSD expects r4 to contain the indirection cell. However r4 is a non-volatile register
2189 // and its value must be preserved. So we need to erect a frame and store indirection cell in r4 before calling
2190 // virtual stub dispatch. Erecting frame is already done by secure delegates so the secureDelegate infrastructure
2191 // can easliy be used for our purpose.
2192 // set needsSecureDelegate flag in order to erect a frame. (Secure Delegate stub also loads the right value in r4)
2193 if (!pTargetMD->IsStatic() && pTargetMD->IsVirtual() && !pTargetMD->GetMethodTable()->IsValueType())
2194 return TRUE;
2195#endif
2196
2197 return FALSE;
2198}
2199
2200
2201#ifndef CROSSGEN_COMPILE
2202
2203// to create a secure delegate wrapper we need:
2204// - the delegate to forward to -> _invocationList
2205// - the creator assembly -> _methodAuxPtr
2206// - the delegate invoke MethodDesc -> _count
2207// the 2 fields used for invocation will contain:
2208// - the delegate itself -> _pORField
2209// - the secure stub -> _pFPField
2210DELEGATEREF COMDelegate::CreateSecureDelegate(DELEGATEREF delegate, MethodDesc* pCreatorMethod, MethodDesc* pTargetMD)
2211{
2212 CONTRACTL
2213 {
2214 THROWS;
2215 GC_TRIGGERS;
2216 MODE_COOPERATIVE;
2217 }
2218 CONTRACTL_END;
2219
2220 MethodTable *pDelegateType = delegate->GetMethodTable();
2221 MethodDesc *pMD = ((DelegateEEClass*)(pDelegateType->GetClass()))->GetInvokeMethod();
2222 // allocate the object
2223 struct _gc {
2224 DELEGATEREF refSecDel;
2225 DELEGATEREF innerDel;
2226 } gc;
2227 gc.refSecDel = delegate;
2228 gc.innerDel = NULL;
2229
2230 GCPROTECT_BEGIN(gc);
2231
2232 // set the proper fields
2233 //
2234
2235 // Object reference field...
2236 gc.refSecDel->SetTarget(gc.refSecDel);
2237
2238 // save the secure invoke stub. GetSecureInvoke() can trigger GC.
2239 PCODE tmp = GetSecureInvoke(pMD);
2240 gc.refSecDel->SetMethodPtr(tmp);
2241 // save the assembly
2242 gc.refSecDel->SetMethodPtrAux((PCODE)(void *)pCreatorMethod);
2243 // save the delegate MethodDesc for the frame
2244 gc.refSecDel->SetInvocationCount((INT_PTR)pMD);
2245
2246 // save the delegate to forward to
2247 gc.innerDel = (DELEGATEREF) pDelegateType->Allocate();
2248 gc.refSecDel->SetInvocationList(gc.innerDel);
2249
2250 if (pCreatorMethod != NULL)
2251 {
2252 // If the pCreatorMethod is a collectible method, then stash a reference to the
2253 // LoaderAllocator/DynamicResolver of the collectible assembly/method in the invocationList
2254 // of the inner delegate
2255 // (The invocationList of the inner delegate is the only field garaunteed to be unused for
2256 // other purposes at this time.)
2257 if (pCreatorMethod->IsLCGMethod())
2258 {
2259 OBJECTREF refCollectible = pCreatorMethod->AsDynamicMethodDesc()->GetLCGMethodResolver()->GetManagedResolver();
2260 gc.innerDel->SetInvocationList(refCollectible);
2261 }
2262 else if (pCreatorMethod->GetLoaderAllocator()->IsCollectible())
2263 {
2264 OBJECTREF refCollectible = pCreatorMethod->GetLoaderAllocator()->GetExposedObject();
2265 gc.innerDel->SetInvocationList(refCollectible);
2266 }
2267 }
2268
2269 GCPROTECT_END();
2270
2271 return gc.innerDel;
2272}
2273
2274// InternalGetMethodInfo
2275// This method will get the MethodInfo for a delegate
2276FCIMPL1(ReflectMethodObject *, COMDelegate::FindMethodHandle, Object* refThisIn)
2277{
2278 FCALL_CONTRACT;
2279
2280 MethodDesc* pMD = NULL;
2281 REFLECTMETHODREF pRet = NULL;
2282 OBJECTREF refThis = ObjectToOBJECTREF(refThisIn);
2283
2284 HELPER_METHOD_FRAME_BEGIN_RET_1(refThis);
2285
2286 pMD = GetMethodDesc(refThis);
2287 pRet = pMD->GetStubMethodInfo();
2288 HELPER_METHOD_FRAME_END();
2289
2290 return (ReflectMethodObject*)OBJECTREFToObject(pRet);
2291}
2292FCIMPLEND
2293
2294FCIMPL2(FC_BOOL_RET, COMDelegate::InternalEqualMethodHandles, Object *refLeftIn, Object *refRightIn)
2295{
2296 FCALL_CONTRACT;
2297
2298 OBJECTREF refLeft = ObjectToOBJECTREF(refLeftIn);
2299 OBJECTREF refRight = ObjectToOBJECTREF(refRightIn);
2300 BOOL fRet = FALSE;
2301
2302 HELPER_METHOD_FRAME_BEGIN_RET_2(refLeft, refRight);
2303
2304 MethodDesc* pMDLeft = GetMethodDesc(refLeft);
2305 MethodDesc* pMDRight = GetMethodDesc(refRight);
2306 fRet = pMDLeft == pMDRight;
2307
2308 HELPER_METHOD_FRAME_END();
2309
2310 FC_RETURN_BOOL(fRet);
2311}
2312FCIMPLEND
2313
2314FCIMPL1(MethodDesc*, COMDelegate::GetInvokeMethod, Object* refThisIn)
2315{
2316 FCALL_CONTRACT;
2317
2318 OBJECTREF refThis = ObjectToOBJECTREF(refThisIn);
2319 MethodTable * pDelMT = refThis->GetMethodTable();
2320
2321 MethodDesc* pMD = ((DelegateEEClass*)(pDelMT->GetClass()))->GetInvokeMethod();
2322 _ASSERTE(pMD);
2323 return pMD;
2324}
2325FCIMPLEND
2326
2327#ifdef FEATURE_MULTICASTSTUB_AS_IL
2328FCIMPL1(PCODE, COMDelegate::GetMulticastInvoke, Object* refThisIn)
2329{
2330 FCALL_CONTRACT;
2331
2332 OBJECTREF refThis = ObjectToOBJECTREF(refThisIn);
2333 MethodTable *pDelegateMT = refThis->GetMethodTable();
2334
2335 DelegateEEClass *delegateEEClass = ((DelegateEEClass*)(pDelegateMT->GetClass()));
2336 Stub *pStub = delegateEEClass->m_pMultiCastInvokeStub;
2337 if (pStub == NULL)
2338 {
2339 MethodDesc* pMD = delegateEEClass->GetInvokeMethod();
2340
2341 HELPER_METHOD_FRAME_BEGIN_RET_0();
2342
2343 GCX_PREEMP();
2344
2345 MetaSig sig(pMD);
2346
2347 BOOL fReturnVal = !sig.IsReturnTypeVoid();
2348
2349 SigTypeContext emptyContext;
2350 ILStubLinker sl(pMD->GetModule(), pMD->GetSignature(), &emptyContext, pMD, TRUE, TRUE, FALSE);
2351
2352 ILCodeStream *pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
2353
2354 DWORD dwInvocationCountNum = pCode->NewLocal(ELEMENT_TYPE_I4);
2355 DWORD dwLoopCounterNum = pCode->NewLocal(ELEMENT_TYPE_I4);
2356
2357 DWORD dwReturnValNum = -1;
2358 if(fReturnVal)
2359 dwReturnValNum = pCode->NewLocal(sig.GetRetTypeHandleNT());
2360
2361 ILCodeLabel *nextDelegate = pCode->NewCodeLabel();
2362 ILCodeLabel *endOfMethod = pCode->NewCodeLabel();
2363
2364 // Get count of delegates
2365 pCode->EmitLoadThis();
2366 pCode->EmitLDFLD(pCode->GetToken(MscorlibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_COUNT)));
2367 pCode->EmitSTLOC(dwInvocationCountNum);
2368
2369 // initialize counter
2370 pCode->EmitLDC(0);
2371 pCode->EmitSTLOC(dwLoopCounterNum);
2372
2373 //Label_nextDelegate:
2374 pCode->EmitLabel(nextDelegate);
2375
2376#ifdef DEBUGGING_SUPPORTED
2377 pCode->EmitLoadThis();
2378 pCode->EmitLDLOC(dwLoopCounterNum);
2379 pCode->EmitCALL(METHOD__STUBHELPERS__MULTICAST_DEBUGGER_TRACE_HELPER, 2, 0);
2380#endif // DEBUGGING_SUPPORTED
2381
2382 // compare LoopCounter with InvocationCount. If equal then branch to Label_endOfMethod
2383 pCode->EmitLDLOC(dwLoopCounterNum);
2384 pCode->EmitLDLOC(dwInvocationCountNum);
2385 pCode->EmitBEQ(endOfMethod);
2386
2387 // Load next delegate from array using LoopCounter as index
2388 pCode->EmitLoadThis();
2389 pCode->EmitLDFLD(pCode->GetToken(MscorlibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_LIST)));
2390 pCode->EmitLDLOC(dwLoopCounterNum);
2391 pCode->EmitLDELEM_REF();
2392
2393 // Load the arguments
2394 UINT paramCount = 0;
2395 while(paramCount < sig.NumFixedArgs())
2396 pCode->EmitLDARG(paramCount++);
2397
2398 // call the delegate
2399 pCode->EmitCALL(pCode->GetToken(pMD), sig.NumFixedArgs(), fReturnVal);
2400
2401 // Save return value.
2402 if(fReturnVal)
2403 pCode->EmitSTLOC(dwReturnValNum);
2404
2405 // increment counter
2406 pCode->EmitLDLOC(dwLoopCounterNum);
2407 pCode->EmitLDC(1);
2408 pCode->EmitADD();
2409 pCode->EmitSTLOC(dwLoopCounterNum);
2410
2411 // branch to next delegate
2412 pCode->EmitBR(nextDelegate);
2413
2414 //Label_endOfMethod
2415 pCode->EmitLabel(endOfMethod);
2416
2417 // load the return value. return value from the last delegate call is returned
2418 if(fReturnVal)
2419 pCode->EmitLDLOC(dwReturnValNum);
2420
2421 // return
2422 pCode->EmitRET();
2423
2424 PCCOR_SIGNATURE pSig;
2425 DWORD cbSig;
2426
2427 pMD->GetSig(&pSig,&cbSig);
2428
2429 MethodDesc* pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc(pMD->GetLoaderAllocator(),
2430 pMD->GetMethodTable(),
2431 ILSTUB_MULTICASTDELEGATE_INVOKE,
2432 pMD->GetModule(),
2433 pSig, cbSig,
2434 NULL,
2435 &sl);
2436
2437 pStub = Stub::NewStub(JitILStub(pStubMD));
2438
2439 g_IBCLogger.LogEEClassCOWTableAccess(pDelegateMT);
2440
2441 InterlockedCompareExchangeT<PTR_Stub>(EnsureWritablePages(&delegateEEClass->m_pMultiCastInvokeStub), pStub, NULL);
2442
2443 HELPER_METHOD_FRAME_END();
2444 }
2445
2446 return pStub->GetEntryPoint();
2447}
2448FCIMPLEND
2449
2450#else // FEATURE_MULTICASTSTUB_AS_IL
2451
2452FCIMPL1(PCODE, COMDelegate::GetMulticastInvoke, Object* refThisIn)
2453{
2454 FCALL_CONTRACT;
2455
2456 OBJECTREF refThis = ObjectToOBJECTREF(refThisIn);
2457 MethodTable *pDelegateMT = refThis->GetMethodTable();
2458
2459 DelegateEEClass *delegateEEClass = ((DelegateEEClass*)(pDelegateMT->GetClass()));
2460 Stub *pStub = delegateEEClass->m_pMultiCastInvokeStub;
2461 if (pStub == NULL)
2462 {
2463 MethodDesc* pMD = delegateEEClass->GetInvokeMethod();
2464
2465 HELPER_METHOD_FRAME_BEGIN_RET_0();
2466
2467 GCX_PREEMP();
2468
2469 MetaSig sig(pMD);
2470
2471 UINT_PTR hash = CPUSTUBLINKER::HashMulticastInvoke(&sig);
2472
2473 pStub = m_pMulticastStubCache->GetStub(hash);
2474 if (!pStub)
2475 {
2476 CPUSTUBLINKER sl;
2477
2478 LOG((LF_CORDB,LL_INFO10000, "COMD::GIMS making a multicast delegate\n"));
2479
2480 sl.EmitMulticastInvoke(hash);
2481
2482 // The cache is process-wide, based on signature. It never unloads
2483 Stub *pCandidate = sl.Link(SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap(), NEWSTUB_FL_MULTICAST);
2484
2485 Stub *pWinner = m_pMulticastStubCache->AttemptToSetStub(hash,pCandidate);
2486 pCandidate->DecRef();
2487 if (!pWinner)
2488 COMPlusThrowOM();
2489
2490 LOG((LF_CORDB,LL_INFO10000, "Putting a MC stub at 0x%x (code:0x%x)\n",
2491 pWinner, (BYTE*)pWinner+sizeof(Stub)));
2492
2493 pStub = pWinner;
2494 }
2495
2496 g_IBCLogger.LogEEClassCOWTableAccess(pDelegateMT);
2497
2498 // we don't need to do an InterlockedCompareExchange here - the m_pMulticastStubCache->AttemptToSetStub
2499 // will make sure all threads racing here will get the same stub, so they'll all store the same value
2500 EnsureWritablePages(&delegateEEClass->m_pMultiCastInvokeStub);
2501 delegateEEClass->m_pMultiCastInvokeStub = pStub;
2502
2503 HELPER_METHOD_FRAME_END();
2504 }
2505
2506 return pStub->GetEntryPoint();
2507}
2508FCIMPLEND
2509#endif // FEATURE_MULTICASTSTUB_AS_IL
2510
2511#ifdef FEATURE_STUBS_AS_IL
2512PCODE COMDelegate::GetSecureInvoke(MethodDesc* pMD)
2513{
2514 CONTRACTL
2515 {
2516 THROWS;
2517 GC_TRIGGERS;
2518 MODE_ANY;
2519 }
2520 CONTRACTL_END;
2521
2522 MethodTable * pDelegateMT = pMD->GetMethodTable();
2523 DelegateEEClass* delegateEEClass = (DelegateEEClass*) pDelegateMT->GetClass();
2524 Stub *pStub = delegateEEClass->m_pSecureDelegateInvokeStub;
2525
2526 if (pStub == NULL)
2527 {
2528
2529 GCX_PREEMP();
2530
2531 MetaSig sig(pMD);
2532
2533 BOOL fReturnVal = !sig.IsReturnTypeVoid();
2534
2535 SigTypeContext emptyContext;
2536 ILStubLinker sl(pMD->GetModule(), pMD->GetSignature(), &emptyContext, pMD, TRUE, TRUE, FALSE);
2537
2538 ILCodeStream *pCode = sl.NewCodeStream(ILStubLinker::kDispatch);
2539
2540 // Load the "real" delegate
2541 pCode->EmitLoadThis();
2542 pCode->EmitLDFLD(pCode->GetToken(MscorlibBinder::GetField(FIELD__MULTICAST_DELEGATE__INVOCATION_LIST)));
2543
2544 // Load the arguments
2545 UINT paramCount = 0;
2546 while(paramCount < sig.NumFixedArgs())
2547 pCode->EmitLDARG(paramCount++);
2548
2549 // Call the delegate
2550 pCode->EmitCALL(pCode->GetToken(pMD), sig.NumFixedArgs(), fReturnVal);
2551
2552 // Return
2553 pCode->EmitRET();
2554
2555 PCCOR_SIGNATURE pSig;
2556 DWORD cbSig;
2557
2558 pMD->GetSig(&pSig,&cbSig);
2559
2560 MethodDesc* pStubMD =
2561 ILStubCache::CreateAndLinkNewILStubMethodDesc(pMD->GetLoaderAllocator(),
2562 pMD->GetMethodTable(),
2563 ILSTUB_SECUREDELEGATE_INVOKE,
2564 pMD->GetModule(),
2565 pSig, cbSig,
2566 NULL,
2567 &sl);
2568
2569 pStub = Stub::NewStub(JitILStub(pStubMD));
2570
2571 g_IBCLogger.LogEEClassCOWTableAccess(pDelegateMT);
2572
2573 InterlockedCompareExchangeT<PTR_Stub>(EnsureWritablePages(&delegateEEClass->m_pSecureDelegateInvokeStub), pStub, NULL);
2574
2575 }
2576 return pStub->GetEntryPoint();
2577}
2578#else // FEATURE_STUBS_AS_IL
2579PCODE COMDelegate::GetSecureInvoke(MethodDesc* pMD)
2580{
2581 CONTRACT (PCODE)
2582 {
2583 THROWS;
2584 GC_TRIGGERS;
2585 MODE_ANY;
2586 POSTCONDITION(RETVAL != NULL);
2587 }
2588 CONTRACT_END;
2589
2590 MethodTable * pDelegateMT = pMD->GetMethodTable();
2591 DelegateEEClass* delegateEEClass = (DelegateEEClass*) pDelegateMT->GetClass();
2592
2593 Stub *pStub = delegateEEClass->m_pSecureDelegateInvokeStub;
2594
2595 if (pStub == NULL)
2596 {
2597 GCX_PREEMP();
2598
2599 MetaSig sig(pMD);
2600
2601 UINT_PTR hash = CPUSTUBLINKER::HashMulticastInvoke(&sig);
2602
2603 pStub = m_pSecureDelegateStubCache->GetStub(hash);
2604 if (!pStub)
2605 {
2606 CPUSTUBLINKER sl;
2607
2608 LOG((LF_CORDB,LL_INFO10000, "COMD::GIMS making a multicast delegate\n"));
2609 sl.EmitSecureDelegateInvoke(hash);
2610
2611 // The cache is process-wide, based on signature. It never unloads
2612 Stub *pCandidate = sl.Link(SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap(), NEWSTUB_FL_MULTICAST);
2613
2614 Stub *pWinner = m_pSecureDelegateStubCache->AttemptToSetStub(hash, pCandidate);
2615 pCandidate->DecRef();
2616 if (!pWinner)
2617 COMPlusThrowOM();
2618
2619 LOG((LF_CORDB,LL_INFO10000, "Putting a MC stub at 0x%x (code:0x%x)\n",
2620 pWinner, (BYTE*)pWinner+sizeof(Stub)));
2621
2622 pStub = pWinner;
2623 }
2624
2625 g_IBCLogger.LogEEClassCOWTableAccess(pDelegateMT);
2626 EnsureWritablePages(&delegateEEClass->m_pSecureDelegateInvokeStub);
2627 delegateEEClass->m_pSecureDelegateInvokeStub = pStub;
2628 }
2629 RETURN (pStub->GetEntryPoint());
2630}
2631#endif // FEATURE_STUBS_AS_IL
2632
2633#endif // CROSSGEN_COMPILE
2634
2635
2636static BOOL IsLocationAssignable(TypeHandle fromHandle, TypeHandle toHandle, BOOL relaxedMatch, BOOL fromHandleIsBoxed)
2637{
2638 CONTRACTL
2639 {
2640 THROWS;
2641 GC_TRIGGERS;
2642 MODE_ANY;
2643 }
2644 CONTRACTL_END;
2645 // Identical types are obviously compatible.
2646 if (fromHandle == toHandle)
2647 return TRUE;
2648
2649 // Byref parameters can never be allowed relaxed matching since type safety will always be violated in one
2650 // of the two directions (in or out). Checking one of the types is enough since a byref type is never
2651 // compatible with a non-byref type.
2652 if (fromHandle.IsByRef())
2653 relaxedMatch = FALSE;
2654
2655 // If we allow relaxed matching then any subtype of toHandle is probably
2656 // compatible (definitely so if we know fromHandle is coming from a boxed
2657 // value such as we get from the bound argument in a closed delegate).
2658 if (relaxedMatch && fromHandle.CanCastTo(toHandle))
2659 {
2660 // If the fromHandle isn't boxed then we need to be careful since
2661 // non-object reference arguments aren't going to be compatible with
2662 // object reference locations (there's no implicit boxing going to happen
2663 // for us).
2664 if (!fromHandleIsBoxed)
2665 {
2666 // Check that the "objrefness" of source and destination matches. In
2667 // reality there are only three objref classes that would have
2668 // passed the CanCastTo above given a value type source (Object,
2669 // ValueType and Enum), but why hard code these in when we can be
2670 // more robust?
2671 if (fromHandle.IsGenericVariable())
2672 {
2673 TypeVarTypeDesc *fromHandleVar = fromHandle.AsGenericVariable();
2674
2675 // We need to check whether constraints of fromHandle have been loaded, because the
2676 // CanCastTo operation might have made its decision without enumerating constraints
2677 // (e.g. when toHandle is System.Object).
2678 if (!fromHandleVar->ConstraintsLoaded())
2679 fromHandleVar->LoadConstraints(CLASS_DEPENDENCIES_LOADED);
2680
2681 if (toHandle.IsGenericVariable())
2682 {
2683 TypeVarTypeDesc *toHandleVar = toHandle.AsGenericVariable();
2684
2685 // Constraints of toHandleVar were not touched by CanCastTo.
2686 if (!toHandleVar->ConstraintsLoaded())
2687 toHandleVar->LoadConstraints(CLASS_DEPENDENCIES_LOADED);
2688
2689 // Both handles are type variables. The following table lists all possible combinations.
2690 //
2691 // In brackets are results of IsConstrainedAsObjRef/IsConstrainedAsValueType
2692 //
2693 // To:| [FALSE/FALSE] | [FALSE/TRUE] | [TRUE/FALSE]
2694 // From: | | |
2695 // --------------------------------------------------------------------------------------
2696 // [FALSE/FALSE] | ERROR | NEVER HAPPENS | ERROR
2697 // | we know nothing | | From may be a VT
2698 // --------------------------------------------------------------------------------------
2699 // [FALSE/TRUE] | ERROR | OK | ERROR
2700 // | To may be an ObjRef | both are VT | mismatch
2701 // --------------------------------------------------------------------------------------
2702 // [TRUE/FALSE] | OK (C# compat) | ERROR - mismatch and | OK
2703 // | (*) | no such instantiation | both are ObjRef
2704 // --------------------------------------------------------------------------------------
2705
2706 if (fromHandleVar->ConstrainedAsObjRef())
2707 {
2708 // (*) Normally we would need to check whether toHandleVar is also constrained
2709 // as ObjRef here and fail if it's not. However, the C# compiler currently
2710 // allows the toHandleVar constraint to be omitted and infers it. We have to
2711 // follow the same rule to avoid introducing a breaking change.
2712 //
2713 // Example:
2714 // class Gen<T, U> where T : class, U
2715 //
2716 // For the sake of delegate co(ntra)variance, U is also regarded as being
2717 // constrained as ObjRef even though it has no constraints.
2718
2719 if (toHandleVar->ConstrainedAsValueType())
2720 {
2721 // reference type / value type mismatch
2722 return FALSE;
2723 }
2724 }
2725 else
2726 {
2727 if (toHandleVar->ConstrainedAsValueType())
2728 {
2729 // If toHandleVar is constrained as value type, fromHandle must be as well.
2730 _ASSERTE(fromHandleVar->ConstrainedAsValueType());
2731 }
2732 else
2733 {
2734 // It was not possible to prove that the variables are both reference types
2735 // or both value types.
2736 return FALSE;
2737 }
2738 }
2739 }
2740 else
2741 {
2742 // We need toHandle to be an ObjRef and fromHandle to be constrained as ObjRef,
2743 // or toHandle to be a value type and fromHandle to be constrained as a value
2744 // type (which must be this specific value type actually as value types are sealed).
2745
2746 // Constraints of fromHandle must ensure that it will be ObjRef if toHandle is an
2747 // ObjRef, and a value type if toHandle is not an ObjRef.
2748 if (CorTypeInfo::IsObjRef_NoThrow(toHandle.GetInternalCorElementType()))
2749 {
2750 if (!fromHandleVar->ConstrainedAsObjRef())
2751 return FALSE;
2752 }
2753 else
2754 {
2755 if (!fromHandleVar->ConstrainedAsValueType())
2756 return FALSE;
2757 }
2758 }
2759 }
2760 else
2761 {
2762 _ASSERTE(!toHandle.IsGenericVariable());
2763
2764 // The COR element types have all the information we need.
2765 if (CorTypeInfo::IsObjRef_NoThrow(fromHandle.GetInternalCorElementType()) !=
2766 CorTypeInfo::IsObjRef_NoThrow(toHandle.GetInternalCorElementType()))
2767 return FALSE;
2768 }
2769 }
2770
2771 return TRUE;
2772 }
2773 else
2774 {
2775 // they are not compatible yet enums can go into each other if their underlying element type is the same
2776 if (toHandle.GetVerifierCorElementType() == fromHandle.GetVerifierCorElementType()
2777 && (toHandle.IsEnum() || fromHandle.IsEnum()))
2778 return TRUE;
2779
2780 }
2781
2782 return FALSE;
2783}
2784
2785MethodDesc* COMDelegate::FindDelegateInvokeMethod(MethodTable *pMT)
2786{
2787 CONTRACTL
2788 {
2789 THROWS;
2790 GC_NOTRIGGER;
2791 MODE_ANY;
2792 }
2793 CONTRACTL_END;
2794
2795 _ASSERTE(pMT->IsDelegate());
2796
2797 MethodDesc * pMD = ((DelegateEEClass*)pMT->GetClass())->GetInvokeMethod();
2798 if (pMD == NULL)
2799 COMPlusThrowNonLocalized(kMissingMethodException, W("Invoke"));
2800 return pMD;
2801}
2802
2803BOOL COMDelegate::IsDelegateInvokeMethod(MethodDesc *pMD)
2804{
2805 LIMITED_METHOD_CONTRACT;
2806
2807 MethodTable *pMT = pMD->GetMethodTable();
2808 _ASSERTE(pMT->IsDelegate());
2809
2810 return (pMD == ((DelegateEEClass *)pMT->GetClass())->GetInvokeMethod());
2811}
2812
2813BOOL COMDelegate::IsMethodDescCompatible(TypeHandle thFirstArg,
2814 TypeHandle thExactMethodType,
2815 MethodDesc *pTargetMethod,
2816 TypeHandle thDelegate,
2817 MethodDesc *pInvokeMethod,
2818 int flags,
2819 BOOL *pfIsOpenDelegate)
2820{
2821 CONTRACTL
2822 {
2823 THROWS;
2824 GC_TRIGGERS;
2825 MODE_ANY;
2826 }
2827 CONTRACTL_END;
2828
2829 // Handle easy cases first -- if there's a constraint on whether the target method is static or instance we can check that very
2830 // quickly.
2831 if (flags & DBF_StaticMethodOnly && !pTargetMethod->IsStatic())
2832 return FALSE;
2833 if (flags & DBF_InstanceMethodOnly && pTargetMethod->IsStatic())
2834 return FALSE;
2835
2836 // we don't allow you to bind to methods on Nullable<T> because the unboxing stubs don't know how to
2837 // handle this case.
2838 if (!pTargetMethod->IsStatic() && Nullable::IsNullableType(pTargetMethod->GetMethodTable()))
2839 return FALSE;
2840
2841 // Have to be careful with automatically generated array methods (Get, Set, etc.). The TypeHandle here may actually be one
2842 // of the "special case" MethodTables (such as Object[]) instead of an ArrayTypeDesc and our TypeHandle CanCastTo code can't
2843 // cope with all the different possible combinations. In general we want to normalize the TypeHandle into an ArrayTypeDesc
2844 // for these cases.
2845 if (thExactMethodType.IsArrayType() && !thExactMethodType.IsArray())
2846 {
2847 TypeHandle thElement = thExactMethodType.AsMethodTable()->GetApproxArrayElementTypeHandle();
2848 CorElementType etElement = thExactMethodType.AsMethodTable()->GetInternalCorElementType();
2849 unsigned uRank = thExactMethodType.AsMethodTable()->GetRank();
2850
2851 thExactMethodType = ClassLoader::LoadArrayTypeThrowing(thElement,
2852 etElement,
2853 uRank,
2854 ClassLoader::DontLoadTypes);
2855 }
2856
2857 // Get signatures for the delegate invoke and target methods.
2858 MetaSig sigInvoke(pInvokeMethod, thDelegate);
2859 MetaSig sigTarget(pTargetMethod, thExactMethodType);
2860
2861 // Check that there is no vararg mismatch.
2862 if (sigInvoke.IsVarArg() != sigTarget.IsVarArg())
2863 return FALSE;
2864
2865 // The relationship between the number of arguments on the delegate invoke and target methods tells us a lot about the type of
2866 // delegate we'll create (open or closed over the first argument). We're getting the fixed argument counts here, which are all
2867 // the arguments apart from any implicit 'this' pointers.
2868 // On the delegate invoke side (the caller) the total number of arguments is the number of fixed args to Invoke plus one if the
2869 // delegate is closed over an argument (i.e. that argument is provided at delegate creation time).
2870 // On the target method side (the callee) the total number of arguments is the number of fixed args plus one if the target is an
2871 // instance method.
2872 // These two totals should match for any compatible delegate and target method.
2873 UINT numFixedInvokeArgs = sigInvoke.NumFixedArgs();
2874 UINT numFixedTargetArgs = sigTarget.NumFixedArgs();
2875 UINT numTotalTargetArgs = numFixedTargetArgs + (pTargetMethod->IsStatic() ? 0 : 1);
2876
2877 // Determine whether the match (if it is otherwise compatible) would result in an open or closed delegate or is just completely
2878 // out of whack.
2879 BOOL fIsOpenDelegate;
2880 if (numTotalTargetArgs == numFixedInvokeArgs)
2881 // All arguments provided by invoke, delegate must be open.
2882 fIsOpenDelegate = TRUE;
2883 else if (numTotalTargetArgs == numFixedInvokeArgs + 1)
2884 // One too few arguments provided by invoke, delegate must be closed.
2885 fIsOpenDelegate = FALSE;
2886 else
2887 // Target method cannot possibly match the invoke method.
2888 return FALSE;
2889
2890 // Deal with cases where the caller wants a specific type of delegate.
2891 if (flags & DBF_OpenDelegateOnly && !fIsOpenDelegate)
2892 return FALSE;
2893 if (flags & DBF_ClosedDelegateOnly && fIsOpenDelegate)
2894 return FALSE;
2895
2896 // If the target (or first argument) is null, the delegate type would be closed and the caller explicitly doesn't want to allow
2897 // closing over null then filter that case now.
2898 if (flags & DBF_NeverCloseOverNull && thFirstArg.IsNull() && !fIsOpenDelegate)
2899 return FALSE;
2900
2901 // If, on the other hand, we're looking at an open delegate but the caller has provided a target it's also not a match.
2902 if (fIsOpenDelegate && !thFirstArg.IsNull())
2903 return FALSE;
2904
2905 // **********OLD COMMENT**********
2906 // We don't allow open delegates over virtual value type methods. That's because we currently have no way to allow the first
2907 // argument of the invoke method to be specified in such a way that the passed value would be both compatible with the target
2908 // method and type safe. Virtual methods always have an objref instance (they depend on this for the vtable lookup algorithm) so
2909 // we can't take a Foo& first argument like other value type methods. We also can't accept System.Object or System.ValueType in
2910 // the invoke signature since that's not specific enough and would allow type safety violations.
2911 // Someday we may invent a boxing stub which would take a Foo& passed in box it before dispatch. This is unlikely given that
2912 // it's a lot of work for an edge case (especially considering that open delegates over value types are always going to be
2913 // tightly bound to the specific value type). It would also be an odd case where merely calling a delegate would involve an
2914 // allocation and thus potential failure before you even entered the method.
2915 // So for now we simply disallow this case.
2916 // **********OLD COMMENT END**********
2917 // Actually we allow them now. We will treat them like non-virtual methods.
2918
2919
2920 // If we get here the basic shape of the signatures match up for either an open or closed delegate. Now we need to verify that
2921 // those signatures are type compatible. This is complicated somewhat by the matrix of delegate type to target method types
2922 // (open static vs closed instance etc.). Where we get the first argument type on the invoke side is controlled by open vs
2923 // closed: closed delegates get the type from the target, open from the first invoke method argument (which is always a fixed
2924 // arg). Similarly the location of the first argument type on the target method side is based on static vs instance (static from
2925 // the first fixed arg, instance from the type of the method).
2926
2927 TypeHandle thFirstInvokeArg;
2928 TypeHandle thFirstTargetArg;
2929
2930 // There is one edge case for an open static delegate which takes no arguments. In that case we're nearly done, just compare the
2931 // return types.
2932 if (numTotalTargetArgs == 0)
2933 {
2934 _ASSERTE(pTargetMethod->IsStatic());
2935 _ASSERTE(fIsOpenDelegate);
2936
2937 goto CheckReturnType;
2938 }
2939
2940 // Invoke side first...
2941 if (fIsOpenDelegate)
2942 {
2943 // No bound arguments, take first type from invoke signature.
2944 if (sigInvoke.NextArgNormalized() == ELEMENT_TYPE_END)
2945 return FALSE;
2946 thFirstInvokeArg = sigInvoke.GetLastTypeHandleThrowing();
2947 }
2948 else
2949 // We have one bound argument and the type of that is what we must compare first.
2950 thFirstInvokeArg = thFirstArg;
2951
2952 // And now the first target method argument for comparison...
2953 if (pTargetMethod->IsStatic())
2954 {
2955 // The first argument for a static method is the first fixed arg.
2956 if (sigTarget.NextArgNormalized() == ELEMENT_TYPE_END)
2957 return FALSE;
2958 thFirstTargetArg = sigTarget.GetLastTypeHandleThrowing();
2959
2960 // Delegates closed over static methods have a further constraint: the first argument of the target must be an object
2961 // reference type (otherwise the argument shuffling logic could get complicated).
2962 if (!fIsOpenDelegate)
2963 {
2964 if (thFirstTargetArg.IsGenericVariable())
2965 {
2966 // If the first argument of the target is a generic variable, it must be constrained to be an object reference.
2967 TypeVarTypeDesc *varFirstTargetArg = thFirstTargetArg.AsGenericVariable();
2968 if (!varFirstTargetArg->ConstrainedAsObjRef())
2969 return FALSE;
2970 }
2971 else
2972 {
2973 // Otherwise the code:CorElementType of the argument must be classified as an object reference.
2974 CorElementType etFirstTargetArg = thFirstTargetArg.GetInternalCorElementType();
2975 if (!CorTypeInfo::IsObjRef(etFirstTargetArg))
2976 return FALSE;
2977 }
2978 }
2979 }
2980 else
2981 {
2982 // The type of the first argument to an instance method is from the method type.
2983 thFirstTargetArg = thExactMethodType;
2984
2985 // If the delegate is open and the target method is on a value type or primitive then the first argument of the invoke
2986 // method must be a reference to that type. So make promote the type we got from the reference to a ref. (We don't need to
2987 // do this for the closed instance case because there we got the invocation side type from the first arg passed in, i.e.
2988 // it's had the ref stripped from it implicitly).
2989 if (fIsOpenDelegate)
2990 {
2991 CorElementType etFirstTargetArg = thFirstTargetArg.GetInternalCorElementType();
2992 if (etFirstTargetArg <= ELEMENT_TYPE_R8 ||
2993 etFirstTargetArg == ELEMENT_TYPE_VALUETYPE ||
2994 etFirstTargetArg == ELEMENT_TYPE_I ||
2995 etFirstTargetArg == ELEMENT_TYPE_U)
2996 thFirstTargetArg = thFirstTargetArg.MakeByRef();
2997 }
2998 }
2999
3000 // Now we have enough data to compare the first arguments on the invoke and target side. Skip this if we are closed over null
3001 // (we don't have enough type information for the match but it doesn't matter because the null matches all object reference
3002 // types, which our first arg must be in this case). We always relax signature matching for the first argument of an instance
3003 // method, since it's always allowable to call the method on a more derived type. In cases where we're closed over the first
3004 // argument we know that argument is boxed (because it was passed to us as an object). We provide this information to
3005 // IsLocationAssignable because it relaxes signature matching for some important cases (e.g. passing a value type to an argument
3006 // typed as Object).
3007 if (!thFirstInvokeArg.IsNull())
3008 if (!IsLocationAssignable(thFirstInvokeArg,
3009 thFirstTargetArg,
3010 !pTargetMethod->IsStatic() || flags & DBF_RelaxedSignature,
3011 !fIsOpenDelegate))
3012 return FALSE;
3013
3014 // Loop over the remaining fixed args, the list should be one to one at this point.
3015 while (TRUE)
3016 {
3017 CorElementType etInvokeArg = sigInvoke.NextArgNormalized();
3018 CorElementType etTargetArg = sigTarget.NextArgNormalized();
3019 if (etInvokeArg == ELEMENT_TYPE_END || etTargetArg == ELEMENT_TYPE_END)
3020 {
3021 // We've reached the end of one signature. We better be at the end of the other or it's not a match.
3022 if (etInvokeArg != etTargetArg)
3023 return FALSE;
3024 break;
3025 }
3026 else
3027 {
3028 TypeHandle thInvokeArg = sigInvoke.GetLastTypeHandleThrowing();
3029 TypeHandle thTargetArg = sigTarget.GetLastTypeHandleThrowing();
3030
3031 if (!IsLocationAssignable(thInvokeArg, thTargetArg, flags & DBF_RelaxedSignature, FALSE))
3032 return FALSE;
3033 }
3034 }
3035
3036 CheckReturnType:
3037
3038 // Almost there, just compare the return types (remember that the assignment is in the other direction here, from callee to
3039 // caller, so switch the order of the arguments to IsLocationAssignable).
3040 // If we ever relax this we have to think about how to unbox this arg in the Nullable<T> case also.
3041 if (!IsLocationAssignable(sigTarget.GetRetTypeHandleThrowing(),
3042 sigInvoke.GetRetTypeHandleThrowing(),
3043 flags & DBF_RelaxedSignature,
3044 FALSE))
3045 return FALSE;
3046
3047 // We must have a match.
3048 if (pfIsOpenDelegate)
3049 *pfIsOpenDelegate = fIsOpenDelegate;
3050 return TRUE;
3051}
3052
3053MethodDesc* COMDelegate::GetDelegateCtor(TypeHandle delegateType, MethodDesc *pTargetMethod, DelegateCtorArgs *pCtorData)
3054{
3055 CONTRACTL
3056 {
3057 THROWS;
3058 GC_TRIGGERS;
3059 MODE_ANY;
3060 }
3061 CONTRACTL_END;
3062
3063 MethodDesc *pRealCtor = NULL;
3064
3065 MethodTable *pDelMT = delegateType.AsMethodTable();
3066 DelegateEEClass *pDelCls = (DelegateEEClass*)(pDelMT->GetClass());
3067
3068 MethodDesc *pDelegateInvoke = COMDelegate::FindDelegateInvokeMethod(pDelMT);
3069
3070 MetaSig invokeSig(pDelegateInvoke);
3071 MetaSig methodSig(pTargetMethod);
3072 UINT invokeArgCount = invokeSig.NumFixedArgs();
3073 UINT methodArgCount = methodSig.NumFixedArgs();
3074 BOOL isStatic = pTargetMethod->IsStatic();
3075 LoaderAllocator *pTargetMethodLoaderAllocator = pTargetMethod->GetLoaderAllocator();
3076 BOOL isCollectible = pTargetMethodLoaderAllocator->IsCollectible();
3077 // A method that may be instantiated over a collectible type, and is static will require a delegate
3078 // that has the _methodBase field filled in with the LoaderAllocator of the collectible assembly
3079 // associated with the instantiation.
3080 BOOL fMaybeCollectibleAndStatic = FALSE;
3081
3082 // Do not allow static methods with [NativeCallableAttribute] to be a delegate target.
3083 // A native callable method is special and allowing it to be delegate target will destabilize the runtime.
3084 if (pTargetMethod->HasNativeCallableAttribute())
3085 {
3086 COMPlusThrow(kNotSupportedException, W("NotSupported_NativeCallableTarget"));
3087 }
3088
3089 if (isStatic)
3090 {
3091 // When this method is called and the method being considered is shared, we typically
3092 // are passed a Wrapper method for the explicit canonical instantiation. It would be illegal
3093 // to actually call that method, but the jit uses it as a proxy for the real instantiated
3094 // method, so we can't make the methoddesc apis that report that it is the shared methoddesc
3095 // report that it is. Hence, this collection of checks that will detect if the methoddesc
3096 // being used is a normal method desc to shared code, or if it is a wrapped methoddesc
3097 // corresponding to the actually uncallable instantiation over __Canon.
3098 if (pTargetMethod->GetMethodTable()->IsSharedByGenericInstantiations())
3099 {
3100 fMaybeCollectibleAndStatic = TRUE;
3101 }
3102 else if (pTargetMethod->IsSharedByGenericMethodInstantiations())
3103 {
3104 fMaybeCollectibleAndStatic = TRUE;
3105 }
3106 else if (pTargetMethod->HasMethodInstantiation())
3107 {
3108 Instantiation instantiation = pTargetMethod->GetMethodInstantiation();
3109 for (DWORD iParam = 0; iParam < instantiation.GetNumArgs(); iParam++)
3110 {
3111 if (instantiation[iParam] == g_pCanonMethodTableClass)
3112 {
3113 fMaybeCollectibleAndStatic = TRUE;
3114 break;
3115 }
3116 }
3117 }
3118 }
3119
3120 // If this might be collectible and is static, then we will go down the slow path. Implementing
3121 // yet another fast path would require a methoddesc parameter, but hopefully isn't necessary.
3122 if (fMaybeCollectibleAndStatic)
3123 return NULL;
3124
3125 if (!isStatic)
3126 methodArgCount++; // count 'this'
3127 MethodDesc *pCallerMethod = (MethodDesc*)pCtorData->pMethod;
3128
3129 if (NeedsWrapperDelegate(pTargetMethod))
3130 {
3131 // If we need a wrapper even it is not a secure delegate, go through slow path
3132 return NULL;
3133 }
3134
3135 // Force the slow path for nullable so that we can give the user an error in case were the verifier is not run.
3136 MethodTable* pMT = pTargetMethod->GetMethodTable();
3137 if (!pTargetMethod->IsStatic() && Nullable::IsNullableType(pMT))
3138 return NULL;
3139
3140#ifdef FEATURE_COMINTEROP
3141 // We'll always force classic COM types to go down the slow path for security checks.
3142 if ((pMT->IsComObjectType() && !pMT->IsWinRTObjectType()) ||
3143 (pMT->IsComImport() && !pMT->IsProjectedFromWinRT()))
3144 {
3145 return NULL;
3146 }
3147#endif
3148
3149 // DELEGATE KINDS TABLE
3150 //
3151 // _target _methodPtr _methodPtrAux _invocationList _invocationCount
3152 //
3153 // 1- Instance closed 'this' ptr target method null null 0
3154 // 2- Instance open non-virt delegate shuffle thunk target method null 0
3155 // 3- Instance open virtual delegate Virtual-stub dispatch method id null 0
3156 // 4- Static closed first arg target method null null 0
3157 // 5- Static closed (special sig) delegate specialSig thunk target method first arg 0
3158 // 6- Static opened delegate shuffle thunk target method null 0
3159 // 7- Secure delegate call thunk MethodDesc (frame) target delegate creator assembly
3160 //
3161 // Delegate invoke arg count == target method arg count - 2, 3, 6
3162 // Delegate invoke arg count == 1 + target method arg count - 1, 4, 5
3163 //
3164 // 1, 4 - MulticastDelegate.ctor1 (simply assign _target and _methodPtr)
3165 // 5 - MulticastDelegate.ctor2 (see table, takes 3 args)
3166 // 2, 6 - MulticastDelegate.ctor3 (take shuffle thunk)
3167 // 3 - MulticastDelegate.ctor4 (take shuffle thunk, retrieve MethodDesc) ???
3168 //
3169 // 7 - Needs special handling
3170 //
3171 // With collectible types, we need to fill the _methodBase field in with a value that represents the LoaderAllocator of the target method
3172 // if the delegate is not a closed instance delegate.
3173 //
3174 // There are two techniques that will work for this.
3175 // One is to simply use the slow path. We use this for unusual constructs. It is rather slow.
3176 // We will use this for the secure variants
3177 //
3178 // Another is to pass a gchandle to the delegate ctor. This is fastest, but only works if we can predict the gc handle at this time.
3179 // We will use this for the non secure variants
3180
3181 if (invokeArgCount == methodArgCount)
3182 {
3183 // case 2, 3, 6
3184 //@TODO:NEWVTWORK: Might need changing.
3185 // The virtual dispatch stub doesn't work on unboxed value type objects which don't have MT pointers.
3186 // Since open virtual (delegate kind 3) delegates on value type methods require unboxed objects we cannot use the
3187 // virtual dispatch stub for them. On the other hand, virtual methods on value types don't need
3188 // to be dispatched because value types cannot be derived. So we treat them like non-virtual methods (delegate kind 2).
3189 if (!isStatic && pTargetMethod->IsVirtual() && !pTargetMethod->GetMethodTable()->IsValueType())
3190 {
3191 // case 3
3192 if (isCollectible)
3193 pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_COLLECTIBLE_VIRTUAL_DISPATCH);
3194 else
3195 pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_VIRTUAL_DISPATCH);
3196 }
3197 else
3198 {
3199 // case 2, 6
3200 if (isCollectible)
3201 pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_COLLECTIBLE_OPENED);
3202 else
3203 pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_OPENED);
3204 }
3205 Stub *pShuffleThunk = NULL;
3206 if (!pTargetMethod->IsStatic() && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
3207 pShuffleThunk = pDelCls->m_pInstRetBuffCallStub;
3208 else
3209 pShuffleThunk = pDelCls->m_pStaticCallStub;
3210
3211 if (!pShuffleThunk)
3212 pShuffleThunk = SetupShuffleThunk(pDelMT, pTargetMethod);
3213 pCtorData->pArg3 = (void*)pShuffleThunk->GetEntryPoint();
3214 if (isCollectible)
3215 {
3216 pCtorData->pArg4 = pTargetMethodLoaderAllocator->GetLoaderAllocatorObjectHandle();
3217 }
3218 }
3219 else
3220 {
3221 // case 1, 4, 5
3222 //TODO: need to differentiate on 5
3223 _ASSERTE(invokeArgCount + 1 == methodArgCount);
3224
3225#ifdef HAS_THISPTR_RETBUF_PRECODE
3226 // Force closed delegates over static methods with return buffer to go via
3227 // the slow path to create ThisPtrRetBufPrecode
3228 if (isStatic && pTargetMethod->HasRetBuffArg() && IsRetBuffPassedAsFirstArg())
3229 return NULL;
3230#endif
3231
3232 // under the conditions below the delegate ctor needs to perform some heavy operation
3233 // to either resolve the interface call to the real target or to get the unboxing stub (or both)
3234 BOOL needsRuntimeInfo = !pTargetMethod->IsStatic() &&
3235 (pTargetMethod->IsInterface() ||
3236 (pTargetMethod->GetMethodTable()->IsValueType() && !pTargetMethod->IsUnboxingStub()));
3237
3238 if (needsRuntimeInfo)
3239 pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_RT_CLOSED);
3240 else
3241 {
3242 if (!isStatic)
3243 pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_CLOSED);
3244 else
3245 {
3246 if (isCollectible)
3247 {
3248 pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_COLLECTIBLE_CLOSED_STATIC);
3249 pCtorData->pArg3 = pTargetMethodLoaderAllocator->GetLoaderAllocatorObjectHandle();
3250 }
3251 else
3252 {
3253 pRealCtor = MscorlibBinder::GetMethod(METHOD__MULTICAST_DELEGATE__CTOR_CLOSED_STATIC);
3254 }
3255 }
3256 }
3257 }
3258
3259 return pRealCtor;
3260}
3261
3262
3263/*@GENERICSVER: new (works for generics too)
3264 Does a static validation of parameters passed into a delegate constructor.
3265
3266
3267 For "new Delegate(obj.method)" where method is statically typed as "C::m" and
3268 the static type of obj is D (some subclass of C)...
3269
3270 Params:
3271 instHnd : Static type of the instance, from which pFtn is obtained. Ignored if pFtn
3272 is static (i.e. D)
3273 ftnParentHnd: Parent of the MethodDesc, pFtn, used to create the delegate (i.e. type C)
3274 pFtn : (possibly shared) MethodDesc of the function pointer used to create the delegate (i.e. C::m)
3275 pDlgt : The delegate type (i.e. Delegate)
3276 module: The module scoping methodMemberRef and delegateConstructorMemberRef
3277 methodMemberRef: the MemberRef, MemberDef or MemberSpec of the target method (i.e. a mdToken for C::m)
3278 delegateConstructorMemberRef: the MemberRef, MemberDef or MemberSpec of the delegate constructor (i.e. a mdToken for Delegate::.ctor)
3279
3280 Validates the following conditions:
3281 1. If the function (pFtn) is not static, pInst should be equal to the type where
3282 pFtn is defined or pInst should be a parent of pFtn's type.
3283 2. The signature of the function should be compatible with the signature
3284 of the Invoke method of the delegate type.
3285 The signature is retrieved from module, methodMemberRef and delegateConstructorMemberRef
3286
3287 NB: Although some of these arguments are redundant, we pass them in to avoid looking up
3288 information that should already be available.
3289 Instead of comparing type handles modulo some context, the method directly compares metadata to avoid
3290 loading classes referenced in the method signatures (hence the need for the module and member refs).
3291 Also, because this method works directly on metadata, without allowing any additional instantiation of the
3292 free type variables in the signature of the method or delegate constructor, this code
3293 will *only* verify a constructor application at the typical (ie. formal) instantiation.
3294*/
3295/* static */
3296BOOL COMDelegate::ValidateCtor(TypeHandle instHnd,
3297 TypeHandle ftnParentHnd,
3298 MethodDesc *pFtn,
3299 TypeHandle dlgtHnd,
3300 BOOL *pfIsOpenDelegate)
3301
3302{
3303 CONTRACTL
3304 {
3305 THROWS;
3306 GC_TRIGGERS;
3307 MODE_ANY;
3308
3309 PRECONDITION(CheckPointer(pFtn));
3310 PRECONDITION(!dlgtHnd.IsNull());
3311 PRECONDITION(!ftnParentHnd.IsNull());
3312
3313 INJECT_FAULT(COMPlusThrowOM()); // from MetaSig::CompareElementType
3314 }
3315 CONTRACTL_END;
3316
3317 DelegateEEClass *pdlgEEClass = (DelegateEEClass*)dlgtHnd.AsMethodTable()->GetClass();
3318 PREFIX_ASSUME(pdlgEEClass != NULL);
3319 MethodDesc *pDlgtInvoke = pdlgEEClass->GetInvokeMethod();
3320 if (pDlgtInvoke == NULL)
3321 return FALSE;
3322 return IsMethodDescCompatible(instHnd, ftnParentHnd, pFtn, dlgtHnd, pDlgtInvoke, DBF_RelaxedSignature, pfIsOpenDelegate);
3323}
3324
3325BOOL COMDelegate::ValidateBeginInvoke(DelegateEEClass* pClass)
3326{
3327 CONTRACTL
3328 {
3329 THROWS;
3330 GC_TRIGGERS;
3331 MODE_ANY;
3332
3333 PRECONDITION(CheckPointer(pClass));
3334 PRECONDITION(CheckPointer(pClass->GetBeginInvokeMethod()));
3335
3336 // insert fault. Can the binder throw an OOM?
3337 }
3338 CONTRACTL_END;
3339
3340 if (pClass->GetInvokeMethod() == NULL)
3341 return FALSE;
3342
3343 // We check the signatures under the typical instantiation of the possibly generic class
3344 MetaSig beginInvokeSig(pClass->GetBeginInvokeMethod()->LoadTypicalMethodDefinition());
3345 MetaSig invokeSig(pClass->GetInvokeMethod()->LoadTypicalMethodDefinition());
3346
3347 if (beginInvokeSig.GetCallingConventionInfo() != (IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_DEFAULT))
3348 return FALSE;
3349
3350 if (beginInvokeSig.NumFixedArgs() != invokeSig.NumFixedArgs() + 2)
3351 return FALSE;
3352
3353 if (beginInvokeSig.GetRetTypeHandleThrowing() != TypeHandle(MscorlibBinder::GetClass(CLASS__IASYNCRESULT)))
3354 return FALSE;
3355
3356 while(invokeSig.NextArg() != ELEMENT_TYPE_END)
3357 {
3358 beginInvokeSig.NextArg();
3359 if (beginInvokeSig.GetLastTypeHandleThrowing() != invokeSig.GetLastTypeHandleThrowing())
3360 return FALSE;
3361 }
3362
3363 beginInvokeSig.NextArg();
3364 if (beginInvokeSig.GetLastTypeHandleThrowing()!= TypeHandle(MscorlibBinder::GetClass(CLASS__ASYNCCALLBACK)))
3365 return FALSE;
3366
3367 beginInvokeSig.NextArg();
3368 if (beginInvokeSig.GetLastTypeHandleThrowing()!= TypeHandle(g_pObjectClass))
3369 return FALSE;
3370
3371 if (beginInvokeSig.NextArg() != ELEMENT_TYPE_END)
3372 return FALSE;
3373
3374 return TRUE;
3375}
3376
3377BOOL COMDelegate::ValidateEndInvoke(DelegateEEClass* pClass)
3378{
3379 CONTRACTL
3380 {
3381 THROWS;
3382 GC_TRIGGERS;
3383 MODE_ANY;
3384
3385 PRECONDITION(CheckPointer(pClass));
3386 PRECONDITION(CheckPointer(pClass->GetEndInvokeMethod()));
3387
3388 // insert fault. Can the binder throw an OOM?
3389 }
3390 CONTRACTL_END;
3391
3392 if (pClass->GetInvokeMethod() == NULL)
3393 return FALSE;
3394
3395 // We check the signatures under the typical instantiation of the possibly generic class
3396 MetaSig endInvokeSig(pClass->GetEndInvokeMethod()->LoadTypicalMethodDefinition());
3397 MetaSig invokeSig(pClass->GetInvokeMethod()->LoadTypicalMethodDefinition());
3398
3399 if (endInvokeSig.GetCallingConventionInfo() != (IMAGE_CEE_CS_CALLCONV_HASTHIS | IMAGE_CEE_CS_CALLCONV_DEFAULT))
3400 return FALSE;
3401
3402 if (endInvokeSig.GetRetTypeHandleThrowing() != invokeSig.GetRetTypeHandleThrowing())
3403 return FALSE;
3404
3405 CorElementType type;
3406 while((type = invokeSig.NextArg()) != ELEMENT_TYPE_END)
3407 {
3408 if (type == ELEMENT_TYPE_BYREF)
3409 {
3410 endInvokeSig.NextArg();
3411 if (endInvokeSig.GetLastTypeHandleThrowing() != invokeSig.GetLastTypeHandleThrowing())
3412 return FALSE;
3413 }
3414 }
3415
3416 if (endInvokeSig.NextArg() == ELEMENT_TYPE_END)
3417 return FALSE;
3418
3419 if (endInvokeSig.GetLastTypeHandleThrowing() != TypeHandle(MscorlibBinder::GetClass(CLASS__IASYNCRESULT)))
3420 return FALSE;
3421
3422 if (endInvokeSig.NextArg() != ELEMENT_TYPE_END)
3423 return FALSE;
3424
3425 return TRUE;
3426}
3427
3428BOOL COMDelegate::IsSecureDelegate(DELEGATEREF dRef)
3429{
3430 CONTRACTL
3431 {
3432 MODE_ANY;
3433 NOTHROW;
3434 GC_NOTRIGGER;
3435 SO_TOLERANT;
3436 }
3437 CONTRACTL_END;
3438 DELEGATEREF innerDel = NULL;
3439 if (dRef->GetInvocationCount() != 0)
3440 {
3441 innerDel = (DELEGATEREF) dRef->GetInvocationList();
3442 if (innerDel != NULL && innerDel->GetMethodTable()->IsDelegate())
3443 {
3444 // We have a secure delegate
3445 return TRUE;
3446 }
3447 }
3448 return FALSE;
3449}
3450
3451#endif // !DACCESS_COMPILE
3452
3453
3454// Decides if pcls derives from Delegate.
3455BOOL COMDelegate::IsDelegate(MethodTable *pMT)
3456{
3457 WRAPPER_NO_CONTRACT;
3458 return (pMT == g_pDelegateClass) || (pMT == g_pMulticastDelegateClass) || pMT->IsDelegate();
3459}
3460
3461
3462#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
3463
3464
3465// Helper to construct an UnhandledExceptionEventArgs. This may fail for out-of-memory or
3466// other reasons. Currently, we fall back on passing a NULL eventargs to the event sink.
3467// Another possibility is to have two shared immutable instances (one for isTerminating and
3468// another for !isTerminating). These must be immutable because we perform no synchronization
3469// around delivery of unhandled exceptions. They occur in a free-threaded manner on various
3470// threads.
3471//
3472// It doesn't add much value to communicate the isTerminating flag under these unusual
3473// conditions.
3474static void TryConstructUnhandledExceptionArgs(OBJECTREF *pThrowable,
3475 BOOL isTerminating,
3476 OBJECTREF *pOutEventArgs)
3477{
3478 CONTRACTL
3479 {
3480 NOTHROW;
3481 GC_TRIGGERS;
3482 MODE_COOPERATIVE;
3483 }
3484 CONTRACTL_END;
3485
3486 _ASSERTE(pThrowable != NULL && IsProtectedByGCFrame(pThrowable));
3487 _ASSERTE(pOutEventArgs != NULL && IsProtectedByGCFrame(pOutEventArgs));
3488 _ASSERTE(*pOutEventArgs == NULL);
3489
3490 EX_TRY
3491 {
3492 MethodTable *pMT = MscorlibBinder::GetClass(CLASS__UNHANDLED_EVENTARGS);
3493 *pOutEventArgs = AllocateObject(pMT);
3494
3495 MethodDescCallSite ctor(METHOD__UNHANDLED_EVENTARGS__CTOR, pOutEventArgs);
3496
3497 ARG_SLOT args[] =
3498 {
3499 ObjToArgSlot(*pOutEventArgs),
3500 ObjToArgSlot(*pThrowable),
3501 BoolToArgSlot(isTerminating)
3502 };
3503
3504 ctor.Call(args);
3505 }
3506 EX_CATCH
3507 {
3508 *pOutEventArgs = NULL; // arguably better than half-constructed object
3509
3510 // It's not even worth asserting, because these aren't our bugs. At
3511 // some point, a MDA may be warranted.
3512 }
3513 EX_END_CATCH(SwallowAllExceptions)
3514}
3515
3516
3517// Helper to dispatch a single unhandled exception notification, swallowing anything
3518// that goes wrong.
3519static void InvokeUnhandledSwallowing(OBJECTREF *pDelegate,
3520 OBJECTREF *pDomain,
3521 OBJECTREF *pEventArgs)
3522{
3523 CONTRACTL
3524 {
3525 NOTHROW;
3526 GC_TRIGGERS;
3527 MODE_COOPERATIVE;
3528 }
3529 CONTRACTL_END;
3530
3531 _ASSERTE(pDelegate != NULL && IsProtectedByGCFrame(pDelegate));
3532 _ASSERTE(pDomain != NULL && IsProtectedByGCFrame(pDomain));
3533 _ASSERTE(pEventArgs == NULL || IsProtectedByGCFrame(pEventArgs));
3534
3535 EX_TRY
3536 {
3537#if defined(FEATURE_CORRUPTING_EXCEPTIONS)
3538 BOOL fCanMethodHandleException = g_pConfig->LegacyCorruptedStateExceptionsPolicy();
3539 if (!fCanMethodHandleException)
3540 {
3541 // CSE policy has not been overridden - proceed with our checks.
3542 //
3543 // Notifications for CSE are only delivered if the delegate target follows CSE rules.
3544 // So, get the corruption severity of the active exception that has gone unhandled.
3545 //
3546 // By Default, assume that the active exception is not corrupting.
3547 CorruptionSeverity severity = NotCorrupting;
3548 Thread *pCurThread = GetThread();
3549 _ASSERTE(pCurThread != NULL);
3550 ThreadExceptionState *pExState = pCurThread->GetExceptionState();
3551 if (pExState->IsExceptionInProgress())
3552 {
3553 // If an exception is active, it implies we have a tracker for it.
3554 // Hence, get the corruption severity from the active exception tracker.
3555 severity = pExState->GetCurrentExceptionTracker()->GetCorruptionSeverity();
3556 _ASSERTE(severity > NotSet);
3557 }
3558
3559 // Notifications are delivered based upon corruption severity of the exception
3560 fCanMethodHandleException = ExceptionNotifications::CanDelegateBeInvokedForException(pDelegate, severity);
3561 if (!fCanMethodHandleException)
3562 {
3563 LOG((LF_EH, LL_INFO100, "InvokeUnhandledSwallowing: ADUEN Delegate cannot be invoked for corruption severity %d\n",
3564 severity));
3565 }
3566 }
3567
3568 if (fCanMethodHandleException)
3569#endif // defined(FEATURE_CORRUPTING_EXCEPTIONS)
3570 {
3571 // We've already exercised the prestub on this delegate's COMDelegate::GetMethodDesc,
3572 // as part of wiring up a reliable event sink. Deliver the notification.
3573 ExceptionNotifications::DeliverExceptionNotification(UnhandledExceptionHandler, pDelegate, pDomain, pEventArgs);
3574 }
3575 }
3576 EX_CATCH
3577 {
3578 // It's not even worth asserting, because these aren't our bugs. At
3579 // some point, a MDA may be warranted.
3580 }
3581 EX_END_CATCH(SwallowAllExceptions)
3582}
3583
3584// The unhandled exception event is a little easier to distribute, because
3585// we simply swallow any failures and proceed to the next event sink.
3586void DistributeUnhandledExceptionReliably(OBJECTREF *pDelegate,
3587 OBJECTREF *pDomain,
3588 OBJECTREF *pThrowable,
3589 BOOL isTerminating)
3590{
3591 CONTRACTL
3592 {
3593 NOTHROW;
3594 GC_TRIGGERS;
3595 MODE_COOPERATIVE;
3596 }
3597 CONTRACTL_END;
3598
3599 _ASSERTE(pDelegate != NULL && IsProtectedByGCFrame(pDelegate));
3600 _ASSERTE(pDomain != NULL && IsProtectedByGCFrame(pDomain));
3601 _ASSERTE(pThrowable != NULL && IsProtectedByGCFrame(pThrowable));
3602
3603 EX_TRY
3604 {
3605 struct _gc
3606 {
3607 PTRARRAYREF Array;
3608 OBJECTREF InnerDelegate;
3609 OBJECTREF EventArgs;
3610 } gc;
3611 ZeroMemory(&gc, sizeof(gc));
3612
3613 GCPROTECT_BEGIN(gc);
3614
3615 // Try to construct an UnhandledExceptionEventArgs out of pThrowable & isTerminating.
3616 // If unsuccessful, the best we can do is pass NULL.
3617 TryConstructUnhandledExceptionArgs(pThrowable, isTerminating, &gc.EventArgs);
3618
3619 gc.Array = (PTRARRAYREF) ((DELEGATEREF)(*pDelegate))->GetInvocationList();
3620 if (gc.Array == NULL || !gc.Array->GetMethodTable()->IsArray())
3621 {
3622 InvokeUnhandledSwallowing(pDelegate, pDomain, &gc.EventArgs);
3623 }
3624 else
3625 {
3626 // The _invocationCount could be less than the array size, if we are sharing
3627 // immutable arrays cleverly.
3628 INT_PTR invocationCount = ((DELEGATEREF)(*pDelegate))->GetInvocationCount();
3629
3630 _ASSERTE(FitsInU4(invocationCount));
3631 DWORD cnt = static_cast<DWORD>(invocationCount);
3632
3633 _ASSERTE(cnt <= gc.Array->GetNumComponents());
3634
3635 for (DWORD i=0; i<cnt; i++)
3636 {
3637 gc.InnerDelegate = gc.Array->m_Array[i];
3638 InvokeUnhandledSwallowing(&gc.InnerDelegate, pDomain, &gc.EventArgs);
3639 }
3640 }
3641 GCPROTECT_END();
3642 }
3643 EX_CATCH
3644 {
3645 // It's not even worth asserting, because these aren't our bugs. At
3646 // some point, a MDA may be warranted.
3647 }
3648 EX_END_CATCH(SwallowAllExceptions)
3649}
3650
3651#endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE
3652