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