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 | |
14 | class Stub; |
15 | class ShuffleThunkCache; |
16 | |
17 | #include "cgensys.h" |
18 | #include "dllimportcallback.h" |
19 | #include "stubcache.h" |
20 | |
21 | typedef ArgBasedStubCache MulticastStubCache; |
22 | |
23 | VOID GenerateShuffleArray(MethodDesc* pInvoke, MethodDesc *pTargetMeth, struct ShuffleEntry * pShuffleEntryArray, size_t nEntries); |
24 | |
25 | |
26 | // This class represents the native methods for the Delegate class |
27 | class COMDelegate |
28 | { |
29 | private: |
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. |
44 | public: |
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 | |
127 | private: |
128 | static Stub* SetupShuffleThunk(MethodTable * pDelMT, MethodDesc *pTargetMeth); |
129 | |
130 | public: |
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 | |
146 | private: |
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. |
160 | enum 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 | |
172 | void DistributeEvent(OBJECTREF *pDelegate, |
173 | OBJECTREF *pDomain); |
174 | |
175 | void 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. |
196 | struct 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 | |
227 | class ShuffleThunkCache : public StubCacheBase |
228 | { |
229 | public: |
230 | ShuffleThunkCache(LoaderHeap* heap) : StubCacheBase(heap) |
231 | { |
232 | } |
233 | private: |
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 | |