Uh oh!
There was an error while loading. Please reload this page.
Remove unneeded DebuggerU2MCatchHandleFrames - #132082
Conversation
Now that we call into managed code from unmanaged with UCO methods with try-catch blocks around the body, the DebuggerU2MCatchHandleFrames that represent "there's a catch-all in manually managed runtime code" no longer provide benefit. This PR removes the unneeded frames. It also cleans up a bit of the usage in ComWrappers so we aren't rethrowing an exception just to immediately catch it and extract the HResult to a local (that we don't even return out anywhere).
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Tagging subscribers to this area: @agocke |
There was a problem hiding this comment.
Pull request overview
This PR removes several uses of DebuggerU2MCatchHandlerFrame (and related “catch-all” metadata) across CoreCLR unmanaged-to-managed transition points, and simplifies some COM/dispatch interop paths that previously used dedicated debugger/EH wrapper scaffolding.
Changes:
- Simplifies
DebuggerU2MCatchHandlerFrameby removing the “catches all exceptions” flag and updating call sites accordingly. - Removes
DebuggerU2MCatchHandlerFrameusage from JIT interface error-trap and COMIDispatchinvocation paths, including deleting theInvokeMemberDebuggerWrapperhelper. - Refactors
ComWrappersinterop invocation to avoid throw/rethrow patterns by using direct UCO invocation with an exception out-parameter.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/vm/threads.cpp | Updates DebuggerU2MCatchHandlerFrame construction to new signature while preserving push/pop lifetime. |
| src/coreclr/vm/jitinterface.cpp | Removes debugger catch-handler frame from CEEInfo::runWithErrorTrap. |
| src/coreclr/vm/interoplibinterface_comwrappers.cpp | Switches CallICustomQueryInterface invocation to InvokeDirect_Ret with an exception out-param. |
| src/coreclr/vm/frames.h | Removes catchesAllExceptions state from DebuggerU2MCatchHandlerFrame and updates constructors. |
| src/coreclr/vm/exceptionhandling.cpp | Simplifies the “unhandled by runtime” check involving topmost DebuggerU2MCatchHandlerFrame. |
| src/coreclr/vm/dispatchinfo.h | Removes the InvokeMemberDebuggerWrapper declaration. |
| src/coreclr/vm/dispatchinfo.cpp | Removes InvokeMemberDebuggerWrapper implementation and calls InvokeMemberWorker directly. |
Suppressed comments (1)
src/coreclr/vm/frames.h:1836
- The "Notes" section a few lines above still says this frame is only used in
DispatchInfo::InvokeMember, but that usage was removed and the frame is still used elsewhere (e.g.,ManagedThreadBase_DispatchOuterinthreads.cpp). Please update/remove that note so it remains accurate.
static constexpr size_t NumObjRefs = offsetof(GCFrame, m_numObjRefs);
static constexpr size_t GCFlags = offsetof(GCFrame, m_gcFlags);
};
//-----------------------------------------------------------------------------
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/coreclr/vm/interoplibinterface_comwrappers.cpp:346
gc.exceptionRefis GC-protected but never initialized beforeGCPROTECT_BEGIN(gc). Leaving anEXCEPTIONREFuninitialized can make the GC treat arbitrary stack data as an object reference, leading to memory corruption or crashes.
struct
{
OBJECTREF objRef;
EXCEPTIONREF exceptionRef;
} gc;
gc.objRef = NULL;
src/coreclr/vm/frames.h:2017
- The class comment’s note says this frame is only used by
DispatchInfo.InvokeMember, but this PR removes that usage and the remaining usage is inManagedThreadBase_DispatchOuter(threads.cpp). Please update the note to avoid misleading future readers.
DebuggerU2MCatchHandlerFrame() : Frame(FrameIdentifier::DebuggerU2MCatchHandlerFrame)
{
WRAPPER_NO_CONTRACT;
Frame::Push();
}
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/coreclr/vm/interoplibinterface_comwrappers.cpp:356
InvokeDirect_Retdoes not throw on managed exceptions; it reports them via the trailingException*parameter. In this path,ComWrappers.CoreCLR.CallICustomQueryInterfacesets*pExceptionand returnsdefault(0 /Handled). Assigningresultunconditionally can therefore reportHandledwith*obj == NULL, causingQueryInterfaceto returnS_OKincorrectly. Only updateresultwhenexceptionRefis null; otherwise preserveFailedToInvoke(or map toFailed) so the caller doesn't treat the interface as found.
UnmanagedCallersOnlyCaller callICustomQueryInterface(METHOD__COMWRAPPERS__CALL_ICUSTOMQUERYINTERFACE);
result = (TryInvokeICustomQueryInterfaceResult)callICustomQueryInterface.InvokeDirect_Ret<INT32>(&gc.objRef, &iid, obj, &gc.exceptionRef);
hr = gc.exceptionRef == NULL ? S_OK : gc.exceptionRef->GetHResult();
jkoritzinsky
commented
Aug 21, 2026
/ba-g TypeMapApp failure fixed in main before KBE issue was opened |
Uh oh!
There was an error while loading. Please reload this page.
Now that we call into managed code from unmanaged with UCO methods with try-catch blocks around the body, the DebuggerU2MCatchHandleFrames that represent "there's a catch-all in manually managed runtime code" no longer provide benefit.
This PR removes the unneeded frames. It also cleans up a bit of the usage in ComWrappers so we aren't rethrowing an exception just to immediately catch it and extract the HResult to a local (that we don't even return out anywhere).