From 8b4716077e5bfaafad5cbb255c611915a0d5137f Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 26 Mar 2026 15:29:06 -0700 Subject: [PATCH 1/6] Migrate COM event stubs over to IL stubs from the generic late dispatch stub --- .../src/System/__ComObject.cs | 15 -- src/coreclr/vm/clrtocomcall.cpp | 160 +++++++++--------- src/coreclr/vm/corelib.h | 2 +- src/coreclr/vm/method.hpp | 11 +- 4 files changed, 88 insertions(+), 100 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs b/src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs index a8c640551faf0e..9d5279cfa5e2f6 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs @@ -140,21 +140,6 @@ internal object GetEventProvider( return CreateEventProvider(t); } - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2062:Value passed to parameter cannot be statically determined", Justification = "The runtime passes a RuntimeType describing the COM event provider. The dynamic constructor access requirements are enforced by runtime callsite semantics.")] - [UnmanagedCallersOnly] - [RequiresUnsafe] - private static unsafe void GetEventProvider(__ComObject* pComObject, RuntimeType* pProviderType, object* pResult, Exception* pException) - { - try - { - *pResult = pComObject->GetEventProvider(*pProviderType); - } - catch (Exception ex) - { - *pException = ex; - } - } - private object CreateEventProvider( [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] RuntimeType t) { diff --git a/src/coreclr/vm/clrtocomcall.cpp b/src/coreclr/vm/clrtocomcall.cpp index 3a2373b8e8735b..6aa2dd96e34699 100644 --- a/src/coreclr/vm/clrtocomcall.cpp +++ b/src/coreclr/vm/clrtocomcall.cpp @@ -24,6 +24,7 @@ #include "sigbuilder.h" #include "callconvbuilder.hpp" #include "callsiteinspect.h" +#include "method.hpp" #define DISPATCH_INVOKE_SLOT 6 @@ -141,11 +142,89 @@ CLRToCOMCallInfo *CLRToCOMCall::PopulateCLRToCOMCallMethodDesc(MethodDesc* pMD, return pComInfo; } +namespace +{ + MethodDesc* CreateEventCallStub(MethodDesc* pMD) + { + STANDARD_VM_CONTRACT; + + _ASSERTE(pMD->IsCLRToCOMCall()); + + CLRToCOMCallInfo* pComInfo = CLRToCOMCallInfo::FromMethodDesc(pMD); + + _ASSERTE(pComInfo->m_pEventProviderMD != NULL); + + MethodDesc *pEvProvMD = pComInfo->m_pEventProviderMD; + MethodTable *pEvProvMT = pEvProvMD->GetMethodTable(); + + FunctionSigBuilder sigBuilder; + sigBuilder.SetCallingConv((CorCallingConvention)IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS); + + LocalDesc obj(ELEMENT_TYPE_OBJECT); + sigBuilder.NewArg(&obj); + + MetaSig sig(pEvProvMD); + LocalDesc retType(sig.GetRetTypeHandleThrowing()); + sigBuilder.SetReturnType(&retType); + + DWORD cbMetaSigSize = sigBuilder.GetSigSize(); + AllocMemHolder szMetaSig(pMD->GetMethodTable()->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMetaSigSize))); + sigBuilder.GetSig(szMetaSig, cbMetaSigSize); + + Signature signature(szMetaSig, cbMetaSigSize); + SigTypeContext typeContext; + + ILStubLinker stubLinker(pMD->GetModule(), signature, &typeContext, pEvProvMD, ILStubLinkerFlags::ILSTUB_LINKER_FLAG_STUB_HAS_THIS); + + ILCodeStream* pCode = stubLinker.NewCodeStream(ILStubLinker::kDispatch); + + pCode->EmitLoadThis(); + pCode->EmitLDTOKEN(pCode->GetToken(pEvProvMT)); + pCode->EmitCALL(METHOD__TYPE__GET_TYPE_FROM_HANDLE, 1, 1); + pCode->EmitCALL(METHOD__COM_OBJECT__GET_EVENT_PROVIDER, 2, 1); + pCode->EmitLDARG(0); + pCode->EmitCALL(pCode->GetToken(pEvProvMD), 2, 1); + pCode->EmitRET(); + + MethodDesc* pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc( + pMD->GetLoaderAllocator(), + pMD->GetMethodTable(), + PINVOKESTUB_FL_COMEVENTCALL, + pMD->GetModule(), + szMetaSig, + cbMetaSigSize, + &typeContext, + &stubLinker + ); + + #if defined(FEATURE_DYNAMIC_METHOD_HAS_NATIVE_STACK_ARG_SIZE) + if (pStubMD->IsDynamicMethod()) + { + DynamicMethodDesc* pDMD = pStubMD->AsDynamicMethodDesc(); + pDMD->SetNativeStackArgSize(2 * TARGET_POINTER_SIZE); // The native stack arg size is constant since the signature for struct stubs is constant. + } + #endif + + szMetaSig.SuppressRelease(); + + return pStubMD; + } +} + MethodDesc* CLRToCOMCall::GetILStubMethodDesc(MethodDesc* pMD, DWORD dwStubFlags) { STANDARD_VM_CONTRACT; - if (SF_IsCOMLateBoundStub(dwStubFlags) || SF_IsCOMEventCallStub(dwStubFlags)) + // COM event stubs are very simple and don't go through any marshalling logic. + // We generate them as a regular IL stub outside of the P/Invoke system. + if (SF_IsCOMEventCallStub(dwStubFlags)) + { + _ASSERTE(pMD->IsCLRToCOMCall()); // no generic COM eventing + ((CLRToCOMCallMethodDesc *)pMD)->InitComEventCallInfo(); + return CreateEventCallStub(pMD); + } + + if (SF_IsCOMLateBoundStub(dwStubFlags)) return NULL; // Get the call signature information @@ -283,76 +362,6 @@ I4ARRAYREF SetUpWrapperInfo(MethodDesc *pMD) return WrapperTypeArr; } -UINT32 CLRToCOMEventCallWorker(CLRToCOMMethodFrame* pFrame, CLRToCOMCallMethodDesc *pMD) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pFrame)); - PRECONDITION(CheckPointer(pMD)); - } - CONTRACTL_END; - - struct { - OBJECTREF EventProviderTypeObj; - OBJECTREF EventProviderObj; - OBJECTREF ThisObj; - } gc; - gc.EventProviderTypeObj = NULL; - gc.EventProviderObj = NULL; - gc.ThisObj = NULL; - - LOG((LF_STUBS, LL_INFO1000, "Calling CLRToCOMEventCallWorker %s::%s \n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); - - // Retrieve the method table and the method desc of the call. - MethodDesc *pEvProvMD = pMD->GetEventProviderMD(); - MethodTable *pEvProvMT = pEvProvMD->GetMethodTable(); - - GCPROTECT_BEGIN(gc) - { - // Retrieve the exposed type object for event provider. - gc.EventProviderTypeObj = pEvProvMT->GetManagedClassObject(); - gc.ThisObj = pFrame->GetThis(); - - UnmanagedCallersOnlyCaller getEventProvider(METHOD__COM_OBJECT__GET_EVENT_PROVIDER); - - // Retrieve the event provider for the event interface type. - getEventProvider.InvokeThrowing(&gc.ThisObj, &gc.EventProviderTypeObj, &gc.EventProviderObj); - - // Set up an arg iterator to retrieve the arguments from the frame. - MetaSig mSig(pMD); - ArgIterator ArgItr(&mSig); - - // Make the call on the event provider method desc. - MethodDescCallSite eventProvider(pEvProvMD, &gc.EventProviderObj); - - // Retrieve the event handler passed in. - OBJECTREF EventHandlerObj = ObjectToOBJECTREF(*(Object**)(pFrame->GetTransitionBlock() + ArgItr.GetNextOffset())); - - ARG_SLOT EventMethArgs[] = - { - ObjToArgSlot(gc.EventProviderObj), - ObjToArgSlot(EventHandlerObj) - }; - - // - // If this can ever return something bigger than an INT64 byval - // then this code is broken. Currently, however, it cannot. - // - *(ARG_SLOT *)(pFrame->GetReturnValuePtr()) = eventProvider.Call_RetArgSlot(EventMethArgs); - - // The COM event call worker does not support value returned in - // floating point registers. - _ASSERTE(ArgItr.GetFPReturnSize() == 0); - } - GCPROTECT_END(); - - // tell the asm stub that we are not returning an FP type - return 0; -} - static CallsiteDetails CreateCallsiteDetails(_In_ FramedMethodFrame *pFrame) { CONTRACTL @@ -728,12 +737,9 @@ UINT32 STDCALL CLRToCOMWorker(TransitionBlock * pTransitionBlock, CLRToCOMCallMe // Retrieve the interface method table. MethodTable *pItfMT = pMD->GetInterfaceMethodTable(); - // If the interface is a COM event call, then delegate to the CLRToCOMEventCallWorker. - if (pItfMT->IsComEventItfType()) - { - returnValue = CLRToCOMEventCallWorker(pFrame, pMD); - } - else if (pItfMT->GetComInterfaceType() == ifDispatch) + // COM event calls should be handled by an IL stub. + _ASSERTE(!pItfMT->IsComEventItfType()); + if (pItfMT->GetComInterfaceType() == ifDispatch) { // If the interface is a Dispatch only interface then convert the early bound // call to a late bound call. diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 51fd4dee8af33c..56d74e3460c1dc 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -179,7 +179,7 @@ DEFINE_CLASS_U(System, __ComObject, ComObject) DEFINE_FIELD_U(m_ObjectToDataMap, ComObject, m_ObjectToDataMap) DEFINE_CLASS(COM_OBJECT, System, __ComObject) DEFINE_METHOD(COM_OBJECT, RELEASE_ALL_DATA, ReleaseAllData, SM_PtrComObject_PtrException_RetVoid) -DEFINE_METHOD(COM_OBJECT, GET_EVENT_PROVIDER, GetEventProvider, SM_PtrComObject_PtrClass_PtrObj_PtrException_RetVoid) +DEFINE_METHOD(COM_OBJECT, GET_EVENT_PROVIDER, GetEventProvider, NoSig) #ifdef FOR_ILLINK DEFINE_METHOD(COM_OBJECT, CTOR, .ctor, IM_RetVoid) #endif // FOR_ILLINK diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index e9eb61abaf5348..b2f8e7c747af52 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -3468,14 +3468,11 @@ struct CLRToCOMCallInfo // EEImplMethodDesc that has already been initialized for COM interop. inline static CLRToCOMCallInfo *FromMethodDesc(MethodDesc *pMD); - union - { - // IL stub for CLR to COM call - PCODE m_pILStub; + // IL stub for CLR to COM call + PCODE m_pILStub; - // MethodDesc of the COM event provider to forward the call to (COM event interfaces) - MethodDesc *m_pEventProviderMD; - }; + // MethodDesc of the COM event provider to forward the call to (COM event interfaces) + MethodDesc *m_pEventProviderMD; // method table of the interface which this represents PTR_MethodTable m_pInterfaceMT; From a617f5d005dc33c48fab3184bf9220b331413ab4 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 27 Mar 2026 13:53:21 -0700 Subject: [PATCH 2/6] Treat CLRToCOMEvent stubs as step-through stubs to the target event method. --- src/coreclr/vm/ilstubcache.cpp | 4 ++++ src/coreclr/vm/method.hpp | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/ilstubcache.cpp b/src/coreclr/vm/ilstubcache.cpp index e3e0e514b3410f..08dfdd128c9522 100644 --- a/src/coreclr/vm/ilstubcache.cpp +++ b/src/coreclr/vm/ilstubcache.cpp @@ -274,6 +274,10 @@ MethodDesc* ILStubCache::CreateNewMethodDesc(LoaderHeap* pCreationHeap, MethodTa { pMD->SetILStubType(DynamicMethodDesc::StubCOMToCLRInterop); } + else if (SF_IsCOMEventCallStub(dwStubFlags)) + { + pMD->SetILStubType(DynamicMethodDesc::StubCLRToCOMEvent); + } else { pMD->SetILStubType(DynamicMethodDesc::StubCLRToCOMInterop); diff --git a/src/coreclr/vm/method.hpp b/src/coreclr/vm/method.hpp index b2f8e7c747af52..e196bf575bb9ce 100644 --- a/src/coreclr/vm/method.hpp +++ b/src/coreclr/vm/method.hpp @@ -2849,7 +2849,9 @@ class DynamicMethodDesc : public StoredSigMethodDesc StubDelegateInvokeMethod = 18, StubAsyncResume = 19, - StubLast = 20 + + StubCLRToCOMEvent = 20, + StubLast = 21 }; enum Flag : DWORD @@ -2971,7 +2973,8 @@ class DynamicMethodDesc : public StoredSigMethodDesc bool isStepThrough = false; ILStubType type = GetILStubType(); - isStepThrough = type == StubUnboxingIL || type == StubInstantiating; + + isStepThrough = type == StubUnboxingIL || type == StubInstantiating || type == StubCLRToCOMEvent; return isStepThrough; } From 2d5062b694358feb02b5afe099a299487ba8ff15 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 27 Mar 2026 15:12:02 -0700 Subject: [PATCH 3/6] Add support for strings in IL stubs --- src/coreclr/vm/dynamicmethod.cpp | 6 ++-- src/coreclr/vm/dynamicmethod.h | 4 +-- src/coreclr/vm/ilstubresolver.cpp | 33 +++++++++++++++++---- src/coreclr/vm/ilstubresolver.h | 2 +- src/coreclr/vm/stubgen.cpp | 11 +++++++ src/coreclr/vm/stubgen.h | 49 +++++++++++++++++++++++++++++++ 6 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/coreclr/vm/dynamicmethod.cpp b/src/coreclr/vm/dynamicmethod.cpp index 5939702e667534..8ebcc866559de2 100644 --- a/src/coreclr/vm/dynamicmethod.cpp +++ b/src/coreclr/vm/dynamicmethod.cpp @@ -1260,14 +1260,14 @@ LCGMethodResolver::GetLocalSig() //--------------------------------------------------------------------------------------- // -OBJECTHANDLE +STRINGREF* LCGMethodResolver::ConstructStringLiteral(mdToken metaTok) { STANDARD_VM_CONTRACT; GCX_COOP(); - OBJECTHANDLE string = NULL; + STRINGREF* string = NULL; STRINGREF strRef = GetStringLiteral(metaTok); GCPROTECT_BEGIN(strRef); @@ -1277,7 +1277,7 @@ LCGMethodResolver::ConstructStringLiteral(mdToken metaTok) // Instead of storing the string literal in the appdomain specific string literal map, // we store it in the dynamic method specific string liternal list // This way we can release it when the dynamic method is collected. - string = (OBJECTHANDLE)GetOrInternString(&strRef); + string = GetOrInternString(&strRef); } GCPROTECT_END(); diff --git a/src/coreclr/vm/dynamicmethod.h b/src/coreclr/vm/dynamicmethod.h index 9669f3b09f8662..92afff311e289d 100644 --- a/src/coreclr/vm/dynamicmethod.h +++ b/src/coreclr/vm/dynamicmethod.h @@ -92,7 +92,7 @@ class DynamicResolver // // jit interface api - virtual OBJECTHANDLE ConstructStringLiteral(mdToken metaTok) = 0; + virtual STRINGREF* ConstructStringLiteral(mdToken metaTok) = 0; virtual BOOL IsValidStringRef(mdToken metaTok) = 0; virtual STRINGREF GetStringLiteral(mdToken metaTok) = 0; virtual void ResolveToken(mdToken token, ResolvedToken* resolvedToken) = 0; @@ -156,7 +156,7 @@ class LCGMethodResolver final : public DynamicResolver BYTE* GetCodeInfo(unsigned *pCodeSize, unsigned *pStackSize, CorInfoOptions *pOptions, unsigned* pEHSize); SigPointer GetLocalSig(); - OBJECTHANDLE ConstructStringLiteral(mdToken metaTok); + STRINGREF* ConstructStringLiteral(mdToken metaTok); BOOL IsValidStringRef(mdToken metaTok); void ResolveToken(mdToken token, ResolvedToken* resolvedToken); SigPointer ResolveSignature(mdToken token); diff --git a/src/coreclr/vm/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 0ab3b1476bccbb..81a96a1d24fbd0 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -112,25 +112,46 @@ ILStubResolver::GetLocalSig() m_pCompileTimeState->m_ILHeader.cbLocalVarSig); } -OBJECTHANDLE ILStubResolver::ConstructStringLiteral(mdToken token) +STRINGREF* ILStubResolver::ConstructStringLiteral(mdToken token) { STANDARD_VM_CONTRACT; - _ASSERTE(FALSE); - return (OBJECTHANDLE)NULL; + + GCX_COOP(); + + STRINGREF* string = NULL; + STRINGREF strRef = GetStringLiteral(token); + + GCPROTECT_BEGIN(strRef); + + if (strRef != NULL) + { + string = GetDynamicMethod()->GetLoaderAllocator()->GetOrInternString(&strRef); + } + + GCPROTECT_END(); + + return string; } BOOL ILStubResolver::IsValidStringRef(mdToken metaTok) { STANDARD_VM_CONTRACT; - _ASSERTE(FALSE); - return FALSE; + + return TypeFromToken(metaTok) == mdtString && metaTok <= m_pCompileTimeState->m_tokenLookupMap.GetMaxUserStringToken(); } STRINGREF ILStubResolver::GetStringLiteral(mdToken metaTok) { LIMITED_METHOD_CONTRACT; - _ASSERTE(FALSE); +#ifndef DACCESS_COMPILE + SString& str = m_pCompileTimeState->m_tokenLookupMap.LookupUserString(metaTok); + + LPCWSTR unicodeStr = str.GetUnicode(); + return StringObject::NewString(unicodeStr, str.GetCount()); +#else + DacNotImpl(); return NULL; +#endif } void ILStubResolver::ResolveToken(mdToken token, ResolvedToken* resolvedToken) diff --git a/src/coreclr/vm/ilstubresolver.h b/src/coreclr/vm/ilstubresolver.h index 4e970423a69da0..73544527044214 100644 --- a/src/coreclr/vm/ilstubresolver.h +++ b/src/coreclr/vm/ilstubresolver.h @@ -32,7 +32,7 @@ class ILStubResolver : public DynamicResolver BYTE* GetCodeInfo(unsigned* pCodeSize, unsigned* pStackSize, CorInfoOptions* pOptions, unsigned* pEHSize); SigPointer GetLocalSig(); - OBJECTHANDLE ConstructStringLiteral(mdToken metaTok); + STRINGREF* ConstructStringLiteral(mdToken metaTok); BOOL IsValidStringRef(mdToken metaTok); STRINGREF GetStringLiteral(mdToken metaTok); void ResolveToken(mdToken token, ResolvedToken* resolvedToken); diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index b70cac45d9de85..d81759cb22891b 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -1636,6 +1636,11 @@ void ILCodeStream::EmitLDSFLDA(int token) WRAPPER_NO_CONTRACT; Emit(CEE_LDSFLDA, 1, token); } +void ILCodeStream::EmitLDSTR(SString str) +{ + WRAPPER_NO_CONTRACT; + Emit(CEE_LDSTR, 1, m_pOwner->GetUserStringToken(std::move(str))); +} void ILCodeStream::EmitLDTOKEN(int token) { WRAPPER_NO_CONTRACT; @@ -3258,6 +3263,12 @@ int ILStubLinker::GetToken(FieldDesc* pFD, mdToken typeSignature) return m_tokenMap.GetToken(pFD, typeSignature); } +int ILStubLinker::GetUserStringToken(SString s) +{ + STANDARD_VM_CONTRACT; + return m_tokenMap.GetUserStringToken(std::move(s)); +} + int ILStubLinker::GetSigToken(PCCOR_SIGNATURE pSig, DWORD cbSig) { STANDARD_VM_CONTRACT; diff --git a/src/coreclr/vm/stubgen.h b/src/coreclr/vm/stubgen.h index da1bbc0a6ee4bc..6a690ffe4f4d87 100644 --- a/src/coreclr/vm/stubgen.h +++ b/src/coreclr/vm/stubgen.h @@ -11,6 +11,7 @@ #define __STUBGEN_H__ #include "stublink.h" +#include "sstring.h" struct ILStubEHClause; class ILStubLinker; @@ -314,6 +315,7 @@ class TokenLookupMap m_memberRefs.Set(pSrc->m_memberRefs); m_methodSpecs.Set(pSrc->m_methodSpecs); m_typeSpecs.Set(pSrc->m_typeSpecs); + m_userStrings.Set(pSrc->m_userStrings); } TypeHandle LookupTypeDef(mdToken token) @@ -419,6 +421,22 @@ class TokenLookupMap return SigPointer(pSig, cbSig); } + SString& LookupUserString(mdToken token) + { + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + PRECONDITION(RidFromToken(token) - 1 < m_userStrings.GetCount()); + PRECONDITION(RidFromToken(token) != 0); + PRECONDITION(TypeFromToken(token) == mdtString); + } + CONTRACTL_END; + + return m_userStrings[static_cast(RidFromToken(token) - 1)]; + } + mdToken GetToken(TypeHandle th) { WRAPPER_NO_CONTRACT; @@ -528,6 +546,34 @@ class TokenLookupMap return token; } + mdToken GetUserStringToken(SString str) + { + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + } + CONTRACTL_END; + + mdToken token = TokenFromRid(m_userStrings.GetCount(), mdtString)+1; + m_userStrings.Append(std::move(str)); + return token; + } + + mdToken GetMaxUserStringToken() + { + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + } + CONTRACTL_END; + + return TokenFromRid(m_userStrings.GetCount(), mdtString); + } + protected: mdToken GetTypeSpecWorker(TypeSpecEntry** entry) { @@ -621,6 +667,7 @@ class TokenLookupMap uint32_t m_nextAvailableRid; CQuickBytesSpecifySize m_qbEntries; SArray, FALSE> m_signatures; + SArray m_userStrings; SArray m_memberRefs; SArray m_methodSpecs; SArray m_typeSpecs; @@ -798,6 +845,7 @@ class ILStubLinker int GetToken(TypeHandle th, mdToken typeSignature); int GetToken(FieldDesc* pFD); int GetToken(FieldDesc* pFD, mdToken typeSignature); + int GetUserStringToken(SString str); int GetSigToken(PCCOR_SIGNATURE pSig, DWORD cbSig); DWORD NewLocal(CorElementType typ = ELEMENT_TYPE_I); DWORD NewLocal(LocalDesc loc); @@ -975,6 +1023,7 @@ class ILCodeStream void EmitLDOBJ (int token); void EmitLDSFLD (int token); void EmitLDSFLDA (int token); + void EmitLDSTR (SString str); void EmitLDTOKEN (int token); void EmitLEAVE (ILCodeLabel* pCodeLabel); void EmitLOCALLOC (); From f844e3b65cffc17fb164325974af36e0a9ded686 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 27 Mar 2026 15:12:18 -0700 Subject: [PATCH 4/6] Add various additional instructions needed for late bound stubs --- src/coreclr/vm/stubgen.cpp | 15 +++++++++++++++ src/coreclr/vm/stubgen.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/coreclr/vm/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index d81759cb22891b..4666eef7a2eb1b 100644 --- a/src/coreclr/vm/stubgen.cpp +++ b/src/coreclr/vm/stubgen.cpp @@ -1671,6 +1671,11 @@ void ILCodeStream::EmitNEWOBJ(int token, int numInArgs) WRAPPER_NO_CONTRACT; Emit(CEE_NEWOBJ, (INT16)(1 - numInArgs), token); } +void ILCodeStream::EmitNEWARR(int token) +{ + WRAPPER_NO_CONTRACT; + Emit(CEE_NEWARR, 0, token); +} void ILCodeStream::EmitNOP(LPCSTR pszNopComment) { @@ -1699,6 +1704,16 @@ void ILCodeStream::EmitSTARG(unsigned uArgIdx) WRAPPER_NO_CONTRACT; Emit(CEE_STARG, -1, uArgIdx); } +void ILCodeStream::EmitSTELEM_I1() +{ + WRAPPER_NO_CONTRACT; + Emit(CEE_STELEM_I1, -3, 0); +} +void ILCodeStream::EmitSTELEM_I4() +{ + WRAPPER_NO_CONTRACT; + Emit(CEE_STELEM_I4, -3, 0); +} void ILCodeStream::EmitSTELEM_REF() { WRAPPER_NO_CONTRACT; diff --git a/src/coreclr/vm/stubgen.h b/src/coreclr/vm/stubgen.h index 6a690ffe4f4d87..10ae25c1ce0bc0 100644 --- a/src/coreclr/vm/stubgen.h +++ b/src/coreclr/vm/stubgen.h @@ -1030,11 +1030,14 @@ class ILCodeStream void EmitMUL (); void EmitMUL_OVF (); void EmitNEWOBJ (int token, int numInArgs); + void EmitNEWARR (int token); void EmitNOP (LPCSTR pszNopComment); void EmitPOP (); void EmitRET (); void EmitSHR_UN (); void EmitSTARG (unsigned uArgIdx); + void EmitSTELEM_I1 (); + void EmitSTELEM_I4 (); void EmitSTELEM_REF (); void EmitSTIND_I (); void EmitSTIND_I1 (); From 0e184d2b401acb3bce27253be8073363b9fa9f75 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Fri, 27 Mar 2026 17:19:23 -0700 Subject: [PATCH 5/6] Convert CLR->COM stubs to use an IL stub --- .../src/System/RuntimeType.CoreCLR.cs | 33 -- src/coreclr/vm/clrtocomcall.cpp | 3 - src/coreclr/vm/corelib.h | 2 +- src/coreclr/vm/dllimport.cpp | 376 +++++++++++++++++- src/coreclr/vm/ilstubresolver.cpp | 6 +- 5 files changed, 381 insertions(+), 39 deletions(-) diff --git a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs index a23e884d7b2d8e..9b8f2d0268c7a0 100644 --- a/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs +++ b/src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs @@ -4221,39 +4221,6 @@ private object InvokeDispMethod( return ret; } - [RequiresUnreferencedCode("The member might be removed")] - [UnmanagedCallersOnly] - private static unsafe void ForwardCallToInvokeMember( - RuntimeType* pRuntimeType, - string* pMemberName, - int flags, - object* pTarget, - object[]* pArgs, - bool[]* pArgsIsByRef, - int[]* pArgsWrapperTypes, - Type[]* pArgsTypes, - Type* pRetType, - object* pResult, - Exception* pException) - { - try - { - *pResult = pRuntimeType->ForwardCallToInvokeMember( - *pMemberName, - (BindingFlags)flags, - *pTarget, - *pArgs, - *pArgsIsByRef, - *pArgsWrapperTypes, - *pArgsTypes, - *pRetType); - } - catch (Exception ex) - { - *pException = ex; - } - } - private static void WrapArgsForInvokeCall(object[] aArgs, int[] aArgsWrapperTypes) { int cArgs = aArgs.Length; diff --git a/src/coreclr/vm/clrtocomcall.cpp b/src/coreclr/vm/clrtocomcall.cpp index 6aa2dd96e34699..9096bd420b3295 100644 --- a/src/coreclr/vm/clrtocomcall.cpp +++ b/src/coreclr/vm/clrtocomcall.cpp @@ -224,9 +224,6 @@ MethodDesc* CLRToCOMCall::GetILStubMethodDesc(MethodDesc* pMD, DWORD dwStubFlags return CreateEventCallStub(pMD); } - if (SF_IsCOMLateBoundStub(dwStubFlags)) - return NULL; - // Get the call signature information StubSigDesc sigDesc(pMD); diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 56d74e3460c1dc..72cd328b8a1a25 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -159,7 +159,7 @@ DEFINE_METHOD(CLASS, CTOR, .ctor, #endif // FOR_ILLINK #ifdef FEATURE_COMINTEROP -DEFINE_METHOD(CLASS, FORWARD_CALL_TO_INVOKE, ForwardCallToInvokeMember, SM_PtrClass_PtrStr_Int_PtrObj_PtrArrObj_PtrArrBool_PtrArrInt_PtrArrType_PtrType_PtrObj_PtrException_RetVoid) +DEFINE_METHOD(CLASS, FORWARD_CALL_TO_INVOKE, ForwardCallToInvokeMember, NoSig) #endif // FEATURE_COMINTEROP BEGIN_ILLINK_FEATURE_SWITCH(System.Runtime.InteropServices.BuiltInComInterop.IsSupported, true, true) diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 68aec52fd682af..69d1006d75012c 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -45,6 +45,7 @@ #ifdef FEATURE_COMINTEROP #include "runtimecallablewrapper.h" #include "clrtocomcall.h" +#include "reflectioninvocation.h" #endif // FEATURE_COMINTEROP #include "eventtrace.h" @@ -782,7 +783,7 @@ class ILStubState : public StubState if (SF_IsHRESULTSwapping(m_dwStubFlags)) { - if (SF_IsForwardStub(m_dwStubFlags)) + if (SF_IsForwardStub(m_dwStubFlags) && !SF_IsCOMLateBoundStub(m_dwStubFlags)) { ILCodeLabel* pSkipThrowLabel = pcsDispatch->NewCodeLabel(); @@ -1621,6 +1622,369 @@ class COMToCLRFieldAccess_ILStubState : public COMToCLR_ILStubState protected: FieldDesc *m_pFD; }; + +class LateBoundCLRToCOM_ILStubState : public ILStubState +{ +public: + + LateBoundCLRToCOM_ILStubState(Module* pStubModule, const Signature &signature, SigTypeContext *pTypeContext, DWORD dwStubFlags, MethodDesc* pTargetMD) + : ILStubState( + pStubModule, + signature, + pTypeContext, + dwStubFlags | PINVOKESTUB_FL_STUB_HAS_THIS | PINVOKESTUB_FL_TARGET_HAS_THIS, + -1 /* no explicit LCID argument support for late binding */, + CoreLibBinder::GetMethod(METHOD__CLASS__FORWARD_CALL_TO_INVOKE)), /* Late binding stubs will always eventually invoke this method. Pass it as the target for easier diagnostic support. */ + m_signature(pTargetMD) + { + STANDARD_VM_CONTRACT; + m_wrapperTypes.ReSizeThrows(m_signature.NumFixedArgs()); + + MethodDesc* pInterfaceMD = pTargetMD; + if (!pInterfaceMD->IsInterface()) + { + pInterfaceMD = pTargetMD->GetInterfaceMD(); + } + + m_pManagedMD = pInterfaceMD; + } + + void BeginEmit(DWORD dwStubFlags) // CLR to COM IL + { + STANDARD_VM_CONTRACT; + + ILStubState::BeginEmit(dwStubFlags); + + + ILCodeStream *pcsSetup = m_slIL.GetSetupCodeStream(); + + LocalDesc argumentArray(ELEMENT_TYPE_OBJECT); + argumentArray.MakeArray(); + m_argumentArrayLocalNum = MakeArrayLocalOfElementType(pcsSetup, CLASS__OBJECT); + m_argumentTypeLocalNum = MakeArrayLocalOfElementType(pcsSetup, CLASS__TYPE); + m_argumentIsByRefArrayLocalNum = MakeArrayLocalOfElementType(pcsSetup, CLASS__BOOLEAN); + } + void MarshalLCID(int argIdx) + { + LIMITED_METHOD_CONTRACT; + } + + void MarshalArgument(MarshalInfo* pInfo, int argOffset) + { + STANDARD_VM_CONTRACT; + + UINT paramIndex = pInfo->GetParamIndex(); + + CorElementType elementType = m_signature.NextArg(); + + _ASSERTE(pInfo->IsByRef() == (elementType == ELEMENT_TYPE_BYREF)); + + TypeHandle argType; + + if (elementType == ELEMENT_TYPE_BYREF) + { + elementType = m_signature.GetByRefType(&argType); + if (CorIsPrimitiveType(elementType)) + { + argType = TypeHandle(CoreLibBinder::GetElementType(elementType)); + } + } + else + { + argType = m_signature.GetLastTypeHandleThrowing(); + } + + ILCodeStream *pcsMarshal = m_slIL.GetMarshalCodeStream(); + + pcsMarshal->EmitLDLOC(m_argumentArrayLocalNum); + pcsMarshal->EmitLDC(paramIndex); + LocalDesc argTypeDesc(argType); + pcsMarshal->EmitLDARG(paramIndex); + if (pInfo->IsByRef()) + { + pcsMarshal->EmitLDIND_T(&argTypeDesc); + } + + if (argType.IsValueType()) + { + pcsMarshal->EmitBOX(pcsMarshal->GetToken(argType)); + } + + pcsMarshal->EmitSTELEM_REF(); + + pcsMarshal->EmitLDLOC(m_argumentTypeLocalNum); + pcsMarshal->EmitLDC(paramIndex); + + pcsMarshal->EmitLDTOKEN(pcsMarshal->GetToken(argType)); + pcsMarshal->EmitCALL(METHOD__TYPE__GET_TYPE_FROM_HANDLE, 1, 1); + pcsMarshal->EmitSTELEM_REF(); + + if (pInfo->IsByRef()) + { + // Mark this argument as by-ref in the argumentIsByRef array so the callee knows to copy back the value after the call. + pcsMarshal->EmitLDLOC(m_argumentIsByRefArrayLocalNum); + pcsMarshal->EmitLDC(paramIndex); + pcsMarshal->EmitLDC(1); + pcsMarshal->EmitSTELEM_I1(); + + // We need to emit the code to copy back the results after the call. + ILCodeStream* pcsUnmarshal = m_slIL.GetUnmarshalCodeStream(); + + pcsUnmarshal->EmitLDARG(paramIndex); + pcsUnmarshal->EmitLDLOC(m_argumentArrayLocalNum); + pcsUnmarshal->EmitLDC(paramIndex); + pcsUnmarshal->EmitLDELEM_REF(); + pcsUnmarshal->EmitUNBOX_ANY(pcsUnmarshal->GetToken(argType)); + pcsUnmarshal->EmitSTIND_T(&argTypeDesc); + } + + m_wrapperTypes[paramIndex] = pInfo->GetDispWrapperType(); + if (m_wrapperTypes[paramIndex] != 0) + { + m_wrapperTypesNeeded = TRUE; + } + } + + void FinishEmit(MethodDesc* pStubMD) + { + if (m_wrapperTypesNeeded) + { + ILCodeStream* pcsMarshal = m_slIL.GetMarshalCodeStream(); + + LocalDesc wrapperArrayType(ELEMENT_TYPE_I4); + wrapperArrayType.MakeArray(); + m_wrapperArrayLocalNum = pcsMarshal->NewLocal(wrapperArrayType); + pcsMarshal->EmitLDC((DWORD)m_signature.NumFixedArgs()); + pcsMarshal->EmitNEWARR(pcsMarshal->GetToken(CoreLibBinder::GetClass(CLASS__INT32))); + pcsMarshal->EmitSTLOC(m_wrapperArrayLocalNum); + + for (UINT i = 0; i < m_signature.NumFixedArgs(); i++) + { + if (m_wrapperTypes[i] != 0) + { + pcsMarshal->EmitLDLOC(m_wrapperArrayLocalNum); + pcsMarshal->EmitLDC(i); + pcsMarshal->EmitLDC(m_wrapperTypes[i]); + pcsMarshal->EmitSTELEM_I4(); + } + } + } + + ILStubState::FinishEmit(pStubMD); + } + + void EmitInvokeTarget(MethodDesc* pTargetMD, MethodDesc* pStubMD) + { + STANDARD_VM_CONTRACT; + + // We don't invoke a target method here directly. + // We invoke a helper that will go through late binding to invoke the target. + ILCodeStream* pcsDispatch = m_slIL.GetDispatchCodeStream(); + + pcsDispatch->EmitLDTOKEN(pcsDispatch->GetToken(m_pManagedMD->GetMethodTable())); + pcsDispatch->EmitCALL(METHOD__TYPE__GET_TYPE_FROM_HANDLE, 1, 1); + + mdToken token; + LPCUTF8 memberName; + DWORD binderFlags = GetInfoFromMetadata(&token, &memberName); + + // Retrieve the name of the target member. If the member + // has a DISPID then use that to optimize the invoke. + DISPID dispId = DISPID_UNKNOWN; + HRESULT hr = m_pManagedMD->GetMDImport()->GetDispIdOfMemberDef(token, (ULONG*)&dispId); + if (hr == S_OK) + { + WCHAR strTmp[ARRAY_SIZE(DISPID_NAME_FORMAT_STRING) + MaxUnsigned32BitDecString]; + _snwprintf_s(strTmp, ARRAY_SIZE(strTmp), _TRUNCATE, DISPID_NAME_FORMAT_STRING, dispId); + pcsDispatch->EmitLDSTR(SString(strTmp)); + } + else + { + pcsDispatch->EmitLDSTR(SString(SString::Utf8, memberName)); + } + + pcsDispatch->EmitLDC(binderFlags); + pcsDispatch->EmitLoadThis(); + pcsDispatch->EmitLDLOC(m_argumentArrayLocalNum); + pcsDispatch->EmitLDLOC(m_argumentIsByRefArrayLocalNum); + if (m_wrapperTypesNeeded) + { + pcsDispatch->EmitLDLOC(m_wrapperArrayLocalNum); + } + else + { + pcsDispatch->EmitLDNULL(); + } + pcsDispatch->EmitLDLOC(m_argumentTypeLocalNum); + + pcsDispatch->EmitLDTOKEN(pcsDispatch->GetToken(m_signature.GetRetTypeHandleThrowing())); + pcsDispatch->EmitCALL(METHOD__TYPE__GET_TYPE_FROM_HANDLE, 1, 1); + pcsDispatch->EmitCALL(METHOD__CLASS__FORWARD_CALL_TO_INVOKE, 9, 1); + } + + void MarshalReturn(MarshalInfo* pInfo, int argOffset) + { + STANDARD_VM_CONTRACT; + + ILCodeStream* pcs = m_slIL.GetReturnUnmarshalCodeStream(); + + if (m_pManagedMD->IsVoid()) + { + pcs->EmitPOP(); + } + else + { + LocalDesc rawRet(ELEMENT_TYPE_OBJECT); + DWORD rawRetLocal = pcs->NewLocal(rawRet); + pcs->EmitSTLOC(rawRetLocal); + pcs->EmitLDLOC(rawRetLocal); + + ILCodeLabel* nullRetLabel = pcs->NewCodeLabel(); + ILCodeLabel* afterRetLabel = pcs->NewCodeLabel(); + pcs->EmitBRFALSE(nullRetLabel); + + // If the return value is non-null, then we can just unbox it. + pcs->EmitLDLOC(rawRetLocal); + pcs->EmitUNBOX_ANY(pcs->GetToken(m_signature.GetRetTypeHandleThrowing())); + pcs->EmitBR(afterRetLabel); + + // If the return value is null, we need to return the default value for the return type. + pcs->EmitLabel(nullRetLabel); + LocalDesc retTypeDesc(m_signature.GetRetTypeHandleThrowing()); + DWORD defaultValueLocal = pcs->NewLocal(retTypeDesc); + pcs->EmitLDLOCA(defaultValueLocal); + pcs->EmitINITOBJ(pcs->GetToken(m_signature.GetRetTypeHandleThrowing())); + pcs->EmitLDLOC(defaultValueLocal); + + pcs->EmitLabel(afterRetLabel); + } + } + +private: + DWORD MakeArrayLocalOfElementType(ILCodeStream* pcs, BinderClassID elementType) + { + MethodTable* pMT = CoreLibBinder::GetClass(elementType); + LocalDesc arrayDesc(pMT); + arrayDesc.MakeArray(); + DWORD localNum = pcs->NewLocal(arrayDesc); + pcs->EmitLDC(m_signature.NumFixedArgs()); + pcs->EmitNEWARR(pcs->GetToken(pMT)); + pcs->EmitSTLOC(localNum); + return localNum; + } + + DWORD GetInfoFromMetadata(mdToken* pToken, LPCUTF8* pstrMemberName) + { + DWORD binderFlags = BINDER_AllLookup; + mdProperty propToken; + LPCUTF8 strMemberName; + ULONG uSemantic; + + // If the method has a void return type, then set the IgnoreReturn binding flag. + if (m_pManagedMD->IsVoid()) + binderFlags |= BINDER_IgnoreReturn; + + // See if there is property information for this member. + HRESULT hr = m_pManagedMD->GetMDImport()->GetPropertyInfoForMethodDef(m_pManagedMD->GetMemberDef(), &propToken, &strMemberName, &uSemantic); + if (hr != S_OK) + { + // Non-property method + *pstrMemberName = m_pManagedMD->GetName(); + *pToken = m_pManagedMD->GetMemberDef(); + return binderFlags | BINDER_InvokeMethod; + } + + // Property accessor + *pstrMemberName = strMemberName; + *pToken = propToken; + + // Determine which type of accessor we are dealing with. + switch (uSemantic) + { + case msGetter: + { + // INVOKE_PROPERTYGET + binderFlags |= BINDER_GetProperty; + break; + } + + case msSetter: + { + // INVOKE_PROPERTYPUT or INVOKE_PROPERTYPUTREF + ULONG cAssoc; + ASSOCIATE_RECORD* pAssoc; + + IMDInternalImport *pMDImport = m_pManagedMD->GetMDImport(); + + // Retrieve all the associates. + HENUMInternalHolder henum{ pMDImport }; + henum.EnumAssociateInit(propToken); + + cAssoc = henum.EnumGetCount(); + _ASSERTE(cAssoc > 0); + + ULONG allocSize = cAssoc * sizeof(*pAssoc); + if (allocSize < cAssoc) + COMPlusThrowHR(COR_E_OVERFLOW); + + pAssoc = (ASSOCIATE_RECORD*)_alloca((size_t)allocSize); + IfFailThrow(pMDImport->GetAllAssociates(&henum, pAssoc, cAssoc)); + + // Check to see if there is both a set and an other. If this is the case + // then the setter is a INVOKE_PROPERTYPUTREF otherwise we will make it a + // INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF. + bool propHasOther = false; + for (ULONG i = 0; i < cAssoc; i++) + { + if (pAssoc[i].m_dwSemantics == msOther) + { + propHasOther = true; + break; + } + } + + if (propHasOther) + { + // There is both a INVOKE_PROPERTYPUT and a INVOKE_PROPERTYPUTREF for this + // property. Therefore be specific and make this invoke a INVOKE_PROPERTYPUTREF. + binderFlags |= BINDER_PutRefDispProperty; + } + else + { + // Only a setter so make the invoke a set which maps to + // INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF. + binderFlags = BINDER_SetProperty; + } + break; + } + + case msOther: + { + // INVOKE_PROPERTYPUT + binderFlags |= BINDER_PutDispProperty; + break; + } + + default: + { + _ASSERTE(!"Invalid method semantic!"); + } + } + + return binderFlags; + } + + MethodDesc* m_pManagedMD; + MetaSig m_signature; + + DWORD m_argumentArrayLocalNum; + DWORD m_argumentIsByRefArrayLocalNum; + DWORD m_argumentTypeLocalNum; + DWORD m_wrapperArrayLocalNum; + CQuickArray m_wrapperTypes; + BOOL m_wrapperTypesNeeded; +}; + #endif // FEATURE_COMINTEROP ILStubLinkerFlags GetILStubLinkerFlagsForPInvokeStubFlags(PInvokeStubFlags flags) @@ -3625,6 +3989,12 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, pss->MarshalReturn(&returnInfo, argOffset); } } + else if (SF_IsCOMLateBoundStub(dwStubFlags)) + { + // Late-bound COM always targets a method that returns an object. + // Call MarshalReturn with a "NULL" marshal info to enable it to handle this appropriately. + pss->MarshalReturn(NULL, argOffset); + } return marshalType; } @@ -5284,6 +5654,10 @@ MethodDesc* PInvoke::CreateCLRToNativeILStub( { pStubState = new COMToCLR_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD); } + else if (SF_IsCOMLateBoundStub(dwStubFlags)) + { + pStubState = new LateBoundCLRToCOM_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, pMD); + } else { pStubState = new CLRToCOM_ILStubState(pModule, pSigDesc->m_sig, &pSigDesc->m_typeContext, dwStubFlags, iLCIDArg, pMD); diff --git a/src/coreclr/vm/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 81a96a1d24fbd0..5cf08ead32155d 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -115,7 +115,10 @@ ILStubResolver::GetLocalSig() STRINGREF* ILStubResolver::ConstructStringLiteral(mdToken token) { STANDARD_VM_CONTRACT; - +#ifdef DACCESS_COMPILE + DacNotImpl(); + return NULL; +#else // DACCESS_COMPILE GCX_COOP(); STRINGREF* string = NULL; @@ -131,6 +134,7 @@ STRINGREF* ILStubResolver::ConstructStringLiteral(mdToken token) GCPROTECT_END(); return string; +#endif // DACCESS_COMPILE } BOOL ILStubResolver::IsValidStringRef(mdToken metaTok) From 990a0164b19792bd499a703a1e7b6bb293f64d55 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 30 Mar 2026 13:29:14 -0700 Subject: [PATCH 6/6] Remove generic late-bound fallback, all code only used by it, and any diagnostics logic that's specific to it. Collapse StubState into ILStubState as there is no longer any other derived type. Also, move over the late-bound stub tracing step to work on the IL stub path. --- docs/design/datacontracts/StackWalk.md | 1 - src/coreclr/debug/daccess/dacdbiimpl.cpp | 2 +- src/coreclr/debug/ee/controller.cpp | 45 +- src/coreclr/debug/ee/controller.h | 10 +- src/coreclr/debug/ee/frameinfo.cpp | 18 - src/coreclr/debug/ee/frameinfo.h | 9 - src/coreclr/vm/CMakeLists.txt | 13 +- src/coreclr/vm/FrameTypes.h | 1 - .../vm/amd64/GenericCLRToCOMCallStubs.asm | 43 - src/coreclr/vm/amd64/cgencpu.h | 1 - src/coreclr/vm/arm64/asmhelpers.asm | 36 - src/coreclr/vm/callsiteinspect.cpp | 518 ------------ src/coreclr/vm/callsiteinspect.h | 47 -- src/coreclr/vm/cgensys.h | 3 - src/coreclr/vm/clrtocomcall.cpp | 751 +----------------- src/coreclr/vm/clrtocomcall.h | 6 - src/coreclr/vm/dllimport.cpp | 204 +---- src/coreclr/vm/frames.cpp | 52 +- src/coreclr/vm/frames.h | 85 +- src/coreclr/vm/i386/asmhelpers.asm | 44 - src/coreclr/vm/prestub.cpp | 2 +- src/coreclr/vm/stubmgr.cpp | 80 +- src/coreclr/vm/stubmgr.h | 1 - .../StackWalk/FrameHandling/FrameIterator.cs | 3 - 24 files changed, 85 insertions(+), 1890 deletions(-) delete mode 100644 src/coreclr/vm/amd64/GenericCLRToCOMCallStubs.asm delete mode 100644 src/coreclr/vm/callsiteinspect.cpp delete mode 100644 src/coreclr/vm/callsiteinspect.h diff --git a/docs/design/datacontracts/StackWalk.md b/docs/design/datacontracts/StackWalk.md index 9e487282b92883..900dd1baa4d092 100644 --- a/docs/design/datacontracts/StackWalk.md +++ b/docs/design/datacontracts/StackWalk.md @@ -278,7 +278,6 @@ When updating the context from a TransitionFrame, the IP, SP, and all ABI specif The following Frame types also use this mechanism: * FramedMethodFrame -* CLRToCOMMethodFrame * PInvokeCallIFrame * PrestubMethodFrame * StubDispatchFrame diff --git a/src/coreclr/debug/daccess/dacdbiimpl.cpp b/src/coreclr/debug/daccess/dacdbiimpl.cpp index aebc7f56a78e06..5c1651f2a6401c 100644 --- a/src/coreclr/debug/daccess/dacdbiimpl.cpp +++ b/src/coreclr/debug/daccess/dacdbiimpl.cpp @@ -6127,7 +6127,7 @@ HRESULT STDMETHODCALLTYPE DacDbiInterfaceImpl::GetContext(VMPTR_Thread vmThread, // Going through thread Frames and looking for first (deepest one) one that // that has context available for stackwalking (SP and PC) - // For example: RedirectedThreadFrame, InlinedCallFrame, DynamicHelperFrame, CLRToCOMMethodFrame + // For example: RedirectedThreadFrame, InlinedCallFrame, DynamicHelperFrame Frame *frame = pThread->GetFrame(); while (frame != NULL && frame != FRAME_TOP) diff --git a/src/coreclr/debug/ee/controller.cpp b/src/coreclr/debug/ee/controller.cpp index 1c2455e5e902d9..37bbc8d65468d4 100644 --- a/src/coreclr/debug/ee/controller.cpp +++ b/src/coreclr/debug/ee/controller.cpp @@ -468,15 +468,12 @@ StackTraceTicket::StackTraceTicket(DebuggerUserBreakpoint * p) // caller wants information about a specific frame. // CONTEXT* pContext: A pointer to a CONTEXT structure. Can be null, // we use our temp context. -// bool suppressUMChainFromCLRToCOMMethodFrameGeneric - A ridiculous flag that is trying to narrowly -// target a fix for issue 650903. // StackTraceTicket - ticket to ensure that we actually have permission for this stacktrace void ControllerStackInfo::GetStackInfo( StackTraceTicket ticket, Thread *thread, FramePointer targetFP, - CONTEXT *pContext, - bool suppressUMChainFromCLRToCOMMethodFrameGeneric + CONTEXT *pContext ) { _ASSERTE(thread != NULL); @@ -504,7 +501,6 @@ void ControllerStackInfo::GetStackInfo( m_targetFP = targetFP; m_targetFrameFound = (m_targetFP == LEAF_MOST_FRAME); m_specialChainReason = CHAIN_NONE; - m_suppressUMChainFromCLRToCOMMethodFrameGeneric = suppressUMChainFromCLRToCOMMethodFrameGeneric; int result = DebuggerWalkStack(thread, LEAF_MOST_FRAME, @@ -565,26 +561,6 @@ StackWalkAction ControllerStackInfo::WalkStack(FrameInfo *pInfo, void *data) if (i->m_bottomFP == LEAF_MOST_FRAME) i->m_bottomFP = pInfo->fp; - // This is part of the targeted fix for issue 650903 (see the other - // parts in code:TrackUMChain and code:DebuggerStepper::TrapStepOut). - // - // pInfo->fIgnoreThisFrameIfSuppressingUMChainFromCLRToCOMMethodFrameGeneric has been - // set by TrackUMChain to help us remember that the current frame we're looking at is - // CLRToCOMMethodFrameGeneric (we can't rely on looking at pInfo->frame to check - // this), and i->m_suppressUMChainFromCLRToCOMMethodFrameGeneric has been set by the - // dude initiating this walk to remind us that our goal in life is to do a Step Out - // during managed-only debugging. These two things together tell us we should ignore - // this frame, rather than erroneously identifying it as the target frame. - // -#ifdef FEATURE_COMINTEROP - if(i->m_suppressUMChainFromCLRToCOMMethodFrameGeneric && - (pInfo->chainReason == CHAIN_ENTER_UNMANAGED) && - (pInfo->fIgnoreThisFrameIfSuppressingUMChainFromCLRToCOMMethodFrameGeneric)) - { - return SWA_CONTINUE; - } -#endif // FEATURE_COMINTEROP - //have we reached the correct frame yet? if (!i->m_targetFrameFound && IsEqualOrCloserToLeaf(i->m_targetFP, pInfo->fp)) @@ -5488,7 +5464,7 @@ InterpreterStepHelper::StepSetupResult InterpreterStepHelper::SetupStep( LOG((LF_CORDB, LL_INFO10000, "ISH::SS: skipIP == nextIP, only one patch needed\n")); } else - { + { LOG((LF_CORDB, LL_INFO10000, "ISH::SS: No skip IP for conditional branch!\n")); return SSR_Failed; } @@ -6909,15 +6885,7 @@ void DebuggerStepper::TrapStepOut(ControllerStackInfo *info, bool fForceTraditio // stack. StackTraceTicket ticket(info); - // The last parameter here is part of a really targeted (*cough* dirty) fix to - // disable getting an unwanted UMChain to fix issue 650903 (See - // code:ControllerStackInfo::WalkStack and code:TrackUMChain for the other - // parts.) In the case of managed step out we know that we aren't interested in - // unmanaged frames, and generating that unmanaged frame causes the stackwalker - // not to report the managed frame that was at the same SP. However the unmanaged - // frame might be used in the mixed-mode step out case so I don't suppress it - // there. - returnInfo.GetStackInfo(ticket, GetThread(), info->GetReturnFrame().fp, NULL, !(m_rgfMappingStop & STOP_UNMANAGED)); + returnInfo.GetStackInfo(ticket, GetThread(), info->GetReturnFrame().fp, NULL); info = &returnInfo; #ifdef _DEBUG @@ -7840,7 +7808,7 @@ TP_RESULT DebuggerStepper::TriggerPatch(DebuggerControllerPatch *patch, // With the addition of Async Thunks, it is now possible that an unjitted method trace // represents a stub. We need to check for this case and follow the stub trace to find - // the real target. + // the real target. // Replica patches do not contain a reference to the original trace so we can not rely // on the trace type == TRACE_UNJITTED_METHOD to identify this case. { @@ -7878,7 +7846,7 @@ TP_RESULT DebuggerStepper::TriggerPatch(DebuggerControllerPatch *patch, traceOk, traceOk ? trace.GetTraceType() : -1)); } - if (traceOk && + if (traceOk && g_pEEInterface->FollowTrace(&trace) && PatchTrace(&trace, info.m_activeFrame.fp, (m_rgfMappingStop & STOP_UNMANAGED) ? true : false)) @@ -8936,8 +8904,7 @@ TP_RESULT DebuggerThreadStarter::TriggerPatch(DebuggerControllerPatch *patch, else if ((patch->trace.GetTraceType() == TRACE_FRAME_PUSH) && (thread->GetFrame()->IsTransitionToNativeFrame())) { // If we've got a frame that is transitioning to native, there's no reason to try to keep tracing. So we - // bail early and save ourselves some effort. This also works around a problem where we deadlock trying to - // do too much work to determine the destination of a CLRToCOMMethodFrame. (See issue 87103.) + // bail early and save ourselves some effort. // // Note: trace call is still enabled, so we can just ignore this patch and wait for trace call to fire // again... diff --git a/src/coreclr/debug/ee/controller.h b/src/coreclr/debug/ee/controller.h index d55ac899d38717..21052da20b7010 100644 --- a/src/coreclr/debug/ee/controller.h +++ b/src/coreclr/debug/ee/controller.h @@ -187,15 +187,12 @@ class ControllerStackInfo // caller wants information about a specific frame. // CONTEXT* pContext: A pointer to a CONTEXT structure. Can be null, // we use our temp context. - // bool suppressUMChainFromCLRToCOMMethodFrameGeneric - A ridiculous flag that is trying to narrowly - // target a fix for issue 650903. // StackTraceTicket - ticket ensuring that we have permission to call this. void GetStackInfo( StackTraceTicket ticket, Thread *thread, FramePointer targetFP, - CONTEXT *pContext, - bool suppressUMChainFromCLRToCOMMethodFrameGeneric = false + CONTEXT *pContext ); //bool ControllerStackInfo::HasReturnFrame() Returns @@ -218,11 +215,6 @@ class ControllerStackInfo bool m_returnFound; FrameInfo m_returnFrame; - // A ridiculous flag that is targeting a very narrow fix at issue 650903 - // (4.5.1/Blue). This is set for the duration of a stackwalk designed to - // help us "Step Out" to a managed frame (i.e., managed-only debugging). - bool m_suppressUMChainFromCLRToCOMMethodFrameGeneric; - // Track if this stackwalk actually happened. // This is used by the StackTraceTicket(ControllerStackInfo * info) ticket. INDEBUG(bool m_dbgExecuted); diff --git a/src/coreclr/debug/ee/frameinfo.cpp b/src/coreclr/debug/ee/frameinfo.cpp index a170e23746813f..0fbda64d28189f 100644 --- a/src/coreclr/debug/ee/frameinfo.cpp +++ b/src/coreclr/debug/ee/frameinfo.cpp @@ -107,8 +107,6 @@ struct DebuggerFrameData this->info.fIsFunclet = false; this->info.fIsFilter = false; - this->info.fIgnoreThisFrameIfSuppressingUMChainFromCLRToCOMMethodFrameGeneric = false; - #if defined(_DEBUG) this->previousFP = LEAF_MOST_FRAME; #endif // _DEBUG @@ -1185,22 +1183,6 @@ StackWalkAction TrackUMChain(CrawlFrame *pCF, DebuggerFrameData *d) f.InitForUMChain(fpRoot, d->GetUMChainStartRD()); -#ifdef FEATURE_COMINTEROP - if ((frame != NULL) && - (frame->GetFrameIdentifier() == FrameIdentifier::CLRToCOMMethodFrame)) - { - // This condition is part of the fix for 650903. (See - // code:ControllerStackInfo::WalkStack and code:DebuggerStepper::TrapStepOut - // for the other parts.) Here, we know that the frame we're looking it may be - // a CLRToCOMMethodFrameGeneric (this info is not otherwise plubmed down into - // the walker; even though the walker does get to see "f.frame", that may not - // be "frame"). Given this, if the walker chooses to ignore these frames - // (while doing a Step Out during managed-only debugging), then it can ignore - // this frame. - f.fIgnoreThisFrameIfSuppressingUMChainFromCLRToCOMMethodFrameGeneric = true; - } -#endif // FEATURE_COMINTEROP - if (d->InvokeCallback(&f) == SWA_ABORT) { // don't need to cancel if they abort. diff --git a/src/coreclr/debug/ee/frameinfo.h b/src/coreclr/debug/ee/frameinfo.h index cabb8d67313f9f..b61843acf0dac1 100644 --- a/src/coreclr/debug/ee/frameinfo.h +++ b/src/coreclr/debug/ee/frameinfo.h @@ -100,15 +100,6 @@ struct FrameInfo bool IsFilterFrame() { return fIsFilter; } bool IsNonFilterFuncletFrame() { return (fIsFunclet && !fIsFilter); } - // A ridiculous flag that is targeting a very narrow fix at issue 650903 (4.5.1/Blue). - // This is set when the currently walked frame is a CLRToCOMMethodFrameGeneric. If the - // dude doing the walking is trying to ignore such frames (see - // code:ControllerStackInfo::m_suppressUMChainFromCLRToCOMMethodFrameGeneric), AND - // this is set, then the walker just continues on to the next frame, without - // erroneously identifying this frame as the target frame. Only used during "Step - // Out" to a managed frame (i.e., managed-only debugging). - bool fIgnoreThisFrameIfSuppressingUMChainFromCLRToCOMMethodFrameGeneric; - // In addition to a Method, a FrameInfo may also represent either a Chain or a Stub (but not both). // chainReason corresponds to ICorDebugChain::GetReason(). CorDebugChainReason chainReason; diff --git a/src/coreclr/vm/CMakeLists.txt b/src/coreclr/vm/CMakeLists.txt index 77851ac7f97452..72d9690d2135ee 100644 --- a/src/coreclr/vm/CMakeLists.txt +++ b/src/coreclr/vm/CMakeLists.txt @@ -410,7 +410,6 @@ set(VM_HEADERS_WKS baseassemblyspec.inl cachelinealloc.h callhelpers.h - callsiteinspect.h callconvbuilder.hpp cdacdata.h ceemain.h @@ -577,14 +576,6 @@ if(CLR_CMAKE_TARGET_WIN32) amsi.h ) - # COM interop scenarios - list(APPEND VM_SOURCES_DAC_AND_WKS_WIN32 - clrtocomcall.cpp - ) - list(APPEND VM_HEADERS_DAC_AND_WKS_WIN32 - clrtocomcall.h - ) - list(APPEND VM_SOURCES_WKS ${VM_SOURCES_DAC_AND_WKS_WIN32} # These should not be included for Linux @@ -603,7 +594,6 @@ if(CLR_CMAKE_TARGET_WIN32) # COM interop scenarios list(APPEND VM_SOURCES_WKS - callsiteinspect.cpp classcompat.cpp comcache.cpp comcallablewrapper.cpp @@ -611,6 +601,7 @@ if(CLR_CMAKE_TARGET_WIN32) cominterfacemarshaler.cpp commtmemberinfomap.cpp comtoclrcall.cpp + clrtocomcall.cpp dispatchinfo.cpp dispparammarshaler.cpp olecontexthelpers.cpp @@ -626,6 +617,7 @@ if(CLR_CMAKE_TARGET_WIN32) cominterfacemarshaler.h commtmemberinfomap.h comtoclrcall.h + clrtocomcall.h dispatchinfo.h dispparammarshaler.h olecontexthelpers.h @@ -662,7 +654,6 @@ elseif(CLR_CMAKE_TARGET_WIN32) ${ARCH_SOURCES_DIR}/CallDescrWorkerAMD64.asm ${ARCH_SOURCES_DIR}/ComCallPreStub.asm ${ARCH_SOURCES_DIR}/GenericComCallStubs.asm - ${ARCH_SOURCES_DIR}/GenericCLRToCOMCallStubs.asm ${ARCH_SOURCES_DIR}/getstate.asm ${ARCH_SOURCES_DIR}/JitHelpers_Fast.asm ${ARCH_SOURCES_DIR}/JitHelpers_FastWriteBarriers.asm diff --git a/src/coreclr/vm/FrameTypes.h b/src/coreclr/vm/FrameTypes.h index 1588290d9064cc..dd87294618efae 100644 --- a/src/coreclr/vm/FrameTypes.h +++ b/src/coreclr/vm/FrameTypes.h @@ -20,7 +20,6 @@ FRAME_TYPE_NAME(FuncEvalFrame) #endif // DEBUGGING_SUPPORTED #ifdef FEATURE_COMINTEROP FRAME_TYPE_NAME(ComMethodFrame) -FRAME_TYPE_NAME(CLRToCOMMethodFrame) FRAME_TYPE_NAME(ComPrestubMethodFrame) #endif // FEATURE_COMINTEROP FRAME_TYPE_NAME(PInvokeCalliFrame) diff --git a/src/coreclr/vm/amd64/GenericCLRToCOMCallStubs.asm b/src/coreclr/vm/amd64/GenericCLRToCOMCallStubs.asm deleted file mode 100644 index ae49ccdd78145e..00000000000000 --- a/src/coreclr/vm/amd64/GenericCLRToCOMCallStubs.asm +++ /dev/null @@ -1,43 +0,0 @@ -; Licensed to the .NET Foundation under one or more agreements. -; The .NET Foundation licenses this file to you under the MIT license. - -ifdef FEATURE_COMINTEROP - -include AsmMacros.inc -include asmconstants.inc - - -extern CLRToCOMWorker:proc - -NESTED_ENTRY GenericCLRToCOMCallStub, _TEXT - - PROLOG_WITH_TRANSITION_BLOCK 8 - - ; - ; Call CLRToCOMWorker. - ; - lea rcx, [rsp + __PWTB_TransitionBlock] ; pTransitionBlock - mov rdx, r10 ; MethodDesc * - call CLRToCOMWorker - - ; handle FP return values - - lea rcx, [rsp + __PWTB_FloatArgumentRegisters - 8] - cmp rax, 4 - jne @F - movss xmm0, real4 ptr [rcx] -@@: - cmp rax, 8 - jne @F - movsd xmm0, real8 ptr [rcx] -@@: - ; load return value - mov rax, [rcx] - - EPILOG_WITH_TRANSITION_BLOCK_RETURN - -NESTED_END GenericCLRToCOMCallStub, _TEXT - -endif ; FEATURE_COMINTEROP - - end diff --git a/src/coreclr/vm/amd64/cgencpu.h b/src/coreclr/vm/amd64/cgencpu.h index 63f7ccd0caf946..4edca696b7acaf 100644 --- a/src/coreclr/vm/amd64/cgencpu.h +++ b/src/coreclr/vm/amd64/cgencpu.h @@ -64,7 +64,6 @@ class ComCallMethodDesc; #define ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE 8 // bytes #define ENREGISTERED_PARAMTYPE_MAXSIZE 8 // bytes #define ENREGISTERED_RETURNTYPE_MAXSIZE 8 // bytes -#define COM_STUBS_SEPARATE_FP_LOCATIONS #define CALLDESCR_REGTYPEMAP 1 #endif diff --git a/src/coreclr/vm/arm64/asmhelpers.asm b/src/coreclr/vm/arm64/asmhelpers.asm index 8bdc98ab21ada3..eb91ccfae97cf8 100644 --- a/src/coreclr/vm/arm64/asmhelpers.asm +++ b/src/coreclr/vm/arm64/asmhelpers.asm @@ -208,42 +208,6 @@ NoFloatingPointRetVal LEAF_END -; ------------------------------------------------------------------ -; GenericCLRToCOMCallStub that erects a CLRToCOMMethodFrame and calls into the runtime -; (CLRToCOMWorker) to dispatch rare cases of the interface call. -; -; On entry: -; x0 : 'this' object -; x12 : Interface MethodDesc* -; plus user arguments in registers and on the stack -; -; On exit: -; x0/x1/s0-s3/d0-d3 set to return value of the call as appropriate -; - NESTED_ENTRY GenericCLRToCOMCallStub - - PROLOG_WITH_TRANSITION_BLOCK ASM_ENREGISTERED_RETURNTYPE_MAXSIZE - - add x0, sp, #__PWTB_TransitionBlock ; pTransitionBlock - mov x1, x12 ; pMethodDesc - - ; Call CLRToCOMWorker(TransitionBlock *, CLRToCOMCallMethodDesc *). - ; This call will set up the rest of the frame (including the vfptr, the GS cookie and - ; linking to the thread), make the client call and return with correct registers set - ; (x0/x1/s0-s3/d0-d3 as appropriate). - - bl CLRToCOMWorker - - ; x0 = fpRetSize - - ; The return value is stored before float argument registers - add x1, sp, #(__PWTB_FloatArgumentRegisters - ASM_ENREGISTERED_RETURNTYPE_MAXSIZE) - bl setStubReturnValue - - EPILOG_WITH_TRANSITION_BLOCK_RETURN - - NESTED_END - ; ------------------------------------------------------------------ ; COM to CLR stub called the first time a particular method is invoked. ; diff --git a/src/coreclr/vm/callsiteinspect.cpp b/src/coreclr/vm/callsiteinspect.cpp deleted file mode 100644 index 5b8d502f9bb64a..00000000000000 --- a/src/coreclr/vm/callsiteinspect.cpp +++ /dev/null @@ -1,518 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#include "common.h" -#include "object.h" -#include "callsiteinspect.h" - -namespace -{ - // Given a frame and value, get a reference to the object - OBJECTREF GetOBJECTREFFromStack( - _In_ FramedMethodFrame *frame, - _In_ PVOID val, - _In_ const CorElementType eType, - _In_ TypeHandle ty, - _In_ BOOL fIsByRef) - { - CONTRACT(OBJECTREF) - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(frame)); - PRECONDITION(CheckPointer(val)); - } - CONTRACT_END; - - // Value types like Nullable have special unboxing semantics - if (eType == ELEMENT_TYPE_VALUETYPE) - { - // box the value class - _ASSERTE(ty.GetMethodTable()->IsValueType() || ty.GetMethodTable()->IsEnum()); - - MethodTable* pMT = ty.GetMethodTable(); - - // What happens when the type contains a stack pointer? - _ASSERTE(!pMT->IsByRefLike()); - - PVOID* pVal = (PVOID *)val; - if (!fIsByRef) - { - val = StackElemEndiannessFixup(val, pMT->GetNumInstanceFieldBytes()); - pVal = &val; - } - - RETURN (pMT->FastBox(pVal)); - } - - switch (CorTypeInfo::GetGCType(eType)) - { - case TYPE_GC_NONE: - { - if (ELEMENT_TYPE_PTR == eType) - COMPlusThrow(kNotSupportedException); - - MethodTable *pMT = CoreLibBinder::GetElementType(eType); - - OBJECTREF pObj = pMT->Allocate(); - if (fIsByRef) - { - val = *((PVOID *)val); - } - else - { - val = StackElemEndiannessFixup(val, CorTypeInfo::Size(eType)); - } - - void *pDest = pObj->UnBox(); - -#ifdef COM_STUBS_SEPARATE_FP_LOCATIONS - if (!fIsByRef - && (ELEMENT_TYPE_R4 == eType || ELEMENT_TYPE_R8 == eType) - && frame != nullptr - && !TransitionBlock::IsStackArgumentOffset(static_cast((TADDR) val - frame->GetTransitionBlock()))) - { - if (ELEMENT_TYPE_R4 == eType) - *(UINT32*)pDest = (UINT32)FPSpillToR4(val); - else - *(UINT64*)pDest = (UINT64)FPSpillToR8(val); - } - else -#endif // COM_STUBS_SEPARATE_FP_LOCATIONS - { - memcpyNoGCRefs(pDest, val, CorTypeInfo::Size(eType)); - } - - RETURN (pObj); - } - - case TYPE_GC_REF: - if (fIsByRef) - val = *((PVOID *)val); - RETURN (ObjectToOBJECTREF(*(Object **)val)); - - default: - COMPlusThrow(kInvalidOperationException, W("InvalidOperation_TypeCannotBeBoxed")); - } - } - - struct ArgDetails - { - int Offset; - BOOL IsByRef; - CorElementType ElementType; - TypeHandle Type; - }; - - ArgDetails GetArgDetails( - _In_ FramedMethodFrame *frame, - _In_ ArgIterator &pArgIter) - { - CONTRACT(ArgDetails) - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - PRECONDITION(CheckPointer(frame)); - } - CONTRACT_END; - - ArgDetails details{}; - details.Offset = pArgIter.GetNextOffset(); - details.ElementType = pArgIter.GetArgType(); - -#ifdef COM_STUBS_SEPARATE_FP_LOCATIONS - // BUGBUG do we need to handle this? - if ((ELEMENT_TYPE_R4 == details.ElementType || ELEMENT_TYPE_R8 == details.ElementType) - && TransitionBlock::IsArgumentRegisterOffset(details.Offset)) - { - int iFPArg = TransitionBlock::GetArgumentIndexFromOffset(details.Offset); - details.Offset = static_cast(frame->GetFPArgOffset(iFPArg)); - } -#endif // COM_STUBS_SEPARATE_FP_LOCATIONS - - // Get the TypeHandle for the argument's type. - MetaSig *pSig = pArgIter.GetSig(); - details.Type = pSig->GetLastTypeHandleThrowing(); - - if (details.ElementType == ELEMENT_TYPE_BYREF) - { - details.IsByRef = TRUE; - // If this is a by-ref arg, GetOBJECTREFFromStack() will dereference "addr" to - // get the real argument address. Dereferencing now will open a gc hole if "addr" - // points into the gc heap, and we trigger gc between here and the point where - // we return the arguments. - - TypeHandle tycopy; - details.ElementType = pSig->GetByRefType(&tycopy); - if (details.ElementType == ELEMENT_TYPE_VALUETYPE) - details.Type = tycopy; - } -#ifdef ENREGISTERED_PARAMTYPE_MAXSIZE - else if (details.ElementType == ELEMENT_TYPE_VALUETYPE) - { - details.IsByRef = ArgIterator::IsArgPassedByRef(details.Type); - } -#endif // ENREGISTERED_PARAMTYPE_MAXSIZE - - RETURN (details); - } - - INT64 CopyOBJECTREFToStack( - _In_ OBJECTREF *src, - _In_opt_ PVOID pvDest, - _In_ CorElementType typ, - _In_ TypeHandle ty, - _In_ MetaSig *pSig, - _In_ BOOL fCopyClassContents) - { - // Use local to ensure proper alignment - INT64 ret = 0; - - CONTRACT(INT64) - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(pvDest, NULL_OK)); - PRECONDITION(CheckPointer(pSig)); - PRECONDITION(typ != ELEMENT_TYPE_VOID); - } - CONTRACT_END; - - if (fCopyClassContents) - { - // We have to copy the contents of a value class to pvDest - - // write unboxed version back to memory provided by the caller - if (pvDest) - { - if (ty.IsNull()) - ty = pSig->GetRetTypeHandleThrowing(); - - _ASSERTE((*src) != NULL || Nullable::IsNullableType(ty)); -#ifdef TARGET_UNIX -#error Non-Windows ABIs must be special cased -#endif - ty.GetMethodTable()->UnBoxIntoUnchecked(pvDest, (*src)); - - // return the object so it can be stored in the frame and - // propagated to the root set - *(Object**)&ret = OBJECTREFToObject(*src); - } - } - else if (CorTypeInfo::IsObjRef(typ)) - { - // We have a real OBJECTREF - - // Check if it is an OBJECTREF (from the GC heap) - if (pvDest) - SetObjectReference((OBJECTREF *)pvDest, *src); - - *(Object**)&ret = OBJECTREFToObject(*src); - } - else - { - // We have something that does not have a return buffer associated. - - // Note: this assert includes ELEMENT_TYPE_VALUETYPE because for enums, - // ArgIterator::HasRetBuffArg() returns 'false'. This occurs because the - // normalized type for enums is ELEMENT_TYPE_I4 even though - // MetaSig::GetReturnType() returns ELEMENT_TYPE_VALUETYPE. - // Almost all ELEMENT_TYPE_VALUETYPEs will go through the copy class - // contents codepath above. - // Also, CorTypeInfo::IsPrimitiveType() does not check for IntPtr, UIntPtr - // hence we have ELEMENT_TYPE_I and ELEMENT_TYPE_U. - _ASSERTE( - CorTypeInfo::IsPrimitiveType(typ) - || (typ == ELEMENT_TYPE_VALUETYPE) - || (typ == ELEMENT_TYPE_I) - || (typ == ELEMENT_TYPE_U) - || (typ == ELEMENT_TYPE_FNPTR)); - - // For a "ref int" arg, if a nasty sink replaces the boxed int with - // a null OBJECTREF, this is where we check. We need to be uniform - // in our policy w.r.t. this (throw vs ignore). - // The branch above throws. - if ((*src) != NULL) - { - PVOID srcData = (*src)->GetData(); - int cbsize = gElementTypeInfo[typ].m_cbSize; - decltype(ret) retBuff; - - // ElementTypeInfo.m_cbSize can be less than zero for cases that need - // special handling (e.g. value types) to be sure of the size (see siginfo.cpp). - // The type handle has the actual byte count, so we look there for such cases. - if (cbsize < 0) - { - if (ty.IsNull()) - ty = pSig->GetRetTypeHandleThrowing(); - - _ASSERTE(!ty.IsNull()); - cbsize = ty.GetSize(); - - // Assert the value class fits in the buffer - _ASSERTE(cbsize <= (int) sizeof(retBuff)); - - // Unbox value into a local buffer, this covers the Nullable case. - ty.GetMethodTable()->UnBoxIntoUnchecked(&retBuff, *src); - - srcData = &retBuff; - } - - if (pvDest) - memcpyNoGCRefs(pvDest, srcData, cbsize); - - // need to sign-extend signed types - bool fEndiannessFixup = false; - switch (typ) - { - case ELEMENT_TYPE_I1: - ret = *(INT8*)srcData; - fEndiannessFixup = true; - break; - case ELEMENT_TYPE_I2: - ret = *(INT16*)srcData; - fEndiannessFixup = true; - break; - case ELEMENT_TYPE_I4: - ret = *(INT32*)srcData; - fEndiannessFixup = true; - break; - default: - memcpyNoGCRefs(StackElemEndiannessFixup(&ret, cbsize), srcData, cbsize); - break; - } - -#if !defined(HOST_64BIT) && BIGENDIAN - if (fEndiannessFixup) - ret <<= 32; -#endif - } - } - - RETURN (ret); - } -} - -void CallsiteInspect::GetCallsiteArgs( - _In_ CallsiteDetails &callsite, - _Outptr_ PTRARRAYREF *args, - _Outptr_ BOOLARRAYREF *argsIsByRef, - _Outptr_ PTRARRAYREF *argsTypes) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(args)); - PRECONDITION(CheckPointer(argsIsByRef)); - PRECONDITION(CheckPointer(argsTypes)); - } - CONTRACTL_END; - - struct - { - PTRARRAYREF Args; - PTRARRAYREF ArgsTypes; - BOOLARRAYREF ArgsIsByRef; - OBJECTREF CurrArgType; - OBJECTREF CurrArg; - } gc; - gc.Args = NULL; - gc.ArgsTypes = NULL; - gc.ArgsIsByRef = NULL; - gc.CurrArgType = NULL; - gc.CurrArg = NULL; - GCPROTECT_BEGIN(gc); - { - // Ensure the sig is in a known state - callsite.MetaSig.Reset(); - - // scan the sig for the argument count - INT32 numArgs = callsite.MetaSig.NumFixedArgs(); - if (callsite.IsDelegate) - numArgs -= 2; // Delegates have 2 implicit additional arguments - - // Allocate all needed arrays for callsite arg details - gc.Args = (PTRARRAYREF)AllocateObjectArray(numArgs, g_pObjectClass); - MethodTable *typeMT = CoreLibBinder::GetClass(CLASS__TYPE); - gc.ArgsTypes = (PTRARRAYREF)AllocateObjectArray(numArgs, typeMT); - gc.ArgsIsByRef = (BOOLARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_BOOLEAN, numArgs); - - ArgIterator iter{ &callsite.MetaSig }; - for (int index = 0; index < numArgs; index++) - { - ArgDetails details = GetArgDetails(callsite.Frame, iter); - PVOID addr = (LPBYTE)callsite.Frame->GetTransitionBlock() + details.Offset; - - // How do we handle pointer types? - _ASSERTE(details.ElementType != ELEMENT_TYPE_PTR); - - gc.CurrArg = GetOBJECTREFFromStack( - callsite.Frame, - addr, - details.ElementType, - details.Type, - details.IsByRef); - - // Store argument - gc.Args->SetAt(index, gc.CurrArg); - - // Record the argument's type - gc.CurrArgType = details.Type.GetManagedClassObject(); - _ASSERTE(gc.CurrArgType != NULL); - gc.ArgsTypes->SetAt(index, gc.CurrArgType); - - // Record if the argument is ByRef - *((UCHAR*)gc.ArgsIsByRef->GetDataPtr() + index) = (!!details.IsByRef); - } - } - GCPROTECT_END(); - - // Return details - *args = gc.Args; - *argsTypes = gc.ArgsTypes; - *argsIsByRef = gc.ArgsIsByRef; -} - -void CallsiteInspect::PropagateOutParametersBackToCallsite( - _In_ PTRARRAYREF outArgs, - _In_ OBJECTREF retVal, - _In_ CallsiteDetails &callsite) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - - struct - { - OBJECTREF RetVal; - PTRARRAYREF OutArgs; - OBJECTREF CurrArg; - } gc; - gc.RetVal = retVal; - gc.OutArgs = outArgs; - gc.CurrArg = NULL; - GCPROTECT_BEGIN(gc); - { - FramedMethodFrame *frame = callsite.Frame; - const INT32 flags = callsite.Flags; - MetaSig *pSig = &callsite.MetaSig; - pSig->Reset(); // Ensure the sig is in a known state - - // Construct an ArgIterator from the sig - ArgIterator argit{ pSig }; - - // Propagate the return value only if the call is not a constructor call - // and the return type is non-void - if ((flags & CallsiteDetails::Ctor) == 0 - && pSig->GetReturnType() != ELEMENT_TYPE_VOID) - { - if (argit.HasRetBuffArg()) - { - // Copy from RetVal into the retBuff. - INT64 retVal = CopyOBJECTREFToStack( - &gc.RetVal, - *(void**)(frame->GetTransitionBlock() + argit.GetRetBuffArgOffset()), - pSig->GetReturnType(), - TypeHandle{}, - pSig, - TRUE /* should copy */); - - // Copy the return value - *(ARG_SLOT *)(frame->GetReturnValuePtr()) = retVal; - } -#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE - else if (argit.HasNonStandardByvalReturn() - && !(flags & CallsiteDetails::HResultReturn)) - { - // In these cases, put the pointer to the return buffer into - // the frame's return value slot. - CopyOBJECTREFToStack( - &gc.RetVal, - frame->GetReturnValuePtr(), - pSig->GetReturnType(), - TypeHandle(), - pSig, - TRUE /* should copy */); - } -#endif // ENREGISTERED_RETURNTYPE_MAXSIZE - else - { - // There is no separate return buffer, - // the retVal should fit in an INT64. - INT64 retVal = CopyOBJECTREFToStack( - &gc.RetVal, - nullptr, - pSig->GetReturnType(), - TypeHandle{}, - pSig, - FALSE /* should copy */); - - // Copy the return value - *(ARG_SLOT *)(frame->GetReturnValuePtr()) = retVal; - } - } - - // Refetch all the variables as GC could have happened - // after copying the return value. - UINT32 cOutArgs = (gc.OutArgs != NULL) ? gc.OutArgs->GetNumComponents() : 0; - if (cOutArgs > 0) - { - MetaSig syncSig{ callsite.MethodDesc }; - MetaSig *pSyncSig = nullptr; - - if (flags & CallsiteDetails::EndInvoke) - pSyncSig = &syncSig; - - PVOID *argAddr; - for (UINT32 i = 0; i < cOutArgs; ++i) - { - // Determine the address of the argument - if (pSyncSig) - { - CorElementType typ = pSyncSig->NextArg(); - if (typ == ELEMENT_TYPE_END) - break; - - if (typ != ELEMENT_TYPE_BYREF) - continue; - - argAddr = reinterpret_cast(frame->GetTransitionBlock() + argit.GetNextOffset()); - } - else - { - int ofs = argit.GetNextOffset(); - if (ofs == TransitionBlock::InvalidOffset) - break; - - if (argit.GetArgType() != ELEMENT_TYPE_BYREF) - continue; - - argAddr = reinterpret_cast(frame->GetTransitionBlock() + ofs); - } - - TypeHandle ty; - CorElementType brType = pSig->GetByRefType(&ty); - - gc.CurrArg = gc.OutArgs->GetAt(i); - CopyOBJECTREFToStack( - &gc.CurrArg, - *argAddr, - brType, - ty, - pSig, - ty.IsNull() ? FALSE : ty.IsValueType()); - } - } - } - GCPROTECT_END(); -} diff --git a/src/coreclr/vm/callsiteinspect.h b/src/coreclr/vm/callsiteinspect.h deleted file mode 100644 index eed2f722bebf32..00000000000000 --- a/src/coreclr/vm/callsiteinspect.h +++ /dev/null @@ -1,47 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#pragma once - -struct CallsiteDetails -{ - // The signature of the current call - class MetaSig MetaSig; - - // The current call frame - FramedMethodFrame *Frame; - - // The relevant method for the callsite - class MethodDesc *MethodDesc; - - // Is the callsite for a delegate - // Note the relevant method may _not_ be a delegate - BOOL IsDelegate; - - // Flags for callsite - enum - { - None = 0x0, - BeginInvoke = 0x01, - EndInvoke = 0x02, - Ctor = 0x04, - HResultReturn = 0x08, - }; - INT32 Flags; -}; - -namespace CallsiteInspect -{ - // Get all arguments and associated argument details at the supplied callsite - void GetCallsiteArgs( - _In_ CallsiteDetails &callsite, - _Outptr_ PTRARRAYREF *args, - _Outptr_ BOOLARRAYREF *argsIsByRef, - _Outptr_ PTRARRAYREF *argsTypes); - - // Properly propagate out parameters - void PropagateOutParametersBackToCallsite( - _In_ PTRARRAYREF outParams, - _In_ OBJECTREF retVal, - _In_ CallsiteDetails &callsite); -} diff --git a/src/coreclr/vm/cgensys.h b/src/coreclr/vm/cgensys.h index 580e563cb26808..59c451b0a90ba7 100644 --- a/src/coreclr/vm/cgensys.h +++ b/src/coreclr/vm/cgensys.h @@ -33,9 +33,6 @@ void CallJitEHFinally(CrawlFrame* pCf, BYTE* startPC, EE_ILEXCEPTION_CLAUSE *EHC #endif // TARGET_X86 #ifdef FEATURE_COMINTEROP -extern "C" UINT32 STDCALL CLRToCOMWorker(TransitionBlock * pTransitionBlock, CLRToCOMCallMethodDesc * pMD); -extern "C" void GenericCLRToCOMCallStub(void); - extern "C" void GenericComCallStub(void); #endif // FEATURE_COMINTEROP diff --git a/src/coreclr/vm/clrtocomcall.cpp b/src/coreclr/vm/clrtocomcall.cpp index 9096bd420b3295..ec8f6e042c92c3 100644 --- a/src/coreclr/vm/clrtocomcall.cpp +++ b/src/coreclr/vm/clrtocomcall.cpp @@ -23,32 +23,15 @@ #include "reflectioninvocation.h" #include "sigbuilder.h" #include "callconvbuilder.hpp" -#include "callsiteinspect.h" #include "method.hpp" -#define DISPATCH_INVOKE_SLOT 6 - -#ifndef DACCESS_COMPILE - -// -// dllimport.cpp -void CreateCLRToDispatchCOMStub( - MethodDesc * pMD, - DWORD dwStubFlags // PInvokeStubFlags - ); - -PCODE TheGenericCLRToCOMCallStub() -{ - LIMITED_METHOD_CONTRACT; - - return GetEEFuncEntryPoint(GenericCLRToCOMCallStub); -} - CLRToCOMCallInfo *CLRToCOMCall::PopulateCLRToCOMCallMethodDesc(MethodDesc* pMD, DWORD* pdwStubFlags) { CONTRACTL { - STANDARD_VM_CHECK; + THROWS; + GC_NOTRIGGER; + MODE_ANY; PRECONDITION(CheckPointer(pMD)); PRECONDITION(CheckPointer(pdwStubFlags, NULL_OK)); } @@ -209,30 +192,30 @@ namespace return pStubMD; } -} - -MethodDesc* CLRToCOMCall::GetILStubMethodDesc(MethodDesc* pMD, DWORD dwStubFlags) -{ - STANDARD_VM_CONTRACT; - // COM event stubs are very simple and don't go through any marshalling logic. - // We generate them as a regular IL stub outside of the P/Invoke system. - if (SF_IsCOMEventCallStub(dwStubFlags)) + MethodDesc* GetILStubMethodDesc(MethodDesc* pMD, DWORD dwStubFlags) { - _ASSERTE(pMD->IsCLRToCOMCall()); // no generic COM eventing - ((CLRToCOMCallMethodDesc *)pMD)->InitComEventCallInfo(); - return CreateEventCallStub(pMD); - } + STANDARD_VM_CONTRACT; - // Get the call signature information - StubSigDesc sigDesc(pMD); + // COM event stubs are very simple and don't go through any marshalling logic. + // We generate them as a regular IL stub outside of the P/Invoke system. + if (SF_IsCOMEventCallStub(dwStubFlags)) + { + _ASSERTE(pMD->IsCLRToCOMCall()); // no generic COM eventing + ((CLRToCOMCallMethodDesc *)pMD)->InitComEventCallInfo(); + return CreateEventCallStub(pMD); + } + + // Get the call signature information + StubSigDesc sigDesc(pMD); - return PInvoke::CreateCLRToNativeILStub( - &sigDesc, - (CorNativeLinkType)0, - (CorNativeLinkFlags)0, - CallConv::GetDefaultUnmanagedCallingConvention(), - dwStubFlags); + return PInvoke::CreateCLRToNativeILStub( + &sigDesc, + (CorNativeLinkType)0, + (CorNativeLinkFlags)0, + CallConv::GetDefaultUnmanagedCallingConvention(), + dwStubFlags); + } } PCODE CLRToCOMCall::GetStubForILStub(MethodDesc* pMD, MethodDesc** ppStubMD) @@ -245,690 +228,10 @@ PCODE CLRToCOMCall::GetStubForILStub(MethodDesc* pMD, MethodDesc** ppStubMD) DWORD dwStubFlags; CLRToCOMCallInfo* pComInfo = CLRToCOMCall::PopulateCLRToCOMCallMethodDesc(pMD, &dwStubFlags); - *ppStubMD = CLRToCOMCall::GetILStubMethodDesc(pMD, dwStubFlags); - - if (*ppStubMD != NULL) - { - PCODE pCode = JitILStub(*ppStubMD); - InterlockedCompareExchangeT(pComInfo->GetAddrOfILStubField(), pCode, NULL); - } - else - { - CreateCLRToDispatchCOMStub(pMD, dwStubFlags); - } - - PCODE pStub = NULL; - - if (*ppStubMD) - { - { - pStub = *pComInfo->GetAddrOfILStubField(); - } - } - else - { - pStub = TheGenericCLRToCOMCallStub(); - } - - return pStub; -} - -I4ARRAYREF SetUpWrapperInfo(MethodDesc *pMD) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(pMD)); - PRECONDITION(!pMD->IsAsyncMethod()); - } - CONTRACTL_END; - - MetaSig msig(pMD); - int numArgs = msig.NumFixedArgs(); - - I4ARRAYREF WrapperTypeArr = NULL; - - GCPROTECT_BEGIN(WrapperTypeArr) - { - // - // Allocate the array of wrapper types. - // - - WrapperTypeArr = (I4ARRAYREF)AllocatePrimitiveArray(ELEMENT_TYPE_I4, numArgs); - - GCX_PREEMP(); - - // Collects ParamDef information in an indexed array where element 0 represents - // the return type. - mdParamDef *params = (mdParamDef*)_alloca((numArgs+1) * sizeof(mdParamDef)); - CollateParamTokens(msig.GetModule()->GetMDImport(), pMD->GetMemberDef(), numArgs, params); - - - // - // Look up the best fit mapping info via Assembly & Interface level attributes - // - - BOOL BestFit = TRUE; - BOOL ThrowOnUnmappableChar = FALSE; - ReadBestFitCustomAttribute(pMD, &BestFit, &ThrowOnUnmappableChar); - - // - // Determine the wrapper type of the arguments. - // - - int iParam = 1; - CorElementType mtype; - while (ELEMENT_TYPE_END != (mtype = msig.NextArg())) - { - // - // Set up the marshaling info for the parameter. - // - - MarshalInfo Info(msig.GetModule(), msig.GetArgProps(), msig.GetSigTypeContext(), params[iParam], - MarshalInfo::MARSHAL_SCENARIO_COMINTEROP, (CorNativeLinkType)0, (CorNativeLinkFlags)0, - TRUE, iParam, numArgs, BestFit, ThrowOnUnmappableChar, FALSE, pMD, TRUE - #ifdef _DEBUG - , pMD->m_pszDebugMethodName, pMD->m_pszDebugClassName, iParam - #endif - ); - - DispatchWrapperType wrapperType = Info.GetDispWrapperType(); - - { - GCX_COOP(); - - // - // Based on the MarshalInfo, set the wrapper type. - // - - *((DWORD*)WrapperTypeArr->GetDataPtr() + iParam - 1) = wrapperType; - } - - // - // Increase the argument index. - // - - iParam++; - } - } - GCPROTECT_END(); - - return WrapperTypeArr; -} - -static CallsiteDetails CreateCallsiteDetails(_In_ FramedMethodFrame *pFrame) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pFrame)); - } - CONTRACTL_END; - - MethodDesc *pMD = pFrame->GetFunction(); - _ASSERTE(!pMD->ContainsGenericVariables() && pMD->IsRuntimeMethodHandle()); - - const BOOL fIsDelegate = pMD->GetMethodTable()->IsDelegate(); - _ASSERTE(!fIsDelegate && pMD->IsRuntimeMethodHandle()); - - MethodDesc *pDelegateMD = nullptr; - INT32 callsiteFlags = CallsiteDetails::None; - if (fIsDelegate) - { - // Gather details on the delegate itself - DelegateEEClass* delegateCls = (DelegateEEClass*)pMD->GetMethodTable()->GetClass(); - _ASSERTE(pFrame->GetThis()->GetMethodTable()->IsDelegate()); - - if (strcmp(pMD->GetName(), "BeginInvoke") == 0) - { - callsiteFlags |= CallsiteDetails::BeginInvoke; - } - else - { - _ASSERTE(strcmp(pMD->GetName(), "EndInvoke") == 0); - callsiteFlags |= CallsiteDetails::EndInvoke; - } - - pDelegateMD = pMD; - - // Get at the underlying method desc for this frame - pMD = COMDelegate::GetMethodDesc(pFrame->GetThis()); - _ASSERTE(pDelegateMD != nullptr - && pMD != nullptr - && !pMD->ContainsGenericVariables() - && pMD->IsRuntimeMethodHandle()); - } - - if (pMD->IsCtor()) - callsiteFlags |= CallsiteDetails::Ctor; - - Signature signature; - Module *pModule; - SigTypeContext typeContext; - - if (fIsDelegate) - { - _ASSERTE(pDelegateMD != nullptr); - signature = pDelegateMD->GetSignature(); - pModule = pDelegateMD->GetModule(); - - // If the delegate is generic, pDelegateMD may not represent the exact instantiation so we recover it from 'this'. - SigTypeContext::InitTypeContext(pFrame->GetThis()->GetMethodTable()->GetInstantiation(), Instantiation{}, &typeContext); - } - else if (pMD->IsVarArg()) - { - VASigCookie *pVACookie = pFrame->GetVASigCookie(); - signature = pVACookie->signature; - pModule = pVACookie->pModule; - SigTypeContext::InitTypeContext(&typeContext); - } - else - { - // COM doesn't support generics so the type is obvious - TypeHandle actualType = TypeHandle{ pMD->GetMethodTable() }; - - signature = pMD->GetSignature(); - pModule = pMD->GetModule(); - SigTypeContext::InitTypeContext(pMD, actualType, &typeContext); - } - - // If the signature is marked preserve sig, then the return - // is required to be an HRESULT, per COM rules. We set a flag to - // indicate this state to avoid issues when a C# developer defines - // an HRESULT in C# as a ValueClass with a single int field. This - // is convenient but does violate the COM ABI. Setting the flag - // lets us permit this convention and allow either a 4 byte primitive - // or the commonly used C# type "struct HResult { int Value; }". - if (IsMiPreserveSig(pMD->GetImplAttrs())) - callsiteFlags |= CallsiteDetails::HResultReturn; - - _ASSERTE(!signature.IsEmpty() && pModule != nullptr); - - // Create details - return CallsiteDetails{ { signature, pModule, &typeContext }, pFrame, pMD, fIsDelegate, callsiteFlags }; -} - -UINT32 CLRToCOMLateBoundWorker( - _In_ CLRToCOMMethodFrame *pFrame, - _In_ CLRToCOMCallMethodDesc *pMD) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - INJECT_FAULT(COMPlusThrowOM()); - PRECONDITION(CheckPointer(pFrame)); - PRECONDITION(CheckPointer(pMD)); - } - CONTRACTL_END; - - HRESULT hr; - - LOG((LF_STUBS, LL_INFO1000, "Calling CLRToCOMLateBoundWorker %s::%s \n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); - - // Retrieve the method table and the method desc of the call. - MethodTable *pItfMT = pMD->GetInterfaceMethodTable(); - CLRToCOMCallMethodDesc *pItfMD = pMD; - - // Make sure this is only called on IDispatch only interfaces. - _ASSERTE(pItfMT->GetComInterfaceType() == ifDispatch); - - // If this is a method impl MD then we need to retrieve the actual interface MD that - // this is a method impl for. - // REVISIT_TODO: Stop using ComSlot to convert method impls to interface MD - // _ASSERTE(pMD->m_pCLRToCOMCallInfo->m_cachedComSlot == 7); - if (!pMD->GetMethodTable()->IsInterface()) - { - const unsigned cbExtraSlots = 7; - pItfMD = (CLRToCOMCallMethodDesc*)pItfMT->GetMethodDescForSlot(pMD->m_pCLRToCOMCallInfo->m_cachedComSlot - cbExtraSlots); - CONSISTENCY_CHECK(pMD->GetInterfaceMD() == pItfMD); - } - - // Token of member to call - mdToken tkMember; - DWORD binderFlags = BINDER_AllLookup; - - // Property details - mdProperty propToken; - LPCUTF8 strMemberName; - ULONG uSemantic; - - // We should never see an async method here, as the async variant should go down - // the async stub path and call the non-async variant (which ends up here). - _ASSERTE(!pItfMD->IsAsyncMethod()); - - // See if there is property information for this member. - hr = pItfMT->GetMDImport()->GetPropertyInfoForMethodDef(pItfMD->GetMemberDef(), &propToken, &strMemberName, &uSemantic); - if (hr != S_OK) - { - // Non-property method - strMemberName = pItfMD->GetName(); - tkMember = pItfMD->GetMemberDef(); - binderFlags |= BINDER_InvokeMethod; - } - else - { - // Property accessor - tkMember = propToken; - - // Determine which type of accessor we are dealing with. - switch (uSemantic) - { - case msGetter: - { - // INVOKE_PROPERTYGET - binderFlags |= BINDER_GetProperty; - break; - } - - case msSetter: - { - // INVOKE_PROPERTYPUT or INVOKE_PROPERTYPUTREF - ULONG cAssoc; - ASSOCIATE_RECORD* pAssoc; - - IMDInternalImport *pMDImport = pItfMT->GetMDImport(); - - // Retrieve all the associates. - HENUMInternalHolder henum{ pMDImport }; - henum.EnumAssociateInit(propToken); - - cAssoc = henum.EnumGetCount(); - _ASSERTE(cAssoc > 0); - - ULONG allocSize = cAssoc * sizeof(*pAssoc); - if (allocSize < cAssoc) - COMPlusThrowHR(COR_E_OVERFLOW); - - pAssoc = (ASSOCIATE_RECORD*)_alloca((size_t)allocSize); - IfFailThrow(pMDImport->GetAllAssociates(&henum, pAssoc, cAssoc)); - - // Check to see if there is both a set and an other. If this is the case - // then the setter is a INVOKE_PROPERTYPUTREF otherwise we will make it a - // INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF. - bool propHasOther = false; - for (ULONG i = 0; i < cAssoc; i++) - { - if (pAssoc[i].m_dwSemantics == msOther) - { - propHasOther = true; - break; - } - } - - if (propHasOther) - { - // There is both a INVOKE_PROPERTYPUT and a INVOKE_PROPERTYPUTREF for this - // property. Therefore be specific and make this invoke a INVOKE_PROPERTYPUTREF. - binderFlags |= BINDER_PutRefDispProperty; - } - else - { - // Only a setter so make the invoke a set which maps to - // INVOKE_PROPERTYPUT | INVOKE_PROPERTYPUTREF. - binderFlags = BINDER_SetProperty; - } - break; - } - - case msOther: - { - // INVOKE_PROPERTYPUT - binderFlags |= BINDER_PutDispProperty; - break; - } - - default: - { - _ASSERTE(!"Invalid method semantic!"); - } - } - } - - // If the method has a void return type, then set the IgnoreReturn binding flag. - if (pItfMD->IsVoid()) - binderFlags |= BINDER_IgnoreReturn; - - UINT32 fpRetSize = 0; - - struct - { - OBJECTREF MemberName; - OBJECTREF ItfTypeObj; - OBJECTREF ThisObj; - PTRARRAYREF Args; - BOOLARRAYREF ArgsIsByRef; - PTRARRAYREF ArgsTypes; - OBJECTREF ArgsWrapperTypes; - OBJECTREF RetValType; - OBJECTREF RetVal; - } gc; - gc.MemberName = NULL; - gc.ItfTypeObj = NULL; - gc.ThisObj = NULL; - gc.Args = NULL; - gc.ArgsIsByRef = NULL; - gc.ArgsTypes = NULL; - gc.ArgsWrapperTypes = NULL; - gc.RetValType = NULL; - gc.RetVal = NULL; - GCPROTECT_BEGIN(gc); - { - // Retrieve the exposed type object for the interface. - gc.ItfTypeObj = pItfMT->GetManagedClassObject(); - - // Retrieve the name of the target member. If the member - // has a DISPID then use that to optimize the invoke. - DISPID dispId = DISPID_UNKNOWN; - hr = pItfMD->GetMDImport()->GetDispIdOfMemberDef(tkMember, (ULONG*)&dispId); - if (hr == S_OK) - { - WCHAR strTmp[ARRAY_SIZE(DISPID_NAME_FORMAT_STRING) + MaxUnsigned32BitDecString]; - _snwprintf_s(strTmp, ARRAY_SIZE(strTmp), _TRUNCATE, DISPID_NAME_FORMAT_STRING, dispId); - gc.MemberName = StringObject::NewString(strTmp); - } - else - { - gc.MemberName = StringObject::NewString(strMemberName); - } - - CallsiteDetails callsite = CreateCallsiteDetails(pFrame); - - // Arguments - CallsiteInspect::GetCallsiteArgs(callsite, &gc.Args, &gc.ArgsIsByRef, &gc.ArgsTypes); - - // If call requires object wrapping, set up the array of wrapper types. - if (pMD->RequiresArgumentWrapping()) - gc.ArgsWrapperTypes = SetUpWrapperInfo(pItfMD); - - // Return type - TypeHandle retValHandle = callsite.MetaSig.GetRetTypeHandleThrowing(); - gc.RetValType = retValHandle.GetManagedClassObject(); - gc.ThisObj = pFrame->GetThis(); - - // the return value is written into the Frame's neginfo, so we don't - // need to return it directly. We can just have the stub do that work. - // However, the stub needs to know what type of FP return this is, if - // any, so we return the return size info as the return value. - if (callsite.MetaSig.HasFPReturn()) - { - callsite.MetaSig.Reset(); - ArgIterator argit{ &callsite.MetaSig }; - fpRetSize = argit.GetFPReturnSize(); - _ASSERTE(fpRetSize > 0); - } - - // Create a call site for the invoke - UnmanagedCallersOnlyCaller forwardCallToInvoke(METHOD__CLASS__FORWARD_CALL_TO_INVOKE); - - // Invoke the method - forwardCallToInvoke.InvokeThrowing( - &gc.ItfTypeObj, - &gc.MemberName, - (INT32)binderFlags, - &gc.ThisObj, - &gc.Args, - &gc.ArgsIsByRef, - &gc.ArgsWrapperTypes, - &gc.ArgsTypes, - &gc.RetValType, - &gc.RetVal); - - // Ensure all outs and return values are moved back to the current callsite - CallsiteInspect::PropagateOutParametersBackToCallsite(gc.Args, gc.RetVal, callsite); - } - GCPROTECT_END(); - - return fpRetSize; -} - -// calls that propagate from CLR to COM - -#pragma optimize( "y", off ) -/*static*/ -UINT32 STDCALL CLRToCOMWorker(TransitionBlock * pTransitionBlock, CLRToCOMCallMethodDesc * pMD) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - ENTRY_POINT; - PRECONDITION(CheckPointer(pTransitionBlock, NULL_NOT_OK)); - } - CONTRACTL_END; - - UINT32 returnValue = 0; - - // This must happen before the UnC handler is setup. Otherwise, an exception will - // cause the UnC handler to pop this frame, leaving a GC hole a mile wide. - - MAKE_CURRENT_THREAD_AVAILABLE(); - - CLRToCOMMethodFrame frame(pTransitionBlock, pMD); - CLRToCOMMethodFrame * pFrame = &frame; - - //we need to zero out the return value buffer because we will report it during GC -#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE - ZeroMemory (pFrame->GetReturnValuePtr(), ENREGISTERED_RETURNTYPE_MAXSIZE); -#else - *(ARG_SLOT *)pFrame->GetReturnValuePtr() = 0; -#endif - - // Link frame into the chain. - pFrame->Push(CURRENT_THREAD); - - INSTALL_UNWIND_AND_CONTINUE_HANDLER - - _ASSERTE(pMD->IsCLRToCOMCall()); - - // Make sure we have been properly loaded here - CONSISTENCY_CHECK(GetAppDomain()->CheckCanExecuteManagedCode(pMD)); - - // Retrieve the interface method table. - MethodTable *pItfMT = pMD->GetInterfaceMethodTable(); - - // COM event calls should be handled by an IL stub. - _ASSERTE(!pItfMT->IsComEventItfType()); - if (pItfMT->GetComInterfaceType() == ifDispatch) - { - // If the interface is a Dispatch only interface then convert the early bound - // call to a late bound call. - returnValue = CLRToCOMLateBoundWorker(pFrame, pMD); - } - else - { - LOG((LF_STUBS, LL_INFO1000, "Calling CLRToCOMWorker %s::%s \n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); - - CONSISTENCY_CHECK_MSG(false, "Should not get here when using IL stubs."); - } - - UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; - - pFrame->Pop(CURRENT_THREAD); - - return returnValue; -} - -#pragma optimize( "", on ) - -#endif // #ifndef DACCESS_COMPILE - -//--------------------------------------------------------- -// Debugger support for CLRToCOMMethodFrame -//--------------------------------------------------------- -TADDR CLRToCOMCall::GetFrameCallIP(FramedMethodFrame *frame) -{ - CONTRACT (TADDR) - { - WRAPPER(THROWS); - WRAPPER(GC_TRIGGERS); - MODE_ANY; - PRECONDITION(CheckPointer(frame)); - POSTCONDITION(CheckPointer((void*)RETVAL, NULL_OK)); - } - CONTRACT_END; - - CLRToCOMCallMethodDesc *pCMD = dac_cast(frame->GetFunction()); - MethodTable *pItfMT = pCMD->GetInterfaceMethodTable(); - TADDR ip = NULL; -#ifndef DACCESS_COMPILE - SafeComHolder pUnk = NULL; -#endif - - _ASSERTE(pCMD->IsCLRToCOMCall()); - - // Note: if this is a COM event call, then the call will be delegated to a different object. The logic below will - // fail with an invalid cast error. For V1, we just won't step into those. - if (pItfMT->IsComEventItfType()) - RETURN NULL; - - // - // This is called from some strange places - from - // unmanaged code, from managed code, from the debugger - // helper thread. Make sure we can deal with this object - // ref. - // - -#ifndef DACCESS_COMPILE - - Thread* thread = GetThreadNULLOk(); - if (thread == NULL) - { - // - // This is being called from the debug helper thread. - // Unfortunately this doesn't bode well for the CLR IP - // mapping code - it expects to be called from the appropriate - // context. - // - // This context-naive code will work for most cases. - // - // It toggles the GC mode, tries to setup a thread, etc, right after our - // verification that we have no Thread object above. This needs to be fixed properly in Beta 2. This is a work - // around for Beta 1, which is just to #if 0 the code out and return NULL. - // - pUnk = NULL; - } - else - { - GCX_COOP(); - - OBJECTREF *pOref = frame->GetThisPtr(); - pUnk = ComObject::GetComIPFromRCWThrowing(pOref, pItfMT); - } - - if (pUnk != NULL) - { - if (pItfMT->GetComInterfaceType() == ifDispatch) - ip = (TADDR)(*(void ***)(IUnknown*)pUnk)[DISPATCH_INVOKE_SLOT]; - else - ip = (TADDR)(*(void ***)(IUnknown*)pUnk)[pCMD->m_pCLRToCOMCallInfo->m_cachedComSlot]; - } - -#else - DacNotImpl(); -#endif // #ifndef DACCESS_COMPILE - - RETURN ip; -} - -void CLRToCOMMethodFrame::GetUnmanagedCallSite_Impl(TADDR* ip, - TADDR* returnIP, - TADDR* returnSP) -{ - CONTRACTL - { - WRAPPER(THROWS); - WRAPPER(GC_TRIGGERS); - MODE_ANY; - PRECONDITION(CheckPointer(ip, NULL_OK)); - PRECONDITION(CheckPointer(returnIP, NULL_OK)); - PRECONDITION(CheckPointer(returnSP, NULL_OK)); - } - CONTRACTL_END; - - LOG((LF_CORDB, LL_INFO100000, "CLRToCOMMethodFrame::GetUnmanagedCallSite\n")); - - if (ip != NULL) - *ip = CLRToCOMCall::GetFrameCallIP(this); - - TADDR retSP = NULL; - // We can't assert retSP here because the debugger may actually call this function even when - // the frame is not fully initiailzed. It is ok because the debugger has code to handle this - // case. However, other callers may not be tolerant of this case, so we should push this assert - // to the callers - //_ASSERTE(retSP != NULL); - - if (returnIP != NULL) - { - *returnIP = retSP ? *(TADDR*)retSP : NULL; - } - - if (returnSP != NULL) - { - *returnSP = retSP; - } - -} - - - -BOOL CLRToCOMMethodFrame::TraceFrame_Impl(Thread *thread, BOOL fromPatch, - TraceDestination *trace, REGDISPLAY *regs) -{ - CONTRACTL - { - WRAPPER(THROWS); - WRAPPER(GC_TRIGGERS); - MODE_ANY; - PRECONDITION(CheckPointer(thread)); - PRECONDITION(CheckPointer(trace)); - } - CONTRACTL_END; - - // - // Get the call site info - // - -#if defined(HOST_64BIT) - // Interop debugging is currently not supported on WIN64, so we always return FALSE. - // The result is that you can't step into an unmanaged frame or step out to one. You - // also can't step a breakpoint in one. - return FALSE; -#endif // HOST_64BIT - - TADDR ip, returnIP, returnSP; - GetUnmanagedCallSite(&ip, &returnIP, &returnSP); - - // - // If we've already made the call, we can't trace any more. - // - // !!! Note that this test isn't exact. - // - - if (!fromPatch && - (dac_cast(thread->GetFrame()) != dac_cast(this) || - !thread->m_fPreemptiveGCDisabled || - *PTR_TADDR(returnSP) == returnIP)) - { - LOG((LF_CORDB, LL_INFO10000, "CLRToCOMMethodFrame::TraceFrame: can't trace...\n")); - return FALSE; - } - - // - // Otherwise, return the unmanaged destination. - // - - trace->InitForUnmanaged(ip); + *ppStubMD = GetILStubMethodDesc(pMD, dwStubFlags); - LOG((LF_CORDB, LL_INFO10000, - "CLRToCOMMethodFrame::TraceFrame: ip=0x%p\n", ip)); + PCODE pCode = JitILStub(*ppStubMD); + InterlockedCompareExchangeT(pComInfo->GetAddrOfILStubField(), pCode, NULL); - return TRUE; + return *pComInfo->GetAddrOfILStubField(); } diff --git a/src/coreclr/vm/clrtocomcall.h b/src/coreclr/vm/clrtocomcall.h index 4c3189fb5d2f98..acaaf1db74bde6 100644 --- a/src/coreclr/vm/clrtocomcall.h +++ b/src/coreclr/vm/clrtocomcall.h @@ -21,12 +21,6 @@ class CLRToCOMCall { public: - //--------------------------------------------------------- - // Debugger helper function - //--------------------------------------------------------- - static TADDR GetFrameCallIP(FramedMethodFrame *frame); - - static MethodDesc* GetILStubMethodDesc(MethodDesc* pMD, DWORD dwStubFlags); static PCODE GetStubForILStub(MethodDesc* pMD, MethodDesc** ppStubMD); static CLRToCOMCallInfo *PopulateCLRToCOMCallMethodDesc(MethodDesc* pMD, DWORD* pdwStubFlags); diff --git a/src/coreclr/vm/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 69d1006d75012c..dbc1fd5b892d38 100644 --- a/src/coreclr/vm/dllimport.cpp +++ b/src/coreclr/vm/dllimport.cpp @@ -238,30 +238,11 @@ static bool IsSharedStubScenario(DWORD dwStubFlags) return true; } -class StubState +class ILStubState { public: - virtual void SetLastError(BOOL fSetLastError) = 0; - virtual void BeginEmit(DWORD dwStubFlags) = 0; - virtual void MarshalReturn(MarshalInfo* pInfo, int argOffset) = 0; - virtual void MarshalArgument(MarshalInfo* pInfo, int argOffset) = 0; - virtual void MarshalLCID(int argIdx) = 0; - virtual void MarshalField(MarshalInfo* pInfo, UINT32 managedOffset, UINT32 nativeOffset, FieldDesc* pFieldDesc) = 0; - - virtual void EmitInvokeTarget(MethodDesc* pTargetMD, MethodDesc* pStubMD) = 0; - - virtual void FinishEmit(MethodDesc* pMD) = 0; - - virtual ~StubState() - { - LIMITED_METHOD_CONTRACT; - } -}; - -class ILStubState : public StubState -{ + virtual ~ILStubState() = default; protected: - ILStubState( Module* pStubModule, const Signature &signature, @@ -289,7 +270,7 @@ class ILStubState : public StubState } public: - void SetLastError(BOOL fSetLastError) + virtual void SetLastError(BOOL fSetLastError) { LIMITED_METHOD_CONTRACT; @@ -309,14 +290,14 @@ class ILStubState : public StubState // The second "emittable" ILCodeLabel is at the beginning of the post linker. It is used to emit code which is // not safe to run in the case of an exception. The rest of the post linker is wrapped in a finally, and it contains // with the necessary clean-up which should be executed in both normal and exception cases. - void BeginEmit(DWORD dwStubFlags) + virtual void BeginEmit(DWORD dwStubFlags) { WRAPPER_NO_CONTRACT; m_slIL.Begin(dwStubFlags); _ASSERTE(m_dwStubFlags == dwStubFlags); } - void MarshalReturn(MarshalInfo* pInfo, int argOffset) + virtual void MarshalReturn(MarshalInfo* pInfo, int argOffset) { CONTRACTL { @@ -332,7 +313,7 @@ class ILStubState : public StubState SF_IsHRESULTSwapping(m_dwStubFlags)); } - void MarshalArgument(MarshalInfo* pInfo, int argOffset) + virtual void MarshalArgument(MarshalInfo* pInfo, int argOffset) { CONTRACTL { @@ -344,7 +325,7 @@ class ILStubState : public StubState pInfo->GenerateArgumentIL(&m_slIL, argOffset, SF_IsForwardStub(m_dwStubFlags)); } - void MarshalField(MarshalInfo* pInfo, UINT32 managedOffset, UINT32 nativeOffset, FieldDesc* pFieldDesc) + virtual void MarshalField(MarshalInfo* pInfo, UINT32 managedOffset, UINT32 nativeOffset, FieldDesc* pFieldDesc) { CONTRACTL { @@ -370,7 +351,7 @@ class ILStubState : public StubState } #endif // FEATURE_COMINTEROP - void MarshalLCID(int argIdx) + virtual void MarshalLCID(int argIdx) { STANDARD_VM_CONTRACT; @@ -529,7 +510,7 @@ class ILStubState : public StubState pStubMD->AsDynamicMethodDesc()->SetStoredMethodSig(pNewSig, cbNewSig); } - void EmitInvokeTarget(MethodDesc* pTargetMD, MethodDesc* pStubMD) + virtual void EmitInvokeTarget(MethodDesc* pTargetMD, MethodDesc* pStubMD) { STANDARD_VM_CONTRACT; @@ -646,7 +627,7 @@ class ILStubState : public StubState } #ifndef DACCESS_COMPILE - void FinishEmit(MethodDesc* pStubMD) + virtual void FinishEmit(MethodDesc* pStubMD) { STANDARD_VM_CONTRACT; @@ -2778,105 +2759,6 @@ void PInvokeStubLinker::EmitLoadStubContext(ILCodeStream* pcsEmit, DWORD dwStubF pcsEmit->EmitCALL(METHOD__STUBHELPERS__GET_STUB_CONTEXT, 0, 1); } -#ifdef FEATURE_COMINTEROP - -class DispatchStubState : public StubState // For CLR-to-COM late-bound/eventing calls -{ -public: - DispatchStubState() - : m_dwStubFlags(0), - m_lateBoundFlags(0) - { - WRAPPER_NO_CONTRACT; - } - - void SetLastError(BOOL fSetLastError) - { - LIMITED_METHOD_CONTRACT; - - CONSISTENCY_CHECK(!fSetLastError); - } - - void BeginEmit(DWORD dwStubFlags) - { - LIMITED_METHOD_CONTRACT; - - CONSISTENCY_CHECK(SF_IsCOMStub(dwStubFlags)); - m_dwStubFlags = dwStubFlags; - } - - void MarshalReturn(MarshalInfo* pInfo, int argOffset) - { - CONTRACTL - { - STANDARD_VM_CHECK; - - PRECONDITION(CheckPointer(pInfo)); - } - CONTRACTL_END; - } - - void MarshalArgument(MarshalInfo* pInfo, int argOffset) - { - CONTRACTL - { - STANDARD_VM_CHECK; - PRECONDITION(CheckPointer(pInfo)); - } - CONTRACTL_END; - - if (SF_IsCOMLateBoundStub(m_dwStubFlags) && pInfo->GetDispWrapperType() != 0) - { - m_lateBoundFlags |= CLRToCOMCallInfo::kRequiresArgumentWrapping; - } - } - - void MarshalLCID(int argIdx) - { - LIMITED_METHOD_CONTRACT; - } - - void MarshalField(MarshalInfo* pInfo, UINT32 managedOffset, UINT32 nativeOffset, FieldDesc* pFieldDesc) - { - LIMITED_METHOD_CONTRACT; - UNREACHABLE(); - } - -#ifdef FEATURE_COMINTEROP - void MarshalHiddenLengthArgument(MarshalInfo *, BOOL) - { - LIMITED_METHOD_CONTRACT; - } - void MarshalFactoryReturn() - { - LIMITED_METHOD_CONTRACT; - UNREACHABLE(); - } -#endif // FEATURE_COMINTEROP - - void EmitInvokeTarget(MethodDesc* pTargetMD, MethodDesc* pStubMD) - { - LIMITED_METHOD_CONTRACT; - UNREACHABLE_MSG("Should never come to DispatchStubState::EmitInvokeTarget"); - } - - void FinishEmit(MethodDesc *pMD) - { - STANDARD_VM_CONTRACT; - - // set flags directly on the interop MD - _ASSERTE(pMD->IsCLRToCOMCall()); - - ((CLRToCOMCallMethodDesc *)pMD)->SetLateBoundFlags(m_lateBoundFlags); - } - -protected: - DWORD m_dwStubFlags; - BYTE m_lateBoundFlags; // CLRToCOMCallMethodDesc::Flags -}; - -#endif // FEATURE_COMINTEROP - namespace { // Use CorInfoCallConvExtension::Managed as a sentinel represent a user-provided WinApi calling convention. @@ -3867,7 +3749,7 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, CorNativeLinkType nlType, CorNativeLinkFlags nlFlags, UINT argidx, // this is used for reverse pinvoke hresult swapping - StubState* pss, + ILStubState* pss, int argOffset, DWORD dwStubFlags, MethodDesc *pMD, @@ -4004,7 +3886,7 @@ static MarshalInfo::MarshalType DoMarshalReturnValue(MetaSig& msig, // Note that this function may now throw if it fails to create // a stub. //--------------------------------------------------------- -static void CreatePInvokeStubWorker(StubState* pss, +static void CreatePInvokeStubWorker(ILStubState* pss, StubSigDesc* pSigDesc, CorNativeLinkType nlType, CorNativeLinkFlags nlFlags, @@ -6207,68 +6089,6 @@ PCODE GetStubForInteropMethod(MethodDesc* pMD, DWORD dwStubFlags) RETURN pStub; } -#ifdef FEATURE_COMINTEROP -void CreateCLRToDispatchCOMStub( - MethodDesc * pMD, - DWORD dwStubFlags) // PInvokeStubFlags -{ - CONTRACTL - { - STANDARD_VM_CHECK; - - PRECONDITION(CheckPointer(pMD)); - } - CONTRACTL_END; - - _ASSERTE(SF_IsCOMLateBoundStub(dwStubFlags) || SF_IsCOMEventCallStub(dwStubFlags)); - - // If we are dealing with a COM event call, then we need to initialize the - // COM event call information. - if (SF_IsCOMEventCallStub(dwStubFlags)) - { - _ASSERTE(pMD->IsCLRToCOMCall()); // no generic COM eventing - ((CLRToCOMCallMethodDesc *)pMD)->InitComEventCallInfo(); - } - - // Get the call signature information - StubSigDesc sigDesc(pMD); - - int iLCIDArg = 0; - int numArgs = 0; - int numParamTokens = 0; - mdParamDef* pParamTokenArray = NULL; - - CreatePInvokeStubAccessMetadata(&sigDesc, - CallConv::GetDefaultUnmanagedCallingConvention(), - &dwStubFlags, - &iLCIDArg, - &numArgs); - - numParamTokens = numArgs + 1; - pParamTokenArray = (mdParamDef*)_alloca(numParamTokens * sizeof(mdParamDef)); - CollateParamTokens(sigDesc.m_pModule->GetMDImport(), sigDesc.m_tkMethodDef, numArgs, pParamTokenArray); - - DispatchStubState MyStubState; - - CreatePInvokeStubWorker(&MyStubState, - &sigDesc, - (CorNativeLinkType)0, - (CorNativeLinkFlags)0, - CallConv::GetDefaultUnmanagedCallingConvention(), - dwStubFlags | PINVOKESTUB_FL_COM, - pMD, - pParamTokenArray, - iLCIDArg); - - _ASSERTE(pMD->IsCLRToCOMCall()); // no generic disp-calls - -#ifdef TARGET_X86 - ((CLRToCOMCallMethodDesc *)pMD)->InitStackPop(); -#endif -} - -#endif // FEATURE_COMINTEROP - VOID PInvokeMethodDesc::SetPInvokeTarget(LPVOID pTarget) { CONTRACTL diff --git a/src/coreclr/vm/frames.cpp b/src/coreclr/vm/frames.cpp index ccf26305f7e158..7cf791833e2d68 100644 --- a/src/coreclr/vm/frames.cpp +++ b/src/coreclr/vm/frames.cpp @@ -1732,59 +1732,9 @@ PInvokeCalliFrame::PInvokeCalliFrame(TransitionBlock * pTransitionBlock, VASigCo } #endif // #ifndef DACCESS_COMPILE -#ifdef FEATURE_COMINTEROP - -#ifndef DACCESS_COMPILE -CLRToCOMMethodFrame::CLRToCOMMethodFrame(TransitionBlock * pTransitionBlock, MethodDesc * pMD) - : FramedMethodFrame(FrameIdentifier::CLRToCOMMethodFrame, pTransitionBlock, pMD) -{ - LIMITED_METHOD_CONTRACT; -} -#endif // #ifndef DACCESS_COMPILE - -void CLRToCOMMethodFrame::GcScanRoots_Impl(promote_func* fn, ScanContext* sc) -{ - WRAPPER_NO_CONTRACT; - - // CLRToCOMMethodFrame is only used in the event call / late bound call code path where we do not have IL stub - // so we need to promote the arguments and return value manually. - - FramedMethodFrame::GcScanRoots_Impl(fn, sc); - PromoteCallerStack(fn, sc); - - // - // Promote the returned object - // - - MetaSig sig(GetFunction()); - - TypeHandle thValueType; - CorElementType et = sig.GetReturnTypeNormalized(&thValueType); - if (CorTypeInfo::IsObjRef_NoThrow(et)) - { - (*fn)(GetReturnObjectPtr(), sc, CHECK_APP_DOMAIN); - } - else if (CorTypeInfo::IsByRef_NoThrow(et)) - { - PromoteCarefully(fn, GetReturnObjectPtr(), sc, GC_CALL_INTERIOR | CHECK_APP_DOMAIN); - } - else if (et == ELEMENT_TYPE_VALUETYPE) - { - ArgIterator argit(&sig); - if (!argit.HasRetBuffArg()) - { -#ifdef TARGET_UNIX -#error Non-Windows ABIs must be special cased -#endif - ReportPointersFromValueType(fn, sc, thValueType.AsMethodTable(), GetReturnObjectPtr()); - } - } -} -#endif // FEATURE_COMINTEROP - #if defined (_DEBUG) && !defined (DACCESS_COMPILE) // For IsProtectedByGCFrame, we need to know whether a given object ref is protected -// by a CLRToCOMMethodFrame or a ComMethodFrame. Since GCScanRoots for those frames are +// by a ComMethodFrame. Since GCScanRoots for those frames are // quite complicated, we don't want to duplicate their logic so we call GCScanRoots with // IsObjRefProtected (a fake promote function) and an extended ScanContext to do the checking. diff --git a/src/coreclr/vm/frames.h b/src/coreclr/vm/frames.h index 55072025229b0f..d5f25c9b0264ca 100644 --- a/src/coreclr/vm/frames.h +++ b/src/coreclr/vm/frames.h @@ -63,11 +63,6 @@ // | +-FramedMethodFrame - this abstract frame represents a call to a method // | | that generates a full-fledged frame. // | | -#ifdef FEATURE_COMINTEROP -// | | -// | +-CLRToCOMMethodFrame - represents a CLR to COM call using the generic worker -// | | -#endif //FEATURE_COMINTEROP // | | // | +-PInvokeCalliFrame - protects arguments when a call to GetILStubForCalli is made // | | to get or create IL stub for an unmanaged CALLI @@ -137,10 +132,8 @@ Delegate over a native function pointer: the first time a call via the corresponding VASigCookie is made. ClrToCom: - Late-bound or eventing: The stub is generated by GenerateGenericComplusWorker - (x86) or exists statically as GenericCLRToCOMCallStub[RetBuffArg] (64-bit), - and it erects a CLRToCOMMethodFrame frame. - Early-bound: The stub does not erect any frames explicitly but contains an + Eventing: The stub does not erect any frames explicitly and calls back into managed code. + Early-bound and late-bound: The stub does not erect any frames explicitly but contains an unmanaged CALLI which turns it into the JIT inlined case. ComToClr: @@ -1230,46 +1223,6 @@ class FramedMethodFrame : public TransitionFrame return TYPE_CALL; } -#ifdef COM_STUBS_SEPARATE_FP_LOCATIONS - static int GetFPArgOffset(int iArg) - { -#ifdef TARGET_AMD64 - // Floating point spill area is between return value and transition block for frames that need it - // (see code:CLRToCOMMethodFrame) - return -(4 * 0x10 /* floating point args */ + 0x8 /* alignment pad */ + TransitionBlock::GetNegSpaceSize()) + (iArg * 0x10); -#endif - } -#endif - - // - // GetReturnObjectPtr and GetReturnValuePtr are only valid on frames - // that allocate - // - PTR_PTR_Object GetReturnObjectPtr() - { - LIMITED_METHOD_DAC_CONTRACT; - return PTR_PTR_Object(GetReturnValuePtr()); - } - - // Get return value address - PTR_VOID GetReturnValuePtr() - { - LIMITED_METHOD_DAC_CONTRACT; -#ifdef COM_STUBS_SEPARATE_FP_LOCATIONS - TADDR p = GetTransitionBlock() + GetFPArgOffset(0); -#else - TADDR p = GetTransitionBlock() - TransitionBlock::GetNegSpaceSize(); -#endif - // Return value is right before the transition block (or floating point spill area on AMD64) for frames that need it - // (see code:CLRToCOMMethodFrame) -#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE - p -= ENREGISTERED_RETURNTYPE_MAXSIZE; -#else - p -= sizeof(ARG_SLOT); -#endif - return dac_cast(p); - } - friend struct cdac_data; }; @@ -1417,40 +1370,6 @@ class ComMethodFrame : public UnmanagedToManagedFrame }; typedef DPTR(class ComMethodFrame) PTR_ComMethodFrame; - -//------------------------------------------------------------------------ -// This represents a generic call from CLR to COM -//------------------------------------------------------------------------ - -typedef DPTR(class CLRToCOMMethodFrame) PTR_CLRToCOMMethodFrame; - -class CLRToCOMMethodFrame : public FramedMethodFrame -{ -public: - CLRToCOMMethodFrame(TransitionBlock * pTransitionBlock, MethodDesc * pMethodDesc); - - void GcScanRoots_Impl(promote_func *fn, ScanContext* sc); - - BOOL IsTransitionToNativeFrame_Impl() - { - LIMITED_METHOD_CONTRACT; - return TRUE; - } - - int GetFrameType_Impl() - { - LIMITED_METHOD_DAC_CONTRACT; - return TYPE_EXIT; - } - - void GetUnmanagedCallSite_Impl(TADDR* ip, - TADDR* returnIP, - TADDR* returnSP); - - BOOL TraceFrame_Impl(Thread *thread, BOOL fromPatch, - TraceDestination *trace, REGDISPLAY *regs); -}; - #endif // FEATURE_COMINTEROP //------------------------------------------------------------------------ diff --git a/src/coreclr/vm/i386/asmhelpers.asm b/src/coreclr/vm/i386/asmhelpers.asm index 71e6edfeb3a443..f4082daf2afcdf 100644 --- a/src/coreclr/vm/i386/asmhelpers.asm +++ b/src/coreclr/vm/i386/asmhelpers.asm @@ -994,50 +994,6 @@ _TheUMEntryPrestub@0 proc public jmp eax ; Tail Jmp _TheUMEntryPrestub@0 endp -ifdef FEATURE_COMINTEROP -;========================================================================== -; CLR -> COM generic or late-bound call -_GenericCLRToCOMCallStub@0 proc public - - STUB_PROLOG - - ; pTransitionBlock - mov esi, esp - - ; return value - sub esp, 8 - - ; save pMD - mov ebx, eax - - push eax ; pMD - push esi ; pTransitionBlock - call _CLRToCOMWorker@8 - - push eax - call _setFPReturn@12 ; pop & set the return value - - ; From here on, mustn't trash eax:edx - - ; Get pCLRToCOMCallInfo for return thunk - mov ecx, [ebx + CLRToCOMCallMethodDesc__m_pCLRToCOMCallInfo] - ; Get size of arguments to pop - movzx ecx, word ptr [ecx + CLRToCOMCallInfo__m_cbStackPop] - ; Get the return address, pushed registers on stack are 24 bytes big - mov ebx, [esp + 24] - ; Set the return address on stack at the last stack slot - mov [esp + ecx + 24], ebx - - STUB_EPILOG_RETURN - - ; Move esp to point to the last stack slot where we put the return - ; address earlier - lea esp, [esp + ecx] - - ret - -_GenericCLRToCOMCallStub@0 endp - _GenericComCallStub@0 proc public ; Pop ComCallMethodDesc* pushed by prestub diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index a9758414d1c684..1d9ccb7a191e04 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -2211,7 +2211,7 @@ PCODE MethodDesc::DoPrestub(MethodTable *pDispatchingMT, CallerGCMode callerGCMo #ifdef FEATURE_COMINTEROP /************************** INTEROP *************************/ /*----------------------------------------------------------------- - // Some method descriptors are COMPLUS-to-COM call descriptors + // Some method descriptors are CLR-to-COM call descriptors // they are not your every day method descriptors, for example // they don't have an IL or code. */ diff --git a/src/coreclr/vm/stubmgr.cpp b/src/coreclr/vm/stubmgr.cpp index c35426881acc83..bb35d9c07b47cc 100644 --- a/src/coreclr/vm/stubmgr.cpp +++ b/src/coreclr/vm/stubmgr.cpp @@ -10,6 +10,7 @@ #include "asmconstants.h" #ifdef FEATURE_COMINTEROP #include "olecontexthelpers.h" +#include "clrtocomcall.h" #endif #ifdef LOGGING @@ -1723,6 +1724,31 @@ static PCODE GetCOMTarget(Object *pThis, CLRToCOMCallInfo *pCLRToCOMCallInfo) PCODE target = (PCODE)lpVtbl[pCLRToCOMCallInfo->m_cachedComSlot]; return target; } + +static PCODE GetLateBoundCOMTarget(Object *pThis, CLRToCOMCallInfo *pCLRToCOMCallInfo) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + // calculate the target interface pointer + SafeComHolder pUnk; + + OBJECTREF oref = ObjectToOBJECTREF(pThis); + GCPROTECT_BEGIN(oref); + pUnk = ComObject::GetComIPFromRCWThrowing(&oref, pCLRToCOMCallInfo->m_pInterfaceMT); + GCPROTECT_END(); + + LPVOID *lpVtbl = *(LPVOID **)(IUnknown *)pUnk; + + + PCODE target = (PCODE)lpVtbl[6]; // IDispatch::Invoke's slot + return target; +} #endif // FEATURE_COMINTEROP BOOL ILStubManager::TraceManager(Thread *thread, @@ -1825,12 +1851,14 @@ BOOL ILStubManager::TraceManager(Thread *thread, { LOG((LF_CORDB, LL_INFO1000, "ILSM::TraceManager: Stub is CLR-to-COM\n")); _ASSERTE(pMD->IsCLRToCOMCall()); - CLRToCOMCallMethodDesc *pCMD = (CLRToCOMCallMethodDesc *)pMD; - _ASSERTE(!pCMD->IsStatic() && !pCMD->IsCtor() && "Static methods and constructors are not supported for built-in classic COM"); + _ASSERTE(!pMD->IsStatic() && !pMD->IsCtor() && "Static methods and constructors are not supported for built-in classic COM"); + + DWORD dwStubFlags; + CLRToCOMCallInfo* pInfo = CLRToCOMCall::PopulateCLRToCOMCallMethodDesc(pMD, &dwStubFlags); if (pThis != NULL) { - target = GetCOMTarget(pThis, pCMD->m_pCLRToCOMCallInfo); + target = SF_IsCOMLateBoundStub(dwStubFlags) ? GetLateBoundCOMTarget(pThis, pInfo) : GetCOMTarget(pThis, pInfo); LOG((LF_CORDB, LL_INFO10000, "ILSM::TraceManager: CLR-to-COM case %p\n", target)); trace->InitForUnmanaged(target); } @@ -1930,7 +1958,7 @@ BOOL PInvokeStubManager::DoTraceStub(PCODE stubStartAddress, #endif // !DACCESS_COMPILE } -// This is used to recognize GenericCLRToCOMCallStub, VarargPInvokeStub, and GenericPInvokeCalliHelper. +// This is used to recognize VarargPInvokeStub, and GenericPInvokeCalliHelper. #ifndef DACCESS_COMPILE @@ -1950,8 +1978,6 @@ void InteropDispatchStubManager::Init() #endif // #ifndef DACCESS_COMPILE -PCODE TheGenericCLRToCOMCallStub(); // clrtocomcall.cpp - #ifndef DACCESS_COMPILE static BOOL IsVarargPInvokeStub(PCODE stubStartAddress) { @@ -1975,13 +2001,6 @@ BOOL InteropDispatchStubManager::CheckIsStub_Internal(PCODE stubStartAddress) //@dbgtodo dharvey implement DAC support #ifndef DACCESS_COMPILE -#ifdef FEATURE_COMINTEROP - if (stubStartAddress == GetEEFuncEntryPoint(GenericCLRToCOMCallStub)) - { - return true; - } -#endif // FEATURE_COMINTEROP - if (IsVarargPInvokeStub(stubStartAddress)) { return true; @@ -2065,41 +2084,6 @@ BOOL InteropDispatchStubManager::TraceManager(Thread *thread, trace->InitForUnmanaged(target); #endif //defined(TARGET_ARM64) && defined(__APPLE__) } -#ifdef FEATURE_COMINTEROP - else - { - CLRToCOMCallMethodDesc *pCMD = (CLRToCOMCallMethodDesc *)arg; - _ASSERTE(pCMD->IsCLRToCOMCall()); - - Object * pThis = StubManagerHelpers::GetThisPtr(pContext); - - { - if (!pCMD->m_pCLRToCOMCallInfo->m_pInterfaceMT->IsComEventItfType() && (pCMD->m_pCLRToCOMCallInfo->m_pILStub != NULL)) - { - // Early-bound CLR->COM call - continue in the IL stub - trace->InitForStub(pCMD->m_pCLRToCOMCallInfo->m_pILStub); - } - else - { - // Late-bound CLR->COM call - continue in target's IDispatch::Invoke - OBJECTREF oref = ObjectToOBJECTREF(pThis); - GCPROTECT_BEGIN(oref); - - MethodTable *pItfMT = pCMD->m_pCLRToCOMCallInfo->m_pInterfaceMT; - _ASSERTE(pItfMT->GetComInterfaceType() == ifDispatch); - - SafeComHolder pUnk = ComObject::GetComIPFromRCWThrowing(&oref, pItfMT); - LPVOID *lpVtbl = *(LPVOID **)(IUnknown *)pUnk; - - PCODE target = (PCODE)lpVtbl[6]; // DISPATCH_INVOKE_SLOT; - LOG((LF_CORDB, LL_INFO10000, "IDSM::TraceManager: CLR-to-COM late-bound case %p\n", target)); - trace->InitForUnmanaged(target); - - GCPROTECT_END(); - } - } - } -#endif // FEATURE_COMINTEROP return TRUE; } diff --git a/src/coreclr/vm/stubmgr.h b/src/coreclr/vm/stubmgr.h index a19724b65f4a2c..e84e6935e332ab 100644 --- a/src/coreclr/vm/stubmgr.h +++ b/src/coreclr/vm/stubmgr.h @@ -692,7 +692,6 @@ class PInvokeStubManager : public StubManager }; // This is used to recognize -// GenericCLRToCOMCallStub() // VarargPInvokeStub() // GenericPInvokeCalliHelper() typedef VPTR(class InteropDispatchStubManager) PTR_InteropDispatchStubManager; diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs index 41134a2fd4c1d3..743d0cc58e94c7 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/FrameHandling/FrameIterator.cs @@ -16,7 +16,6 @@ internal enum FrameType /* TransitionFrame Types */ FramedMethodFrame, - CLRToCOMMethodFrame, PInvokeCalliFrame, PrestubMethodFrame, StubDispatchFrame, @@ -93,7 +92,6 @@ public void UpdateContextFromFrame(IPlatformAgnosticContext context) // TransitionFrame type frames case FrameType.FramedMethodFrame: - case FrameType.CLRToCOMMethodFrame: case FrameType.PInvokeCalliFrame: case FrameType.PrestubMethodFrame: case FrameType.StubDispatchFrame: @@ -206,7 +204,6 @@ public static TargetPointer GetMethodDescPtr(Target target, TargetPointer frameP case FrameType.ExternalMethodFrame: case FrameType.PrestubMethodFrame: case FrameType.CallCountingHelperFrame: - case FrameType.CLRToCOMMethodFrame: case FrameType.InterpreterFrame: Data.FramedMethodFrame framedMethodFrame = target.ProcessedData.GetOrAdd(frame.Address); return framedMethodFrame.MethodDescPtr;