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.h
6//
7// This module contains the native methods for the Delegate class.
8//
9
10
11#ifndef _COMDELEGATE_H_
12#define _COMDELEGATE_H_
13
14class Stub;
15class ShuffleThunkCache;
16
17#include "cgensys.h"
18#include "dllimportcallback.h"
19#include "stubcache.h"
20
21typedef ArgBasedStubCache MulticastStubCache;
22
23VOID GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, struct ShuffleEntry * pShuffleEntryArray, size_t nEntries);
24
25
26// This class represents the native methods for the Delegate class
27class COMDelegate
28{
29private:
30 // friend VOID CPUSTUBLINKER::EmitMulticastInvoke(...);
31 // friend VOID CPUSTUBLINKER::EmitShuffleThunk(...);
32 friend class CPUSTUBLINKER;
33 friend class DelegateInvokeStubManager;
34 friend class SecureDelegateFrame;
35 friend BOOL MulticastFrame::TraceFrame(Thread *thread, BOOL fromPatch,
36 TraceDestination *trace, REGDISPLAY *regs);
37
38 static MulticastStubCache* m_pSecureDelegateStubCache;
39 static MulticastStubCache* m_pMulticastStubCache;
40
41 static CrstStatic s_DelegateToFPtrHashCrst; // Lock for the following hash.
42 static PtrHashMap* s_pDelegateToFPtrHash; // Hash table containing the Delegate->FPtr pairs
43 // passed out to unmanaged code.
44public:
45 static ShuffleThunkCache *m_pShuffleThunkCache;
46
47 //REVIEW: reconcile initialization, one init?
48 // One time init.
49 static void Init();
50
51 static FCDECL3(void, DelegateConstruct, Object* refThis, Object* target, PCODE method);
52
53 static FCDECL1(Object*, InternalAlloc, ReflectClassBaseObject* target);
54 static FCDECL1(Object*, InternalAllocLike, Object* pThis);
55 static FCDECL2(FC_BOOL_RET, InternalEqualTypes, Object* pThis, Object *pThat);
56
57 static FCDECL3(PCODE, AdjustTarget, Object* refThis, Object* target, PCODE method);
58 static FCDECL2(PCODE, GetCallStub, Object* refThis, PCODE method);
59
60 static FCDECL5(FC_BOOL_RET, BindToMethodName, Object* refThisUNSAFE, Object* targetUNSAFE, ReflectClassBaseObject *pMethodTypeUNSAFE, StringObject* methodNameUNSAFE, int flags);
61
62 static FCDECL5(FC_BOOL_RET, BindToMethodInfo, Object* refThisUNSAFE, Object* targetUNSAFE, ReflectMethodObject *method, ReflectClassBaseObject *pMethodTypeUNSAFE, int flags);
63
64 // This gets the MethodInfo for a delegate, creating it if necessary
65 static FCDECL1(ReflectMethodObject*, FindMethodHandle, Object* refThis);
66 static FCDECL2(FC_BOOL_RET, InternalEqualMethodHandles, Object *refLeftIn, Object *refRightIn);
67
68 // Get the invoke method for the delegate. Used to transition delegates to multicast delegates.
69 static FCDECL1(PCODE, GetMulticastInvoke, Object* refThis);
70 static FCDECL1(MethodDesc*, GetInvokeMethod, Object* refThis);
71 static PCODE GetSecureInvoke(MethodDesc* pMD);
72 // determines where the delegate needs to be wrapped for non-security reason
73 static BOOL NeedsWrapperDelegate(MethodDesc* pTargetMD);
74 // on entry delegate points to the delegate to wrap
75 static DELEGATEREF CreateSecureDelegate(DELEGATEREF delegate, MethodDesc* pCreatorMethod, MethodDesc* pTargetMD);
76
77 // Marshals a delegate to a unmanaged callback.
78 static LPVOID ConvertToCallback(OBJECTREF pDelegate);
79
80 // Marshals a managed method to an unmanaged callback , provided the method is static and uses only
81 // blittable parameter types.
82 static PCODE ConvertToCallback(MethodDesc* pMD);
83
84 // Marshals an unmanaged callback to Delegate
85 static OBJECTREF ConvertToDelegate(LPVOID pCallback, MethodTable* pMT);
86
87#ifdef FEATURE_COMINTEROP
88 // Marshals a WinRT delegate interface pointer to a managed Delegate
89 static OBJECTREF ConvertWinRTInterfaceToDelegate(IUnknown *pUnk, MethodTable* pMT);
90
91 static ComPlusCallInfo * PopulateComPlusCallInfo(MethodTable * pDelMT);
92#endif // FEATURE_COMINTEROP
93
94 // Checks whether two delegates wrapping function pointers have the same unmanaged target
95 static FCDECL2(FC_BOOL_RET, CompareUnmanagedFunctionPtrs, Object *refDelegate1UNSAFE, Object *refDelegate2UNSAFE);
96
97 static PCODE GetStubForILStub(EEImplMethodDesc* pDelegateMD, MethodDesc** ppStubMD, DWORD dwStubFlags);
98 static MethodDesc* GetILStubMethodDesc(EEImplMethodDesc* pDelegateMD, DWORD dwStubFlags);
99
100 static void ValidateDelegatePInvoke(MethodDesc* pMD);
101
102 static void RemoveEntryFromFPtrHash(UPTR key);
103
104 // Decides if pcls derives from Delegate.
105 static BOOL IsDelegate(MethodTable *pMT);
106
107 // Decides if this is a secure delegate
108 static BOOL IsSecureDelegate(DELEGATEREF dRef);
109
110 // Get the cpu stub for a delegate invoke.
111 static PCODE GetInvokeMethodStub(EEImplMethodDesc* pMD);
112
113 // get the one single delegate invoke stub
114 static PCODE TheDelegateInvokeStub();
115
116#ifdef _TARGET_X86_
117#ifdef MDA_SUPPORTED
118 static Stub *GenerateStubForMDA(MethodDesc *pInvokeMD, MethodDesc *pStubMD, LPVOID pNativeTarget, Stub *pInnerStub);
119#endif // MDA_SUPPORTED
120#endif // _TARGET_X86_
121
122 static MethodDesc * __fastcall GetMethodDesc(OBJECTREF obj);
123 static OBJECTREF GetTargetObject(OBJECTREF obj);
124
125 static BOOL IsTrueMulticastDelegate(OBJECTREF delegate);
126
127private:
128 static Stub* SetupShuffleThunk(MethodTable * pDelMT, MethodDesc *pTargetMeth);
129
130public:
131 static MethodDesc* FindDelegateInvokeMethod(MethodTable *pMT);
132 static BOOL IsDelegateInvokeMethod(MethodDesc *pMD);
133
134 static BOOL IsMethodDescCompatible(TypeHandle thFirstArg,
135 TypeHandle thExactMethodType,
136 MethodDesc *pTargetMethod,
137 TypeHandle thDelegate,
138 MethodDesc *pInvokeMethod,
139 int flags,
140 BOOL *pfIsOpenDelegate);
141 static MethodDesc* GetDelegateCtor(TypeHandle delegateType, MethodDesc *pTargetMethod, DelegateCtorArgs *pCtorData);
142 //@GENERICSVER: new (suitable for generics)
143 // Method to do static validation of delegate .ctor
144 static BOOL ValidateCtor(TypeHandle objHnd, TypeHandle ftnParentHnd, MethodDesc *pFtn, TypeHandle dlgtHnd, BOOL *pfIsOpenDelegate);
145
146private:
147 static BOOL ValidateBeginInvoke(DelegateEEClass* pClass); // make certain the BeginInvoke method is consistant with the Invoke Method
148 static BOOL ValidateEndInvoke(DelegateEEClass* pClass); // make certain the EndInvoke method is consistant with the Invoke Method
149
150 static void BindToMethod(DELEGATEREF *pRefThis,
151 OBJECTREF *pRefFirstArg,
152 MethodDesc *pTargetMethod,
153 MethodTable *pExactMethodType,
154 BOOL fIsOpenDelegate,
155 BOOL fCheckSecurity);
156};
157
158// These flags effect the way BindToMethodInfo and BindToMethodName are allowed to bind a delegate to a target method. Their
159// values must be kept in sync with the definition in bcl\system\delegate.cs.
160enum DelegateBindingFlags
161{
162 DBF_StaticMethodOnly = 0x00000001, // Can only bind to static target methods
163 DBF_InstanceMethodOnly = 0x00000002, // Can only bind to instance (including virtual) methods
164 DBF_OpenDelegateOnly = 0x00000004, // Only allow the creation of delegates open over the 1st argument
165 DBF_ClosedDelegateOnly = 0x00000008, // Only allow the creation of delegates closed over the 1st argument
166 DBF_NeverCloseOverNull = 0x00000010, // A null target will never been considered as a possible null 1st argument
167 DBF_CaselessMatching = 0x00000020, // Use case insensitive lookup for methods matched by name
168 DBF_SkipSecurityChecks = 0x00000040, // Skip security checks (visibility, link demand etc.)
169 DBF_RelaxedSignature = 0x00000080, // Allow relaxed signature matching (co/contra variance)
170};
171
172void DistributeEvent(OBJECTREF *pDelegate,
173 OBJECTREF *pDomain);
174
175void DistributeUnhandledExceptionReliably(OBJECTREF *pDelegate,
176 OBJECTREF *pDomain,
177 OBJECTREF *pThrowable,
178 BOOL isTerminating);
179
180// Want no unused bits in ShuffleEntry since unused bits can make
181// equivalent ShuffleEntry arrays look unequivalent and deoptimize our
182// hashing.
183#include <pshpack1.h>
184
185// To handle a call to a static delegate, we create an array of ShuffleEntry
186// structures. Each entry instructs the shuffler to move a chunk of bytes.
187// The size of the chunk is StackElemSize (typically a DWORD): long arguments
188// have to be expressed as multiple ShuffleEntry's.
189//
190// The ShuffleEntry array serves two purposes:
191//
192// 1. A platform-indepedent blueprint for creating the platform-specific
193// shuffle thunk.
194// 2. A hash key for finding the shared shuffle thunk for a particular
195// signature.
196struct ShuffleEntry
197{
198 // Offset masks and special value
199 enum {
200 REGMASK = 0x8000, // Register offset bit
201 FPREGMASK = 0x4000, // Floating point register bit
202 FPSINGLEMASK = 0x2000, // Single precising floating point register
203 OFSMASK = 0x7fff, // Mask to get stack offset
204 OFSREGMASK = 0x1fff, // Mask to get register index
205 SENTINEL = 0xffff, // Indicates end of shuffle array
206 };
207
208#if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
209 union {
210 UINT16 srcofs;
211 CorElementType argtype; // AMD64: shuffle array is just types
212 };
213#else
214
215 UINT16 srcofs;
216
217 union {
218 UINT16 dstofs; //if srcofs != SENTINEL
219 UINT16 stacksizedelta; //if dstofs == SENTINEL, difference in stack size between virtual and static sigs
220 };
221#endif // _TARGET_AMD64_
222};
223
224
225#include <poppack.h>
226
227class ShuffleThunkCache : public StubCacheBase
228{
229public:
230 ShuffleThunkCache(LoaderHeap* heap) : StubCacheBase(heap)
231 {
232 }
233private:
234 //---------------------------------------------------------
235 // Compile a static delegate shufflethunk. Always returns
236 // STANDALONE since we don't interpret these things.
237 //---------------------------------------------------------
238 virtual void CompileStub(const BYTE *pRawStub,
239 StubLinker *pstublinker)
240 {
241 STANDARD_VM_CONTRACT;
242
243 ((CPUSTUBLINKER*)pstublinker)->EmitShuffleThunk((ShuffleEntry*)pRawStub);
244 }
245
246 //---------------------------------------------------------
247 // Tells the StubCacheBase the length of a ShuffleEntryArray.
248 //---------------------------------------------------------
249 virtual UINT Length(const BYTE *pRawStub)
250 {
251 LIMITED_METHOD_CONTRACT;
252 ShuffleEntry *pse = (ShuffleEntry*)pRawStub;
253 while (pse->srcofs != ShuffleEntry::SENTINEL)
254 {
255 pse++;
256 }
257 return sizeof(ShuffleEntry) * (UINT)(1 + (pse - (ShuffleEntry*)pRawStub));
258 }
259
260 virtual void AddStub(const BYTE* pRawStub, Stub* pNewStub)
261 {
262 CONTRACTL
263 {
264 THROWS;
265 GC_NOTRIGGER;
266 MODE_ANY;
267 }
268 CONTRACTL_END;
269
270#ifndef CROSSGEN_COMPILE
271 DelegateInvokeStubManager::g_pManager->AddStub(pNewStub);
272#endif
273 }
274};
275
276#endif // _COMDELEGATE_H_
277