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/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/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/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 3a2373b8e8735b..ec8f6e042c92c3 100644 --- a/src/coreclr/vm/clrtocomcall.cpp +++ b/src/coreclr/vm/clrtocomcall.cpp @@ -23,31 +23,15 @@ #include "reflectioninvocation.h" #include "sigbuilder.h" #include "callconvbuilder.hpp" -#include "callsiteinspect.h" - -#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); -} +#include "method.hpp" CLRToCOMCallInfo *CLRToCOMCall::PopulateCLRToCOMCallMethodDesc(MethodDesc* pMD, DWORD* pdwStubFlags) { CONTRACTL { - STANDARD_VM_CHECK; + THROWS; + GC_NOTRIGGER; + MODE_ANY; PRECONDITION(CheckPointer(pMD)); PRECONDITION(CheckPointer(pdwStubFlags, NULL_OK)); } @@ -141,791 +125,113 @@ CLRToCOMCallInfo *CLRToCOMCall::PopulateCLRToCOMCallMethodDesc(MethodDesc* pMD, return pComInfo; } -MethodDesc* CLRToCOMCall::GetILStubMethodDesc(MethodDesc* pMD, DWORD dwStubFlags) -{ - STANDARD_VM_CONTRACT; - - if (SF_IsCOMLateBoundStub(dwStubFlags) || SF_IsCOMEventCallStub(dwStubFlags)) - return NULL; - - // Get the call signature information - StubSigDesc sigDesc(pMD); - - return PInvoke::CreateCLRToNativeILStub( - &sigDesc, - (CorNativeLinkType)0, - (CorNativeLinkFlags)0, - CallConv::GetDefaultUnmanagedCallingConvention(), - dwStubFlags); -} - -PCODE CLRToCOMCall::GetStubForILStub(MethodDesc* pMD, MethodDesc** ppStubMD) -{ - STANDARD_VM_CONTRACT; - - _ASSERTE(pMD->IsCLRToCOMCall()); - _ASSERTE(*ppStubMD == NULL); - - 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; -} - -UINT32 CLRToCOMEventCallWorker(CLRToCOMMethodFrame* pFrame, CLRToCOMCallMethodDesc *pMD) +namespace { - CONTRACTL + MethodDesc* CreateEventCallStub(MethodDesc* pMD) { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pFrame)); - PRECONDITION(CheckPointer(pMD)); - } - CONTRACTL_END; + STANDARD_VM_CONTRACT; - struct { - OBJECTREF EventProviderTypeObj; - OBJECTREF EventProviderObj; - OBJECTREF ThisObj; - } gc; - gc.EventProviderTypeObj = NULL; - gc.EventProviderObj = NULL; - gc.ThisObj = NULL; + _ASSERTE(pMD->IsCLRToCOMCall()); - LOG((LF_STUBS, LL_INFO1000, "Calling CLRToCOMEventCallWorker %s::%s \n", pMD->m_pszDebugClassName, pMD->m_pszDebugMethodName)); + CLRToCOMCallInfo* pComInfo = CLRToCOMCallInfo::FromMethodDesc(pMD); - // Retrieve the method table and the method desc of the call. - MethodDesc *pEvProvMD = pMD->GetEventProviderMD(); - MethodTable *pEvProvMT = pEvProvMD->GetMethodTable(); + _ASSERTE(pComInfo->m_pEventProviderMD != NULL); - GCPROTECT_BEGIN(gc) - { - // Retrieve the exposed type object for event provider. - gc.EventProviderTypeObj = pEvProvMT->GetManagedClassObject(); - gc.ThisObj = pFrame->GetThis(); + MethodDesc *pEvProvMD = pComInfo->m_pEventProviderMD; + MethodTable *pEvProvMT = pEvProvMD->GetMethodTable(); - UnmanagedCallersOnlyCaller getEventProvider(METHOD__COM_OBJECT__GET_EVENT_PROVIDER); + FunctionSigBuilder sigBuilder; + sigBuilder.SetCallingConv((CorCallingConvention)IMAGE_CEE_CS_CALLCONV_DEFAULT_HASTHIS); - // Retrieve the event provider for the event interface type. - getEventProvider.InvokeThrowing(&gc.ThisObj, &gc.EventProviderTypeObj, &gc.EventProviderObj); + LocalDesc obj(ELEMENT_TYPE_OBJECT); + sigBuilder.NewArg(&obj); - // Set up an arg iterator to retrieve the arguments from the frame. - MetaSig mSig(pMD); - ArgIterator ArgItr(&mSig); + MetaSig sig(pEvProvMD); + LocalDesc retType(sig.GetRetTypeHandleThrowing()); + sigBuilder.SetReturnType(&retType); - // Make the call on the event provider method desc. - MethodDescCallSite eventProvider(pEvProvMD, &gc.EventProviderObj); + DWORD cbMetaSigSize = sigBuilder.GetSigSize(); + AllocMemHolder szMetaSig(pMD->GetMethodTable()->GetLoaderAllocator()->GetHighFrequencyHeap()->AllocMem(S_SIZE_T(cbMetaSigSize))); + sigBuilder.GetSig(szMetaSig, cbMetaSigSize); - // Retrieve the event handler passed in. - OBJECTREF EventHandlerObj = ObjectToOBJECTREF(*(Object**)(pFrame->GetTransitionBlock() + ArgItr.GetNextOffset())); + Signature signature(szMetaSig, cbMetaSigSize); + SigTypeContext typeContext; - 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 - { - THROWS; - GC_TRIGGERS; - MODE_COOPERATIVE; - PRECONDITION(CheckPointer(pFrame)); - } - CONTRACTL_END; + ILStubLinker stubLinker(pMD->GetModule(), signature, &typeContext, pEvProvMD, ILStubLinkerFlags::ILSTUB_LINKER_FLAG_STUB_HAS_THIS); - MethodDesc *pMD = pFrame->GetFunction(); - _ASSERTE(!pMD->ContainsGenericVariables() && pMD->IsRuntimeMethodHandle()); + ILCodeStream* pCode = stubLinker.NewCodeStream(ILStubLinker::kDispatch); - const BOOL fIsDelegate = pMD->GetMethodTable()->IsDelegate(); - _ASSERTE(!fIsDelegate && pMD->IsRuntimeMethodHandle()); + 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 *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()); + MethodDesc* pStubMD = ILStubCache::CreateAndLinkNewILStubMethodDesc( + pMD->GetLoaderAllocator(), + pMD->GetMethodTable(), + PINVOKESTUB_FL_COMEVENTCALL, + pMD->GetModule(), + szMetaSig, + cbMetaSigSize, + &typeContext, + &stubLinker + ); - if (strcmp(pMD->GetName(), "BeginInvoke") == 0) + #if defined(FEATURE_DYNAMIC_METHOD_HAS_NATIVE_STACK_ARG_SIZE) + if (pStubMD->IsDynamicMethod()) { - callsiteFlags |= CallsiteDetails::BeginInvoke; + 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. } - 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; + #endif - // Make sure this is only called on IDispatch only interfaces. - _ASSERTE(pItfMT->GetComInterfaceType() == ifDispatch); + szMetaSig.SuppressRelease(); - // 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); + return pStubMD; } - // 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) + MethodDesc* GetILStubMethodDesc(MethodDesc* pMD, DWORD dwStubFlags) { - // Non-property method - strMemberName = pItfMD->GetName(); - tkMember = pItfMD->GetMemberDef(); - binderFlags |= BINDER_InvokeMethod; - } - else - { - // Property accessor - tkMember = propToken; + STANDARD_VM_CONTRACT; - // Determine which type of accessor we are dealing with. - switch (uSemantic) + // 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)) { - case msGetter: - { - // INVOKE_PROPERTYGET - binderFlags |= BINDER_GetProperty; - break; + _ASSERTE(pMD->IsCLRToCOMCall()); // no generic COM eventing + ((CLRToCOMCallMethodDesc *)pMD)->InitComEventCallInfo(); + return CreateEventCallStub(pMD); } - 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; - } + // Get the call signature information + StubSigDesc sigDesc(pMD); - default: - { - _ASSERTE(!"Invalid method semantic!"); - } - } + return PInvoke::CreateCLRToNativeILStub( + &sigDesc, + (CorNativeLinkType)0, + (CorNativeLinkFlags)0, + CallConv::GetDefaultUnmanagedCallingConvention(), + dwStubFlags); } - - // 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) +PCODE CLRToCOMCall::GetStubForILStub(MethodDesc* pMD, MethodDesc** ppStubMD) { - 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 + STANDARD_VM_CONTRACT; _ASSERTE(pMD->IsCLRToCOMCall()); + _ASSERTE(*ppStubMD == NULL); - // Make sure we have been properly loaded here - CONSISTENCY_CHECK(GetAppDomain()->CheckCanExecuteManagedCode(pMD)); - - // 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) - { - // 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. - // + DWORD dwStubFlags; + CLRToCOMCallInfo* pComInfo = CLRToCOMCall::PopulateCLRToCOMCallMethodDesc(pMD, &dwStubFlags); - 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/corelib.h b/src/coreclr/vm/corelib.h index 51fd4dee8af33c..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) @@ -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/dllimport.cpp b/src/coreclr/vm/dllimport.cpp index 68aec52fd682af..dbc1fd5b892d38 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" @@ -237,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, @@ -288,7 +270,7 @@ class ILStubState : public StubState } public: - void SetLastError(BOOL fSetLastError) + virtual void SetLastError(BOOL fSetLastError) { LIMITED_METHOD_CONTRACT; @@ -308,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 { @@ -331,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 { @@ -343,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 { @@ -369,7 +351,7 @@ class ILStubState : public StubState } #endif // FEATURE_COMINTEROP - void MarshalLCID(int argIdx) + virtual void MarshalLCID(int argIdx) { STANDARD_VM_CONTRACT; @@ -528,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; @@ -645,7 +627,7 @@ class ILStubState : public StubState } #ifndef DACCESS_COMPILE - void FinishEmit(MethodDesc* pStubMD) + virtual void FinishEmit(MethodDesc* pStubMD) { STANDARD_VM_CONTRACT; @@ -782,7 +764,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 +1603,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) @@ -2414,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. @@ -3503,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, @@ -3625,6 +3871,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; } @@ -3634,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, @@ -5284,6 +5536,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); @@ -5833,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/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/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/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/ilstubresolver.cpp b/src/coreclr/vm/ilstubresolver.cpp index 0ab3b1476bccbb..5cf08ead32155d 100644 --- a/src/coreclr/vm/ilstubresolver.cpp +++ b/src/coreclr/vm/ilstubresolver.cpp @@ -112,25 +112,50 @@ 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; +#ifdef DACCESS_COMPILE + DacNotImpl(); + return NULL; +#else // DACCESS_COMPILE + GCX_COOP(); + + STRINGREF* string = NULL; + STRINGREF strRef = GetStringLiteral(token); + + GCPROTECT_BEGIN(strRef); + + if (strRef != NULL) + { + string = GetDynamicMethod()->GetLoaderAllocator()->GetOrInternString(&strRef); + } + + GCPROTECT_END(); + + return string; +#endif // DACCESS_COMPILE } 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/method.hpp b/src/coreclr/vm/method.hpp index e9eb61abaf5348..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; } @@ -3468,14 +3471,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; 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/stubgen.cpp b/src/coreclr/vm/stubgen.cpp index b70cac45d9de85..4666eef7a2eb1b 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; @@ -1666,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) { @@ -1694,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; @@ -3258,6 +3278,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..10ae25c1ce0bc0 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,17 +1023,21 @@ 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 (); 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 (); 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;